class ExplosionLight : PointLightBase { void ExplosionLight() { SetVisibleDuringDaylight(true); SetRadiusTo(60); SetBrightnessTo(0.05); SetFlareVisible(false); SetAmbientColor(1.0, 1.0, 1.0); SetDiffuseColor(1.0, 1.0, 1.0); SetLifetime(2.1); //SetDisableShadowsWithinRadius(-1); SetFadeOutTime(1); m_FadeInTime = 0.25; SetFlickerSpeed(7); //SetFlickerAmplitude(0.5); SetFlickerAmplitudeMax(3); SetFlickerAmplitudeMin(0); SetCastShadow( false ); } } //----------------------------------------------------------------------------------------------- class FireworksLauncherClientEventBase { void OnFired(); } //----------------------------------------------------------------------------------------------- class FireworksLauncherClientEvent : FireworksLauncherClientEventBase { protected ref Timer m_Timer = new Timer(); protected FireworksLauncher m_Item; protected int m_Index; protected vector m_ExplosionPos; protected EffectSound m_FireSound; protected EffectSound m_ExplosionSound; protected ParticleSource m_ParticleShot; protected ParticleSource m_ParticleExplosion; protected vector m_ShotDir; protected ExplosionLight m_ExplosionLight; protected ParticleSource m_ParticleAfterBurnEnd; protected string m_Color; protected int m_RemainingExplosions = GetSecondaryExplosionCount(); protected ref array m_Events = new ref array; #ifdef DEVELOPER Shape m_ShotTrajectory; #endif // ----------------------------- // ------- Tweaking Start------- // ----------------------------- protected int GetSoundDelay() { return 0; } protected int GetLightDelay() { return 0; } protected float GetExplosionDistance() { return Math.RandomFloatInclusive(35,40); } protected float GetShotDispersionAngle() { return Math.RandomFloatInclusive(-25,25);//this is rotated 360 degrees around the UP vector too } protected string GetExplosionSoundSet() { return "FireworksLauncher_Explosion_SoundSet"; } protected float GetExplosionDelay() { return Math.RandomFloatInclusive(1.5,1.75); } // ----------------------------- // ------- Tweaking End -------- // ----------------------------- void FireworksLauncherClientEvent(FireworksLauncher item, int index) { m_Item = item; m_Index = index; int colorSequenceLastIndex = m_Item.GetColorSequence().Length() - 1; int coloreSequenceIndex = m_Index - 1;//shot index(m_Index) starts at 1 if (colorSequenceLastIndex >= coloreSequenceIndex) { m_Color = m_Item.GetColorSequence().Get(coloreSequenceIndex); } else { Debug.LogError("Failed to obtain color from color sequence"); m_Color = "B"; } } void ~FireworksLauncherClientEvent() { SEffectManager.DestroyEffect(m_FireSound); SEffectManager.DestroyEffect(m_ExplosionSound); if (m_ParticleAfterBurnEnd) { m_ParticleAfterBurnEnd.StopParticle(); } } override protected void OnFired() { m_Timer.Run(GetExplosionDelay(), this, "OnExplode", null); m_Item.PlaySoundSet( m_FireSound, "FireworksLauncher_Shot_SoundSet", 0, 0 ); m_ParticleShot = ParticleManager.GetInstance().PlayInWorld(ParticleList.FIREWORKS_SHOT, GetShotPos()); m_ParticleShot.SetYawPitchRoll(Vector(Math.RandomFloatInclusive(0,360),GetShotDispersionAngle(),0)); m_ParticleAfterBurnEnd = ParticleManager.GetInstance().PlayInWorld(ParticleList.FIREWORKS_AFTERBURN_END, GetShotPos()); if (m_ParticleAfterBurnEnd) m_ParticleAfterBurnEnd.SetOwner(this); m_ShotDir = m_ParticleShot.GetDirectionUp(); #ifdef DEVELOPER vector pts[2]; pts[0] = GetShotPos(); pts[1] = GetShotPos() + m_ShotDir * GetExplosionDistance(); //m_ShotTrajectory = Shape.CreateLines(COLOR_RED, ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE|ShapeFlags.NOZBUFFER, pts, 2); #endif } protected vector GetShotPos() { vector memPos; string memName = "Shot_" + m_Index; if (m_Item.MemoryPointExists(memName)) { memPos = m_Item.GetMemoryPointPos(memName); memPos = m_Item.ModelToWorld(memPos); } else { memPos = m_Item.GetPosition();//fallback to item's location Debug.LogError("Missing shot memory point on Fireworks"); } return memPos; } protected void CalculateExplosionPosition() { m_ExplosionPos = GetShotPos() + m_ShotDir * GetExplosionDistance(); } protected vector GetExplosionPosition() { if (!m_ExplosionPos) { CalculateExplosionPosition(); } return m_ExplosionPos; } int GetSecondaryExplosionCount() { return 4; } protected void OnExplode() { #ifdef DEVELOPER if (m_ShotTrajectory) { m_ShotTrajectory.Destroy(); m_ShotTrajectory = null; } #endif m_ParticleAfterBurnEnd.Stop(); GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( PlayExplosionSound, GetSoundDelay(), false); GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( SpawnLight, GetLightDelay(), false); m_ParticleExplosion = ParticleManager.GetInstance().PlayInWorld(GetExplParticleFromSequence(), GetExplosionPosition()); RequestSecondaryExplosion(); } protected void RequestSecondaryExplosion() { if (m_RemainingExplosions> 0) { m_RemainingExplosions--; GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( SpawnSecondaryExplosion, GetSecondaryExplosionDelay(), false); } } protected void SpawnSecondaryExplosion() { FireworksLauncherClientEventSecondary evnt = new FireworksLauncherClientEventSecondary(m_Item,m_Index); evnt.Init(GetExplosionPosition()); evnt.OnExplode(); m_Events.Insert(evnt); RequestSecondaryExplosion(); } protected int GetSecondaryExplosionDelay() { return Math.RandomIntInclusive(100,250); } protected int GetExplParticleFromSequence() { switch (m_Color) { case "R": return ParticleList.FIREWORKS_EXPLOSION_RED; case "G": return ParticleList.FIREWORKS_EXPLOSION_GREEN; case "B": return ParticleList.FIREWORKS_EXPLOSION_BLUE; case "Y": return ParticleList.FIREWORKS_EXPLOSION_YELLOW; case "P": return ParticleList.FIREWORKS_EXPLOSION_PINK; default: ErrorEx("Incorrect explosion particle color in the sequence"); } return ParticleList.FIREWORKS_EXPLOSION_RED; } protected void SetupLight(PointLightBase light) { switch (m_Color) { case "R": light.SetDiffuseColor(255,51,51); light.SetAmbientColor(255,51,51); break; case "G": light.SetDiffuseColor(0,255,128); light.SetAmbientColor(0,255,128); break; case "B": light.SetDiffuseColor(51,153,255); light.SetAmbientColor(51,153,255); break; case "Y": light.SetDiffuseColor(255,255,51); light.SetAmbientColor(255,255,51); break; case "P": light.SetDiffuseColor(255,102,255); light.SetAmbientColor(255,102,255); break; default: ErrorEx("Incorrect explosion particle color in the sequence"); } } protected void SpawnLight() { m_ExplosionLight = ExplosionLight.Cast( ScriptedLightBase.CreateLight( ExplosionLight, GetExplosionPosition()) ); SetupLight(m_ExplosionLight); } protected void PlayExplosionSound() { if (m_FireSound) { m_FireSound.Stop(); } m_Item.PlaySoundSet( m_ExplosionSound, GetExplosionSoundSet(), 0, 0 ); } } //------------------------------------------------------------------------------------ class FireworksLauncherClientEventSecondary : FireworksLauncherClientEvent { protected vector m_ShotPos; override protected vector GetExplosionPosition() { return GetShotPos() + m_ShotDir * GetExplosionDistance(); } void Init(vector pos) { m_ShotPos = pos; m_ShotDir[0] = Math.RandomFloatInclusive(-1,1); m_ShotDir[1] = Math.RandomFloatInclusive(-1,1); m_ShotDir[2] = Math.RandomFloatInclusive(-1,1); m_ShotDir.Normalize(); } override protected vector GetShotPos() { return m_ShotPos; } override protected float GetExplosionDistance() { return Math.RandomFloatInclusive(10,15); } override void OnExplode() { GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( PlayExplosionSound, GetSoundDelay(), false); GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( SpawnLight, GetLightDelay(), false); m_ParticleExplosion = ParticleManager.GetInstance().PlayInWorld(GetExplParticleFromSequence(), GetExplosionPosition()); } } class FireworksLauncher: FireworksBase { int m_Index;//counts the shots int m_IndexPrev;//counts the shots int m_ColorSequenceIndex; ref Timer m_TimerFuse; ref array m_Events; EffectSound m_FuseSoundStart; EffectSound m_FuseSound; ParticleSource m_ParticleFuse; ParticleSource m_ParticleAfterBurnEnd; ref array m_ColorSequence; void FireworksLauncher() { m_ColorSequence = new array(); SetupColorSequences(); RegisterNetSyncVariableInt("m_State", 0, EnumTools.GetLastEnumValue(EFireworksState)); RegisterNetSyncVariableInt("m_Index", 0, GetMaxShots()); RegisterNetSyncVariableInt("m_RandomSeed", 0, 1023); int lastIndex = m_ColorSequence.Count() - 1; RegisterNetSyncVariableInt("m_ColorSequenceIndex", 0, lastIndex); m_RandomSeed = Math.RandomInt(0,1023); m_ColorSequenceIndex = Math.RandomIntInclusive(0, lastIndex); } void ~FireworksLauncher() { SEffectManager.DestroyEffect(m_FuseSound); SEffectManager.DestroyEffect(m_FuseSoundStart); } override bool IsDeployable() { return true; } //!how long it takes to deploy this item in seconds override float GetDeployTime() { return 2; } protected void SetupColorSequences() { m_ColorSequence.Insert("RGBYPBRGBRGBYPBRGBRGBYPBRGBPBRGBRGBY"); m_ColorSequence.Insert("PGPYPBYPYPBYYPBRPYPBYYPBRGBPBRGRGBRB"); m_ColorSequence.Insert("YPBRPYPBYYPBRGBPBRGRGBRBGRPBRGBRYPBY"); m_ColorSequence.Insert("YRBGPRYPGRYBGRGRGBRBBYPYPBYRYPGRYGRP"); m_ColorSequence.Insert("BGRYPYRPBYYPYRBGPRYPGBYPBRGBPBRGBRGB"); m_ColorSequence.Insert("RYGRPBRGBYPBRRPBRGBBRBBYPYPRGBRGBRPY"); m_ColorSequence.Insert("GBRGBYRGBYPBRRPBRBYRYPGPYPRGBRGBRPYG"); m_ColorSequence.Insert("RYPBYYPBRGBYPBRGBRBGBPBRGRGBRBGRYPYR"); m_ColorSequence.Insert("PBRGBYPBRGBRBGBPBRGRGBRBGRYPYRRYPBYY"); m_ColorSequence.Insert("RGRGBRBBYPYPBYRYPGRYGRPYRBGPRYPGRYBG"); m_ColorSequence.Insert("RBYRYPGPYPRGBRGBRPYGGBRGBYRGBYPBRRPB"); m_ColorSequence.Insert("PRGBRGBRPYGGBRRBYRYPGPYGBYRGBYPBRRPB"); } string GetColorSequence() { if (m_ColorSequence.IsValidIndex(m_ColorSequenceIndex)) { return m_ColorSequence.Get(m_ColorSequenceIndex); } else { ErrorEx("Failed to obtain color sequence"); return "RYPBYYPBRGBYPBRGBRBGBPBRGRGBRBGRYPYR"; } } override protected void OnPlacementComplete(Man player, vector position = "0 0 0", vector orientation = "0 0 0") { super.OnPlacementComplete(player, position, orientation); if (GetGame().IsServer()) { if (GetState() == EFireworksState.DEFAULT) { SetState(EFireworksState.PLACED); } } } override protected float GetMaxAllowedWetness() { return GameConstants.STATE_WET; } override protected float GetEventDelay() { return Math.RandomFloatInclusive(3,3.35); } protected string GetFuseSoundSet() { return "FireworksLauncher_Ignition_Loop_SoundSet"; } protected int GetMaxShots() { return 16; } protected float GetFuseDelay() { return 3; } protected string GetAmmoType() { return "Fireworks_Ammo"; } protected int GetDamageType() { return DamageType.EXPLOSION; } override protected void OnStateChangedServer(EFireworksState currentState) { switch (currentState) { case EFireworksState.PLACED: break case EFireworksState.IGNITED: OnFuseIgnitedServer(); break case EFireworksState.FIRING: HideSelection("cover"); OnFiringStartServer(); break case EFireworksState.FINISHED: HideSelection("cover");//when loading from storage break default: {}; } } override protected void OnStateChangedClient(EFireworksState currentState) { switch (currentState) { case EFireworksState.IGNITED: OnFuseIgnitedClient(); break case EFireworksState.PLACED: break case EFireworksState.FIRING: OnFiringStartClient(); break default: {}; } } //! Executed on Server when some item ignited this one override void OnIgnitedThis( EntityAI fire_source) { super.OnIgnitedThis(fire_source); if (m_Events) { m_Events.Clear(); } m_Index = 0; if (m_TimerEvent) { m_TimerEvent.Stop(); } } override protected bool CanPutInCargo( EntityAI parent ) { return (GetState() == EFireworksState.DEFAULT || (GetState() == EFireworksState.PLACED) || (GetState() == EFireworksState.FINISHED); } override protected bool CanPutIntoHands( EntityAI parent ) { return (GetState() == EFireworksState.DEFAULT || (GetState() == EFireworksState.PLACED) || (GetState() == EFireworksState.FINISHED); } protected void OnFuseIgnitedServer() { int state = EFireworksState.FIRING; GetGame().GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( SetState, GetFuseDelay()*1000, false, state); } protected void OnFuseIgnitedClient() { m_ParticleFuse = ParticleManager.GetInstance().PlayInWorld(ParticleList.FIREWORKS_FUSE, GetPosition() + "0 0.15 0"); if (m_ParticleFuse) m_ParticleFuse.SetOwner(this); PlaySoundSet( m_FuseSoundStart, "FireworksLauncher_Ignition_Start_SoundSet", 0, 0 ); vector fuseStart; vector fuseEnd; if (MemoryPointExists("Fuse_Start")) { fuseStart = GetMemoryPointPos("Fuse_Start"); fuseStart = ModelToWorld(fuseStart); } if (MemoryPointExists("Fuse_End")) { fuseEnd = GetMemoryPointPos("Fuse_End"); fuseEnd = ModelToWorld(fuseEnd); } vector fuseDir = fuseEnd - fuseStart; vector fuseOrientation[4]; vector ori = fuseDir.VectorToAngles(); m_ParticleFuse.SetOrientation(ori); m_ParticleFuse.SetPosition(fuseStart); PlaySoundSetLoop( m_FuseSound, GetFuseSoundSet(), 0, 0 ); } protected void OnFiringStartServer() { OnEventServer(0); } protected void OnFiringStartClient() { if (m_ParticleFuse) { m_ParticleFuse.StopParticle(StopParticleFlags.IMMEDIATE); } if (m_FuseSound) { m_FuseSound.Stop(); } } protected void OnFiringStop() { SetHealth01("","",0); } //!Starts event timer protected void RestartEventTimer() { if (!m_TimerEvent) { m_TimerEvent = new Timer(); } m_TimerEvent.Run(GetEventDelay(), this, "OnEventServer", new Param1(0)); } override protected bool IsIgnited() { return GetState()==EFireworksState.IGNITED; } override protected bool CanIgniteItem(EntityAI ignite_target = NULL) { return false; } //!Called periodically but only after the entity gets ignited override protected void OnEventServer(int type) { m_Index++; DamageSystem.ExplosionDamage(this, NULL, GetAmmoType(), GetPosition(), GetDamageType()); SetSynchDirty(); if (m_Index > GetMaxShots()) { m_Index = GetMaxShots(); m_TimerEvent = null; SetState(EFireworksState.FINISHED); } else { RestartEventTimer(); } } protected void OnIndexChangedClient() { if (!m_Events) { m_Events = new array; } if (m_Index != 0 && m_State == EFireworksState.FIRING)//can only be true when restarting the device during debug calls { FireworksLauncherClientEventBase fireEvent = SpawnEvent(); m_Events.Insert(fireEvent); } } protected FireworksLauncherClientEventBase SpawnEvent() { FireworksLauncherClientEventBase evnt = new FireworksLauncherClientEvent(this,m_Index); evnt.OnFired(); return evnt; } override protected void OnVariablesSynchronized() { super.OnVariablesSynchronized(); //Print("index: " + m_Index); if (m_Index != m_IndexPrev) { Math.Randomize(m_RandomSeed+m_Index); OnIndexChangedClient(); m_IndexPrev = m_Index; } } override protected void OnStoreSave(ParamsWriteContext ctx) { super.OnStoreSave(ctx); ctx.Write(m_Index); ctx.Write(m_State); } override protected bool OnStoreLoad( ParamsReadContext ctx, int version ) { if (!super.OnStoreLoad(ctx, version)) { return false; } if (version >= 130) { if (!ctx.Read(m_Index)) { return false; } if (!ctx.Read(m_State)) { return false; } SetState(m_State); } return true; } override string GetDeploySoundset() { return "placeFireworks_SoundSet"; } override string GetLoopDeploySoundset() { return "fireworks_deploy_SoundSet"; } #ifdef DEVELOPER override protected string GetDebugText() { string debug_output; if( GetGame().IsDedicatedServer()) { debug_output+= EnumTools.EnumToString(EFireworksState, m_State) +"\n"; debug_output+= "m_Index:" + m_Index +"\n"; } else { } return debug_output; } #endif }