Class MiniAmmo : Ammo { Default { Tag "Large Bullets"; Inventory.PickupMessage "You picked up %d bullets."; Inventory.Amount 50; Inventory.MaxAmount 200; Ammo.BackpackAmount 100; Ammo.BackpackMaxAmount 400; Ammo.DropAmount 20; } override String PickupMessage() { return String.Format(pickupmsg,Amount); } States { Spawn: MAMO A -1; Stop; } } Class MinigunLight : EnforcerLight { override void PostBeginPlay() { Super.PostBeginPlay(); args[LIGHT_INTENSITY] = Random[Minigun](120,180); } } Class MinigunTracer : Actor { Vector3 dest; Default { RenderStyle "Add"; Radius 0.1; Height 0; +NOCLIP; +NOGRAVITY; +DONTSPLASH; +INTERPOLATEANGLES; } override void Tick() { Super.Tick(); if ( level.frozen || globalfreeze ) return; Vector3 dir = level.Vec3Diff(pos,dest); if ( dir.length() < 160 ) { Destroy(); return; } dir = dir.unit(); SetOrigin(Vec3Offset(dir.x*160,dir.y*160,dir.z*160),true); angle = atan2(dir.y,dir.x); pitch = asin(-dir.z); roll += 60; } States { Spawn: TRAC A -1 Bright; Stop; } } Class Minigun : UTWeapon { int bcnt, tcnt; action void A_FireBullet( bool alt = false ) { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; A_Overlay(-2,"MuzzleFlash",true); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-2,STYLE_Add); invoker.bcnt++; if ( (alt && (invoker.bcnt < 2)) || (!alt && (invoker.bcnt < 4)) ) return; invoker.bcnt = 0; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(32,255,255,0),1); A_AlertMonsters(); if ( alt ) { A_QuakeEx(2,2,2,8,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); UTMainHandler.DoSwing(self,(FRandom[Minigun](-1,1),FRandom[Minigun](-1,1)),0.5,0,1,SWING_Spring,0,3); } else { A_QuakeEx(1,1,1,8,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.08); UTMainHandler.DoSwing(self,(FRandom[Minigun](-1,1),FRandom[Minigun](-1,1)),0.3,0,1,SWING_Spring,0,3); } let l = Spawn("MinigunLight",pos); l.target = self; if ( !alt ) MinigunLight(l).cnt--; Vector3 x, y, z, x2, y2, z2; [x, y, z] = Matrix4.GetAxes(pitch,angle,roll); Vector3 origin = pos+(0,0,player.viewheight)+10.0*x+y*2.0-z*2.0; double a = FRandom[Minigun](0,360), s = FRandom[Minigun](0,alt?0.05:0.02); [x2, y2, z2] = Matrix4.GetAxes(BulletSlope(),angle,roll); Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); FLineTraceData d; LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); if ( d.HitType == TRACE_HitActor ) { int dmg = Random[Minigun](9,18); // fun fact: the Minigun is one of the few weapons that has actual RNG damage in UT dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); double mm = 500; if ( FRandom[Minigun](0,1) < 0.2 ) mm *= 2.5; UTMainHandler.DoKnockback(d.HitActor,d.HitDir,dmg*mm); if ( d.HitActor.bNOBLOOD ) { let p = Spawn("BulletImpact",d.HitLocation); p.scale *= 0.75; p.angle = atan2(d.HitDir.y,d.HitDir.x)+180; p.pitch = asin(d.HitDir.z); } else { d.HitActor.TraceBleed(dmg,self); d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); } } else if ( d.HitType != TRACE_HitNone ) { Vector3 hitnormal = -d.HitDir; if ( d.HitType == TRACE_HitFloor ) hitnormal = d.HitSector.floorplane.Normal; else if ( d.HitType == TRACE_HitCeiling ) hitnormal = d.HitSector.ceilingplane.Normal; else if ( d.HitType == TRACE_HitWall ) { hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit(); if ( !d.LineSide ) hitnormal *= -1; } let p = Spawn("BulletImpact",d.HitLocation+hitnormal*0.01); p.scale *= 0.75; p.angle = atan2(hitnormal.y,hitnormal.x); p.pitch = asin(-hitnormal.z); if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); } if ( !Random[Minigun](0,1) ) { let t = Spawn("MinigunTracer",origin+x*20.0); t.angle = atan2(dir.y,dir.x); t.pitch = asin(-dir.z); MinigunTracer(t).dest = d.HitLocation; } for ( int i=0; i<4; i++ ) { let s = Spawn("UTViewSmoke",origin); UTViewSmoke(s).ofs = (10,2,-2); s.scale *= 1.5; s.alpha *= 0.6; UTViewSmoke(s).vvel += (FRandom[Minigun](0.2,0.8),FRandom[Minigun](-0.3,0.3),FRandom[Minigun](-0.3,0.3)); s.target = self; } origin += x*8.0+y*5.0-z*5.0; let c = Spawn("UTCasing",origin); c.vel = x*FRandom[Junk](-2,2)+y*FRandom[Junk](3,6)+z*FRandom[Junk](3,5); c.Scale *= 0.5; } action void A_MinigunRefire( statelabel flash = null, bool nounwind = false ) { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( weap.Ammo1.Amount <= 0 ) { if ( nounwind ) return; A_ClearRefire(); player.setpsprite(PSP_WEAPON,weap.FindState("Unwind")); return; } if ( nounwind ) { if ( player.cmd.buttons&BT_ALTATTACK ) { weap.bAltFire = true; flash = "AltFire"; } else { weap.bAltFire = false; flash = "Fire"; } } A_Refire(flash); } Default { Tag "Minigun"; Obituary "%k's Minigun turned %o into a leaky piece of meat."; Inventory.PickupMessage "You got the Minigun."; Weapon.UpSound "minigun/select"; Weapon.SlotNumber 7; Weapon.SelectionOrder 3; Weapon.AmmoType "MiniAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "MiniAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 50; Weapon.Kickback 180; UTWeapon.DropAmmo 20; } States { Spawn: MGNP A -1; Stop; MGNP B -1; Stop; Select: MGNS A 1 A_Raise(int.max); Wait; Ready: MGNS ABCDEFGHIJKLMNOPQRST 1 A_WeaponReady(WRF_NOFIRE); Idle: MGNI A 1 { A_CheckReload(); A_WeaponReady(); } Wait; Fire: AltFire: MGNI A 3 { invoker.bcnt = 5; } Hold: MGNF A 1 { A_PlaySound("minigun/fire",CHAN_WEAPON,1.0,true); A_FireBullet(); } MGNF B 0 A_MinigunRefire(1); Goto Unwind; MGNF B 1 A_FireBullet(); MGNF C 0 A_MinigunRefire(1); Goto Unwind; MGNF C 1 A_FireBullet(); MGNF D 0 A_MinigunRefire(1); Goto Unwind; MGNF D 1 A_FireBullet(); MGNF E 0 A_MinigunRefire(1); Goto Unwind; MGNF E 1 A_FireBullet(); MGNF F 0 A_MinigunRefire(1); Goto Unwind; MGNF F 1 A_FireBullet(); MGNF G 0 A_MinigunRefire(1); Goto Unwind; MGNF G 1 A_FireBullet(); MGNF H 0 A_MinigunRefire(1); Goto Unwind; MGNF H 1 A_FireBullet(); MGNF I 0 A_MinigunRefire(1); Goto Unwind; MGNF I 1 A_FireBullet(); MGNF J 0 A_MinigunRefire(1); Goto Unwind; MGNF J 1 A_FireBullet(); MGNF K 0 A_MinigunRefire(1); Goto Unwind; MGNF K 1 A_FireBullet(); MGNF L 0 A_MinigunRefire(1); Goto Unwind; MGNF L 1 A_FireBullet(); MGNF M 0 A_MinigunRefire(1); Goto Unwind; MGNF M 1 A_FireBullet(); MGNF N 0 A_MinigunRefire(1); Goto Unwind; MGNF N 1 A_FireBullet(); MGNF O 0 A_MinigunRefire(1); Goto Unwind; MGNF O 1 A_FireBullet(); MGNF P 0 A_MinigunRefire(1); Goto Unwind; MGNF P 1 A_FireBullet(); MGNF Q 0 A_MinigunRefire(1); Goto Unwind; MGNF Q 1 A_FireBullet(); MGNF R 0 A_MinigunRefire(1); Goto Unwind; MGNF R 1 A_FireBullet(); MGNF S 0 A_MinigunRefire(1); Goto Unwind; MGNF S 1 A_FireBullet(); MGNF A 0 { if ( invoker.bAltFire ) A_MinigunRefire(1); else A_MinigunRefire("Hold"); } Goto Unwind; AltHold: MGNF A 1 { A_PlaySound("minigun/altfire",CHAN_WEAPON,1.0,true); A_FireBullet(true); } MGNF D 0 A_MinigunRefire(1); Goto Unwind; MGNF D 1 A_FireBullet(true); MGNF G 0 A_MinigunRefire(1); Goto Unwind; MGNF G 1 A_FireBullet(true); MGNF J 0 A_MinigunRefire(1); Goto Unwind; MGNF J 1 A_FireBullet(true); MGNF M 0 A_MinigunRefire(1); Goto Unwind; MGNF M 1 A_FireBullet(true); MGNF P 0 A_MinigunRefire(1); Goto Unwind; MGNF P 1 A_FireBullet(true); MGNF S 0 A_MinigunRefire(1); Goto Unwind; MGNF S 1 A_FireBullet(true); MGNF C 0 A_MinigunRefire(1); Goto Unwind; MGNF C 1 A_FireBullet(true); MGNF F 0 A_MinigunRefire(1); Goto Unwind; MGNF F 1 A_FireBullet(true); MGNF I 0 A_MinigunRefire(1); Goto Unwind; MGNF I 1 A_FireBullet(true); MGNF L 0 A_MinigunRefire(1); Goto Unwind; MGNF L 1 A_FireBullet(true); MGNF O 0 A_MinigunRefire(1); Goto Unwind; MGNF O 1 A_FireBullet(true); MGNF R 0 A_MinigunRefire(1); Goto Unwind; MGNF R 1 A_FireBullet(true); MGNF B 0 A_MinigunRefire(1); Goto Unwind; MGNF B 1 A_FireBullet(true); MGNF E 0 A_MinigunRefire(1); Goto Unwind; MGNF E 1 A_FireBullet(true); MGNF H 0 A_MinigunRefire(1); Goto Unwind; MGNF H 1 A_FireBullet(true); MGNF K 0 A_MinigunRefire(1); Goto Unwind; MGNF K 1 A_FireBullet(true); MGNF N 0 A_MinigunRefire(1); Goto Unwind; MGNF N 1 A_FireBullet(true); MGNF Q 0 A_MinigunRefire(1); Goto Unwind; MGNF Q 1 A_FireBullet(true); MGNF A 0 A_MinigunRefire("AltHold"); Goto Unwind; Unwind: MGNU A 0 A_PlaySound("minigun/unwind",CHAN_WEAPON); MGNU ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_MinigunRefire(null,true); MGU2 ABCDEFGHIJKLM 1 A_MinigunRefire(null,true); Goto Idle; Deselect: MGND A 1 A_StopSound(CHAN_WEAPON); MGND BCDEFGHIJ 1; MGND J 1 A_Lower(int.max); Wait; MuzzleFlash: TNT1 A 0 A_Jump(256,1,2,3,4,5,6,7,8,9); Stop; MMUZ A 2 Bright; Stop; MMUZ B 2 Bright; Stop; MMUZ C 2 Bright; Stop; MMUZ D 2 Bright; Stop; MMUZ E 2 Bright; Stop; MMUZ F 2 Bright; Stop; MMUZ G 2 Bright; Stop; MMUZ H 2 Bright; Stop; MMUZ I 2 Bright; Stop; } }