Class FlameAmmo : Ammo { Default { Tag "$T_FLAMEAMMO"; Inventory.Icon "I_Napalm"; Inventory.PickupMessage "$I_FLAMEAMMO"; Inventory.Amount 75; Inventory.MaxAmount 450; Ammo.BackpackAmount 0; Ammo.BackpackMaxAmount 900; Ammo.DropAmount 30; } States { Spawn: FLMA A -1; Stop; } } Class OnFire : Thinker { Actor victim, instigator; int amount, cnt; override void Tick() { if ( !victim ) { Destroy(); return; } if ( victim.waterlevel > 0 ) { victim.A_RemoveLight('OnFireLight'); amount -= victim.waterlevel**2; } else victim.A_AttachLight('OnFireLight',DynamicLight.RandomFlickerLight,Color(4,2,0)*clamp(amount,0,63),int(max(victim.radius,victim.height))+60+min(amount/4,40),int(max(victim.radius,victim.height))+60+min(amount/4,40)+8,DynamicLight.LF_ATTENUATE,(0,0,victim.height/2),0.1); if ( victim.Health <= 0 ) amount = min(amount,100); if ( !(level.maptime%3) ) amount -= ((victim.Health>0)?1:2); if ( amount < -10 ) { victim.A_RemoveLight('OnFireLight'); Destroy(); return; } if ( amount <= 0 ) return; if ( cnt > 0 ) cnt--; else { cnt = 10; if ( victim.bSHOOTABLE && (victim.Health > 0) ) victim.DamageMobj(instigator.FindInventory("UFlamethrower"),instigator,max(1,amount*(victim.bBOSS?0.05:0.15)),'Fire',DMG_THRUSTLESS); if ( !victim ) { Destroy(); return; } } if ( level.maptime%5 ) return; int numpt = clamp(int(Random[FlameT](2,4)*amount*0.02),1,4); double mult = max(victim.radius,victim.height)/30.; numpt = int(max(1,numpt*mult)); for ( int i=0; i0); } return false; } } Class UFlameLight : PaletteLight { Default { Tag "Ampd"; Args 0,0,0,40; ReactionTime 40; } override void Tick() { Super.Tick(); Args[0] /= 5; Args[1] /= 5; Args[2] /= 5; Args[3] += 4; if ( !target || (target.waterlevel > 0) ) { Destroy(); return; } SetOrigin(target.pos,true); } } Class UFlameTrailLight : PaletteLight { Default { Tag "Ampd"; Args 0,0,0,80; ReactionTime 20; } override void Tick() { Super.Tick(); Args[0] /= 9; Args[1] /= 9; Args[2] /= 9; if ( !target || (target.waterlevel > 0) ) { Destroy(); return; } SetOrigin(target.pos,true); } } Class UFlame : Actor { override void PostBeginPlay() { Super.PostBeginPlay(); let l = Spawn("UFlameLight",pos); l.target = self; Scale.x *= RandomPick[ExploS](-1,1); Scale.y *= RandomPick[ExploS](-1,1); } bool TraceCheck( Actor other ) { Vector3 to; double len; FLineTraceData d; to = level.Vec3Diff(pos,other.pos); len = to.length(); to /= len; if ( !LineTrace(atan2(to.y,to.x),len,asin(-to.z),TRF_THRUACTORS) ) return true; to = level.Vec3Diff(pos,other.Vec3Offset(0,0,other.height/2)); len = to.length(); to /= len; if ( !LineTrace(atan2(to.y,to.x),len,asin(-to.z),TRF_THRUACTORS) ) return true; to = level.Vec3Diff(pos,other.Vec3Offset(0,0,other.height)); len = to.length(); to /= len; if ( !LineTrace(atan2(to.y,to.x),len,asin(-to.z),TRF_THRUACTORS) ) return true; return false; } action void A_Flame() { if ( waterlevel > 0 ) vel *= 0.9; else { vel *= 0.98; vel.z += 0.2; } if ( waterlevel > 0 ) bINVISIBLE = true; if ( !Random[FlameT](0,int(30*(0.4-alpha))) && (!bINVISIBLE || (waterlevel > 0)) ) { let s = Spawn("UTSmoke",pos+vel); s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2)); s.vel += vel*0.2; s.alpha *= 0.4; s.scale *= 2.5; } if ( bAMBUSH && !sting_flametspread ) return; if ( Random[FlameT](0,int(12*(0.4-alpha))) ) return; double rad = bAMBUSH?10:60; if ( !bAMBUSH ) rad += 90*(0.4-alpha); let bt = BlockThingsIterator.Create(self,rad+200); while ( bt.Next() ) { let t = bt.Thing; if ( !t || !t.bSHOOTABLE || (t.Health <= 0) || (t == tracer) || (!bAMBUSH && (t == master) && (GetAge() < 6)) || (Distance3D(t) > rad+t.radius) || !invoker.TraceCheck(t) ) continue; int amt = max(1,int(alpha*15)); if ( bAMBUSH && OnFire.IsOnFire(t) ) amt /= 2; OnFire.Apply(t,master,amt); } } Default { RenderStyle "Add"; Speed 20; Radius 4; Height 4; Alpha 0.4; Scale 0.1; +NOBLOCKMAP; +NOGRAVITY; +NOFRICTION; +SLIDESONWALLS; +ACTIVATEPCROSS; +ACTIVATEIMPACT; +NOTELEPORT; +FORCEXYBILLBOARD; +DROPOFF; +NOBLOCKMONST; } States { Spawn: SEXP AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTT 1 Bright { A_Flame(); A_SetScale(scale.x*1.08); A_FadeOut(0.01); } Stop; } } Class UFlameTrail : UFlame { override void PostBeginPlay() { Actor.PostBeginPlay(); Scale.x *= RandomPick[ExploS](-1,1); Scale.y *= RandomPick[ExploS](-1,1); } Default { Speed 2; Alpha 0.3; Scale 0.6; +AMBUSH; } States { Spawn: SEXP AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTT 1 Bright { A_Flame(); A_SetScale(scale.x*0.98); A_FadeOut(0.01); } Stop; } } Class UNapalm : Actor { } Class UFlamethrower : UnrealWeapon { bool bCharging; double ChargeSize, Count; override Inventory CreateTossable( int amt ) { if ( Owner.player && (Owner.player.ReadyWeapon == self) ) Owner.A_PlaySound("flamet/down",CHAN_6,Dampener.Active(self)?.1:1.); return Super.CreateTossable(amt); } override void DoEffect() { Super.DoEffect(); if ( Owner.player.ReadyWeapon != self ) return; let psp = Owner.player.FindPSprite(-2); if ( psp ) psp.alpha = (Owner.waterlevel>2)?.0:1.; } action void A_FireFlame() { let weap = Weapon(invoker); if ( (weap.Ammo1.Amount <= 0) || !(player.cmd.buttons&BT_ATTACK) ) { player.SetPSPrite(PSP_WEAPON,invoker.FindState("Release")); return; } invoker.count += 10./TICRATE; while ( invoker.count > 1. ) { weap.DepleteAmmo(weap.bAltFire,true,1); invoker.count -= 1.; } invoker.FireEffect(); A_AlertMonsters(); UTMainHandler.DoFlash(self,Color(32,255,128,0),1); Vector3 x, y, z, x2, y2, z2; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2.5*y-2*z); Actor p = Spawn("UFlame",origin); double a = FRandom[FlameT](0,360), s = FRandom[FlameT](0,.05); [x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll); Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); p.angle = atan2(dir.y,dir.x); p.pitch = asin(-dir.z); p.vel = vel*0.5+(cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed*FRandom[FlameT](0.8,1.4); p.master = self; } action void A_BeginFlame() { let weap = Weapon(invoker); weap.DepleteAmmo(weap.bAltFire,true,1); invoker.count = 0; A_PlaySound("flamet/fire",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true); A_Overlay(-9999,"Dummy2"); } action void A_BeginCharge() { let weap = Weapon(invoker); weap.DepleteAmmo(weap.bAltFire,true,1); invoker.count = invoker.chargesize = 0; A_PlaySound("flamet/charge",CHAN_WEAPON,Dampener.Active(self)?.1:1.); A_Overlay(-9999,"Dummy3"); } action void A_ChargeUp() { let weap = Weapon(invoker); if ( (invoker.chargesize < 4.9) && (weap.Ammo1.Amount > 0) ) { invoker.count += 40./TICRATE; while ( invoker.count > 1. ) { invoker.chargesize += 0.05; weap.DepleteAmmo(weap.bAltFire,true,1); invoker.count -= 1.; } } double ox = FRandom[Flamet](-1,1)*invoker.chargesize*0.3; double oy = FRandom[Flamet](-1,1)*invoker.chargesize*0.3; A_WeaponOffset(ox,32+oy); A_OverlayOffset(-2,-ox,-oy); A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.); if ( (weap.Ammo1.Amount <= 0) || (invoker.chargesize >= 4.9) || !(player.cmd.buttons&BT_ALTATTACK) ) player.SetPSPrite(PSP_WEAPON,invoker.FindState("AltRelease")); } action void A_FireNapalm() { A_Overlay(-9999,"Null"); A_WeaponOffset(0,32); A_OverlayOffset(-2,0,0); invoker.bCharging = false; A_PlaySound("flamet/altfire",CHAN_WEAPON,Dampener.Active(self)?.1:1.); if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); } Default { Tag "$T_FLAMETHROWER"; Inventory.PickupMessage "$I_FLAMETHROWER"; Weapon.UpSound "flamet/select"; Weapon.SlotNumber 6; Weapon.SelectionOrder 4; Weapon.SlotPriority 0.9; Weapon.AmmoType "FlameAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "FlameAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 100; UTWeapon.DropAmmo 50; +NOEXTREMEDEATH; } States { Spawn: FLMP A -1; Stop; FLMP B -1; Stop; Select: FLMS A 1 A_Raise(int.max); Wait; Ready: FLMS ABCDEF 2 A_WeaponReady(WRF_NOFIRE); FLMS G 0 { A_PlaySound("flamet/idle",CHAN_6,Dampener.Active(self)?.1:1.,true); A_Overlay(-2,"FlameReady"); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE|PSPF_ALPHA|PSPF_FORCEALPHA,true); A_OverlayRenderStyle(-2,STYLE_Add); } FLMS GHIJ 2 A_WeaponReady(WRF_NOFIRE); Goto Idle; Dummy: TNT1 A 1 { A_CheckReload(); A_WeaponReady(); } Wait; Idle: FLMI A 0 A_Overlay(-9999,"Dummy"); FLMI ABCDEFG 12 A_SoundVolume(CHAN_6,Dampener.Active(self)?.1:1.); FLMI A 0 A_Jump(20,"Twiddle"); Goto Idle+1; Twiddle: #### # 2 { A_SoundVolume(CHAN_6,Dampener.Active(self)?.1:1.); player.SetPSprite(-2,ResolveState("FlameTwiddle")); } FLMT ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 2 A_SoundVolume(CHAN_6,Dampener.Active(self)?.1:1.); FLT2 ABCDEF 2 A_SoundVolume(CHAN_6,Dampener.Active(self)?.1:1.); Goto Idle+1; Fire: #### # 2 { A_Overlay(-9999,"Null"); player.SetPSprite(-2,ResolveState("FlameFire")); } FLMF ABCDE 2; FLMF F 0 A_BeginFlame(); Goto Hold; Dummy2: TNT1 A 1 A_FireFlame(); Wait; Hold: FLMF FGHIJK 2 A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.); Loop; Release: #### # 2 { A_Overlay(-9999,"Null"); player.SetPSprite(-2,ResolveState("FlameRelease")); A_PlaySound("flamet/fireend",CHAN_WEAPON,Dampener.Active(self)?.1:1.); A_ClearRefire(); } FLMF MNOP 2; Goto Idle; AltFire: #### # 2 { A_Overlay(-9999,"Null"); player.SetPSprite(-2,ResolveState("FlameAltFire")); invoker.bCharging = true; } FLMA ABCDE 2; FLMA F 0 A_BeginCharge(); Goto AltHold; Dummy3: TNT1 A 1 A_ChargeUp(); Wait; AltHold: FLMA FIGHLKGJHLGFHJGILHFKGFKHIGKGHL 10; Loop; AltRelease: FLMA M 0 { A_FireNapalm(); player.SetPSprite(-2,ResolveState("FlameAltRelease")); } FLMA MNOPQRSTUVWX 2; FLMA Y 0 { if ( invoker.CheckAmmo(1,false,true) ) A_Refire("GotoAltHold"); } FLMA Y 0 A_ClearRefire(); FLMA YZ[\] 2; FLA2 ABCD 2; Goto Idle; GotoAltHold: FLMA Y 0 { player.SetPSprite(-2,ResolveState("FlameAltHold")); } Goto AltHold; Deselect: #### # 1 { A_Overlay(-9999,"Null"); A_PlaySound("flamet/down",CHAN_6,Dampener.Active(self)?.1:1.); player.SetPSprite(-2,ResolveState("FlameDeselect")); } FLMD ABCDEFHIJ 1; FLMD J 1 A_Lower(int.max); Wait; FlameReady: FLFS ABCD 2 Bright; Goto FlameIdle; FlameIdle: FLFI ABCDEFG 12 Bright; Loop; FlameTwiddle: #### # 2 Bright; FLFT ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 2 Bright; FFT2 ABCDEF 2 Bright; Goto FlameIdle; FlameFire: #### # 1 Bright; FLFF ABCDE 2 Bright; Goto FlameHold; FlameHold: FLFF FGHIJK 2 Bright; Loop; FlameRelease: #### # 2 Bright; FLFF MNOP 2 Bright; Goto FlameIdle; FlameAltFire: #### # 2 Bright; FLFA ABCDE 2 Bright; Goto FlameAltHold; FlameAltHold: FLFA FIGHLKGJHLGFHJGILHFKGFKHIGKGHL 10 Bright; Loop; FlameAltRelease: FLFA MNOPQRSTUVWXYZ[\] 2 Bright; FFA2 ABCD 2 Bright; Goto FlameIdle; FlameDeselect: #### # 1 Bright; FLFD ABCD 2 Bright; Stop; } }