FireworksLauncher.c
class ExplosionLightclass FireworksLauncherClientEventBaseclass FireworksLauncherClientEventclass FireworksLauncherClientEventSecondaryclass FireworksLauncher
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<ref FireworksLauncherClientEventBase> m_Events = new array<ref FireworksLauncherClientEventBase>;
#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();
g_Game.GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( PlayExplosionSound, GetSoundDelay(), false);
g_Game.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--;
g_Game.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()
{
g_Game.GetCallQueue( CALL_CATEGORY_SYSTEM ).CallLater( PlayExplosionSound, GetSoundDelay(), false);
g_Game.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<ref FireworksLauncherClientEventBase> m_Events;
EffectSound m_FuseSoundStart;
EffectSound m_FuseSound;
ParticleSource m_ParticleFuse;
ParticleSource m_ParticleAfterBurnEnd;
ref array<string> m_ColorSequence;
void FireworksLauncher()
{
m_ColorSequence = new array<string>();
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 (g_Game.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;
g_Game.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<int>(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<ref FireworksLauncherClientEventBase>;
}
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( g_Game.IsDedicatedServer())
{
debug_output+= EnumTools.EnumToString(EFireworksState, m_State) +"\n";
debug_output+= "m_Index:" + m_Index +"\n";
}
else
{
}
return debug_output;
}
#endif
}