Class FlakAmmo : Ammo { Default { Tag "Flak Shells"; Inventory.PickupMessage "You picked up %d Flak Shells."; Inventory.Amount 10; Inventory.MaxAmount 50; Ammo.BackpackAmount 20; Ammo.BackpackMaxAmount 100; Ammo.DropAmount 5; } override String PickupMessage() { return String.Format(pickupmsg,Amount); } States { Spawn: FAMO A -1; Stop; } } Class FlakAmmo2 : FlakAmmo { Default { Tag "Flak Shell"; Inventory.PickupMessage "You picked up a Flak Shell."; Inventory.Amount 1; Ammo.DropAmount 1; +INVENTORY.IGNORESKILL; } States { Spawn: FSLG A -1; Stop; } } Class ChunkLight : DynamicLight { Default { DynamicLight.Type "Point"; Args 255,224,128,8; } override void Tick() { Super.Tick(); if ( !target ) { Destroy(); return; } if ( globalfreeze || level.frozen ) return; args[LIGHT_RED] = int(255*(10-target.frame)*0.1); args[LIGHT_GREEN] = int(224*(10-target.frame)*0.1); args[LIGHT_BLUE] = int(128*(10-target.frame)*0.1); } } Class ChunkTrail : Actor { Default { RenderStyle "Add"; Radius 0.1; Height 0; +NOBLOCKMAP; +NOGRAVITY; +DONTSPLASH; +FORCEXYBILLBOARD; Scale 0.2; } override void PostBeginPlay() { Super.PostBeginPlay(); let l = Spawn("ChunkLight",pos); l.target = self; } override void Tick() { Super.Tick(); if ( globalfreeze || level.frozen ) return; if ( InStateSequence(CurState,FindState("Death")) ) return; if ( !target ) { int dist = FindState("Spawn").DistanceTo(CurState); SetState(FindState("Death")+dist); return; } SetOrigin(target.pos+(0,0,speed),true); } States { Spawn: FGLO ABCDEFGHIJK 3 Bright; Stop; Death: FGLO ABCDEFGHIJK 1 Bright; Stop; } } Class FlakAccumulator : Thinker { Actor victim; int amount; int cnt; override void Tick() { Super.Tick(); cnt++; if ( cnt < 5 ) return; Destroy(); } static void Accumulate( Actor victim, int amount ) { let ti = ThinkerIterator.Create("FlakAccumulator",STAT_USER); FlakAccumulator a, match = null; while ( a = FlakAccumulator(ti.Next()) ) { if ( a.victim != victim ) continue; match = a; break; } if ( !match ) { match = new("FlakAccumulator"); match.ChangeStatNum(STAT_USER); match.victim = victim; } match.cnt = 0; match.amount += amount; } static int GetAmount( Actor victim ) { let ti = ThinkerIterator.Create("FlakAccumulator",STAT_USER); FlakAccumulator a; while ( a = FlakAccumulator(ti.Next()) ) { if ( a.victim != victim ) continue; return a.amount; } return 0; } } Class FlakChunk : Actor { ChunkTrail trail; double rollvel, pitchvel, yawvel; double lifetime, lifespeed; int lifetics; Default { Obituary "%o was ripped to shreds by %k's Flak Cannon."; Radius 2; Height 2; Speed 50; DamageFunction Random[Flak](15,20); DamageType 'Shredded'; BounceType "Doom"; BounceFactor 0.8; WallBounceFactor 0.8; PROJECTILE; +USEBOUNCESTATE; -BOUNCEAUTOOFF; +CANBOUNCEWATER; +SKYEXPLODE; +INTERPOLATEANGLES; Scale 0.3; } override void PostBeginPlay() { Super.PostBeginPlay(); lifetime = 0; lifespeed = FRandom[Flak](0.004,0.008); trail = ChunkTrail(Spawn("ChunkTrail",pos)); trail.target = self; trail.speed = 1.2; rollvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1); pitchvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1); yawvel = FRandom[Flak](50,100)*RandomPick[Flak](-1,1); scale *= Frandom[Flak](0.8,1.2); SetState(ResolveState("Spawn")+Random[Flak](0,3)); } override void Tick() { Super.Tick(); if ( globalfreeze || level.frozen ) return; if ( waterlevel > 0 ) { vel.xy *= 0.98; rollvel *= 0.98; pitchvel *= 0.98; yawvel *= 0.98; if ( trail ) trail.Destroy(); } lifetics++; if ( lifetics > 3 ) { lifetics = 0; if ( frame < 11 ) frame++; } lifetime += lifespeed; if ( (waterlevel <= 0) && (frame < 10) && !(lifetics%2) ) { let s = Spawn("UTSmoke",pos); s.vel = (FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1)); s.alpha = scale.x/0.5; s.SetShade("AAAAAA"); } else if ( waterlevel > 0 ) { let s = Spawn("UTBubble",pos); s.vel = (FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1),FRandom[Flak](-0.1,0.1)); s.scale *= scale.x*0.5; } if ( trail ) trail.alpha = max(0,11-frame)/11.; if ( InStateSequence(CurState,FindState("Death")) ) return; roll += rollvel; pitch += pitchvel; angle += pitchvel; } action void A_HandleBounce() { bHITOWNER = true; A_SprayDecal("WallCrack",-8); int numpt = Random[Flak](2,3); if ( frame < 10 ) { for ( int i=0; i 0 ) vel.xy *= 0.98; } action void A_FlakExplode() { bForceXYBillboard = true; A_SetRenderStyle(1.0,STYLE_Add); A_SprayDecal("RocketBlast",50); A_NoGravity(); A_SetScale(1.2); UTMainHandler.DoBlast(self,120,75000); A_Explode(Random[Flak](70,80),120); A_QuakeEx(4,4,4,8,0,170,"",QF_RELATIVE|QF_SCALEDOWN,falloff:120,rollIntensity:0.2); A_PlaySound("flak/explode",CHAN_VOICE); A_AlertMonsters(); if ( !Tracer ) Spawn("SlugSmoke",pos); Spawn("SlugLight",pos); Vector3 x, y, z; double a, s; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Actor p; for ( int i=0; i<5; i++ ) { p = Spawn("FlakChunk",pos); p.bHITOWNER = true; a = FRandom[Flak](0,360); s = FRandom[Flak](0,0.2); Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit(); p.angle = atan2(dir.y,dir.x); p.pitch = -asin(dir.z); p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed*FRandom[Flak](0.5,1.5); p.target = target; } int numpt = Random[Flak](8,12); for ( int i=0; i 2 ) Destroy(); } } Class FlakCannon : UTWeapon { action void A_Loading( bool first = false ) { if ( first ) A_PlaySound("flak/load",CHAN_WEAPON); else A_PlaySound("flak/reload",CHAN_6); } action void A_FireChunks() { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; A_PlaySound("flak/fire",CHAN_WEAPON); invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(160,255,96,0),1); UTMainHandler.DoSwing(self,(FRandom[Flak](-0.3,-0.8),FRandom[Flak](-0.5,0.5)),4,-1.5,2,SWING_Spring,2,2); A_AlertMonsters(); A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05); Vector3 x, y, z; double a, s; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+4.0*y-3.0*z; A_Overlay(-2,"MuzzleFlash"); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-2,STYLE_Add); [x, y, z] = Matrix4.GetAxes(BulletSlope(),angle,roll); Actor p; for ( int i=0; i<6; i++ ) { p = Spawn("FlakChunk",origin); a = FRandom[Flak](0,360); s = FRandom[Flak](0,0.2); Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit(); p.angle = atan2(dir.y,dir.x); p.pitch = -asin(dir.z); 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[Flak](20,30); for ( int i=0; i 0 ) A_Loading(); } FLKL BCEFGIJKMNO 1; Idle: FLKI A 1 A_WeaponReady(); Wait; Fire: FLKF A 1 A_FireChunks(); FLKF BCDEFGHI 1; FLKF J 4; Goto Loading; AltFire: FLKA A 1 A_FireSlug(); FLKA BCDEFGHIJK 2; FLKA K 4; Goto Loading; Select: FLKS A 1 A_Raise(int.max); Wait; Deselect: FLKD ABCDEFGHIJ 1; FLKD J 1 A_Lower(int.max); Wait; MuzzleFlash: FMUZ A 3 Bright { let l = Spawn("FlakLight",pos); l.target = self; } Stop; } }