Class StingerAmmo : Ammo { Default { Tag "$T_STINGERAMMO"; Inventory.Icon "I_Stingr"; Inventory.PickupMessage ""; Inventory.Amount 5; Inventory.MaxAmount 200; Ammo.BackpackAmount 20; Ammo.BackpackMaxAmount 400; Ammo.DropAmount 2; } override String PickupMessage() { return String.Format("%s%d%s",StringTable.Localize("$I_STINGERAMMOL"),Amount,StringTable.Localize("$I_STINGERAMMOR")); } States { Spawn: SAMO A -1; Stop; } } Class StingerAmmo2 : StingerAmmo { Default { Tag "$T_STINGERAMMO2"; Inventory.Amount 40; Ammo.DropAmount 15; } States { Spawn: SAMO B -1; Stop; } } Class StingerBurstLight : PaletteLight { Default { Tag "Blue4"; Args 0,0,0,30; ReactionTime 12; } } Class StingerChunk : Actor { int deadtimer; double rollvel, anglevel, pitchvel; Default { Radius 2; Height 2; +NOBLOCKMAP; +MISSILE; +MOVEWITHSECTOR; +THRUACTORS; +NOTELEPORT; +DONTSPLASH; +INTERPOLATEANGLES; BounceType "Doom"; BounceFactor 0.4; Gravity 0.35; } override void PostBeginPlay() { Super.PostBeginPlay(); deadtimer = 0; anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); frame = Random[Junk](0,8); scale *= Frandom[Junk](0.4,0.8); } override void Tick() { Super.Tick(); if ( isFrozen() ) return; if ( InStateSequence(CurState,ResolveState("Death")) ) { deadtimer++; if ( deadtimer > 60 ) A_FadeOut(0.05); return; } } States { Spawn: CHIP # 1 { angle += anglevel; pitch += pitchvel; roll += rollvel; } Loop; Bounce: CHIP # 0 { anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); } Goto Spawn; Death: CHIP # -1; Stop; Dummy: CHIP ABCDEFGHI -1; Stop; } } Class ViewStingerChunk : StingerChunk { Vector3 ofs, vvel; Default { +NOCLIP; +NOGRAVITY; -MISSILE; BounceType "None"; } override void Tick() { Actor.Tick(); if ( !target || !target.player ) { Destroy(); return; } Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll); Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); SetOrigin(origin,true); bInvisible = (players[consoleplayer].camera != target); if ( isFrozen() ) return; ofs += vvel; scale *= 0.75; if ( scale.x <= 0.01 ) Destroy(); } } Class TarydiumExLight : PaletteLight { Default { Tag "Blue2"; Args 0,0,0,60; ReactionTime 30; } } Class TarydiumExplosion : Actor { Default { RenderStyle "Add"; +NOGRAVITY; +NOBLOCKMAP; +FORCEXYBILLBOARD; +FORCERADIUSDMG; +NODAMAGETHRUST; DamageType 'TarydiumCharge'; Obituary "$O_STINGERX"; } override void PostBeginPlay() { Super.PostBeginPlay(); A_Explode(max(10,special1),90+special1); UTMainHandler.DoBlast(self,90+special1,900*special1); UTMainHandler.DoBlast(self,200+special1,200*special1); // hurtless secondary blast, so stuff that's pretty close by gets pushed away // (just going off the novels here, it's supposed to be pretty strong) A_QuakeEx(clamp(special1/6,2,9),clamp(special1/6,2,9),clamp(special1/6,2,9),10,0,300+special1*2,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200+special1*2,rollIntensity:0.12); SetOrigin(Vec3Offset(0,0,16),false); A_StartSound("stinger/explode",CHAN_VOICE,pitch:FRandom[Stinger](0.8,1.5)); A_StartSound("stinger/explode",CHAN_ITEM,pitch:FRandom[Stinger](0.8,1.5)); let l = Spawn("TarydiumExLight",pos); l.args[3] = int(60*scale.x); scale.x *= RandomPick[Stinger](-1,1); scale.y *= RandomPick[Stinger](-1,1); double ang, pt; int numpt = Random[Stinger](10,15)*min(3,special1/60); for ( int i=0; i 0 ) return; explodetimer = Random[Stinger](3,6); BlowUp(); return; } if ( victim.Health <= 0 ) { exploding = true; return; } if ( victim.pos.z > victim.floorz ) wasonair = true; else { if ( wasonair && (oldvel.z < -20) ) { Amount += min(int(-oldvel.z),100); exploding = true; return; } wasonair = false; } oldvel = victim.vel; amount *= 1.-.04/TICRATE; UpdateEffect(); if ( amount <= .4 ) { Destroy(); return; } if ( level.maptime%3 ) return; int numpt = clamp(int(Random[Stinger](1,3)*amount*0.02),1,8); for ( int i=0; ivictim.floorz); t.UpdateEffect(); } } Class StingerProjectile : Actor { Vector3 oldvel; Default { Obituary "$O_STINGER"; DamageType 'Stinger'; DamageFunction 14; Speed 40; Radius 2; Height 2; PROJECTILE; +SKYEXPLODE; } override void PostBeginPlay() { Super.PostBeginPlay(); if ( sting_stingerb ) { // set bounce stuff bBOUNCEONWALLS = true; bBOUNCEONFLOORS = true; bBOUNCEONCEILINGS = true; bALLOWBOUNCEONACTORS = true; bCANBOUNCEWATER = true; bUSEBOUNCESTATE = true; BounceCount = 4; BounceFactor = 1.; WallBounceFactor = 1.; } } override void Tick() { oldvel = vel; Super.Tick(); } void A_HandleBounce() { // cancel bounce based on "incidence" if ( vel dot oldvel < 0.87 ) { ClearBounce(); ExplodeMissile(); return; } if ( !Random[Stinger](0,2) ) A_StartSound("stinger/hit2",volume:.5,pitch:FRandom[Stinger](0.5,1.5)); else A_StartSound("stinger/hit",volume:.6); A_SprayDecal("WallCrack",-20); A_AlertMonsters(); let l = Spawn("StingerBurstLight",pos); l.Args[3] /= 2; double ang, pt; int numpt = Random[Stinger](1,3); for ( int i=0; i 0 ) Spawn("UTBubble",pos); } Wait; Bounce: TPRJ A 0 Bright A_HandleBounce(); Goto Spawn; Death: Crash: TNT1 A 1 A_StingerHit(); Stop; XDeath: TNT1 A 1; Stop; } } Class StingerLight : EnforcerLight { Default { args 64,128,255,80; } } Class Stinger : UnrealWeapon { int flashnum; Default { Tag "$T_STINGER"; Inventory.PickupMessage "$I_STINGER"; Weapon.UpSound "stinger/select"; Weapon.SlotNumber 3; Weapon.SelectionOrder 2500; Weapon.SlotPriority 1; Weapon.AmmoType "StingerAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "StingerAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 40; UTWeapon.DropAmmo 20; } action void A_StingerFire( bool hold = false ) { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; if ( hold ) { A_SoundVolume(CHAN_WEAPONMISC,Dampener.Active(self)?.1:1.); A_Overlay(PSP_FLASH,"MFlashHold"); } else { A_StartSound("stinger/fire",CHAN_WEAPON,CHANF_OVERLAP,Dampener.Active(self)?.1:1.); A_Overlay(PSP_FLASH,"MFlash"); } A_OverlayFlags(PSP_FLASH,PSPF_RenderStyle,true); A_OverlayRenderstyle(PSP_FLASH,STYLE_Add); invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(16,0,64,255),1); UTMainHandler.DoSwing(self,(FRandom[Stinger](-0.1,-0.2),FRandom[Stinger](-0.1,0.1)),4,-1.5,2,SWING_Spring,2,2); if ( !Dampener.Active(self) ) A_AlertMonsters(); A_QuakeEx(1,1,1,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1); Vector3 x, y, z; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),5*x+8*y-8*z); Actor p = Spawn("StingerProjectile",origin); p.angle = angle; p.pitch = BulletSlope(); p.roll = FRandom[Stinger](0,360); p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed; p.target = self; int numpt = Random[Stinger](5,9); double ang; for ( int i=0; i0)?.08:0.); // first shot always accurate dir = (x+y*cos(a)*s+z*sin(a)*s).unit(); p = Spawn("StingerProjectile",origin); p.angle = atan2(dir.y,dir.x); p.pitch = asin(-dir.z); p.roll = FRandom[Stinger](0,360); p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed; p.target = self; int numpt = Random[Stinger](5,9); double ang; for ( int i=0; i