Class UMiniAmmo : Ammo { Default { Tag "$T_MINIAMMO"; Inventory.Icon "I_ShellA"; Inventory.PickupMessage ""; Inventory.Amount 50; Inventory.MaxAmount 200; Ammo.BackpackAmount 25; Ammo.BackpackMaxAmount 400; Ammo.DropAmount 20; } override String PickupMessage() { if ( PickupMsg.Length() > 0 ) return Super.PickupMessage(); return String.Format("%s%d%s",StringTable.Localize("$I_MINIAMMOL"),Amount,StringTable.Localize("$I_MINIAMMOR")); } States { Spawn: MAMO A -1; Stop; } } Class UMinigun : UnrealWeapon { 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); if ( !Dampener.Active(self) ) A_AlertMonsters(); A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.); 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,2,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,2,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] = dt_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*3-z*3); double a = FRandom[Minigun](0,360), s = FRandom[Minigun](0,alt?0.05:0.02); [x2, y2, z2] = dt_CoordUtil.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); UTBulletTrail.DoTrail(self,origin,dir,10000,alt?5:3); if ( d.HitType == TRACE_HitActor ) { int dmg = Random[Minigun](8,14); // fun fact: the Minigun is one of the few weapons that has actual RNG damage in Unreal 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.; 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 ) { if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal; else hitnormal = d.HitSector.floorplane.Normal; } else if ( d.HitType == TRACE_HitCeiling ) { if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal; else 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",level.Vec3Offset(origin,x*20)); t.angle = atan2(dir.y,dir.x); t.pitch = asin(-dir.z); MinigunTracer(t).dest = d.HitLocation; } for ( int i=0; i<2; i++ ) { let s = Spawn("UTViewSmoke",origin); UTViewSmoke(s).ofs = (10,3,-3); 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 = level.Vec3Offset(origin,x*4+y*3-z*12); let c = Spawn("UCasing",origin); c.angle = angle; c.pitch = pitch; c.vel = x*FRandom[Junk](-1.5,1.5)-y*FRandom[Junk](2,4)+z*FRandom[Junk](-2,1); } action void A_MinigunRefire() { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( weap.Ammo1.Amount <= 0 ) { A_ClearRefire(); return; } weap.bAltFire = !!(player.cmd.buttons&BT_ALTATTACK); invoker.bcnt = 5; A_Refire("Hold"); } Default { Tag "$T_MINIGUN"; Inventory.PickupMessage "$I_MINIGUN"; Weapon.UpSound "minigun/select"; Weapon.SlotNumber 0; Weapon.SelectionOrder 500; Weapon.SlotPriority 1; Weapon.AmmoType "UMiniAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UMiniAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 50; UTWeapon.DropAmmo 25; } States { Spawn: MGNP A -1; Stop; MGNP B -1; Stop; Select: MGNS A 1 A_Raise(int.max); Wait; Ready: MGNS ABCDEFGHIJKLMNOPQRSTUVW 1 A_WeaponReady(WRF_NOFIRE); Idle: MGNI A 1 { A_CheckReload(); A_WeaponReady(); } Wait; FireDummy: TNT1 A 1 { if ( Health <= 0 ) return ResolveState("Null"); let weap = Weapon(invoker); if ( !(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)) || (weap.Ammo1.Amount <= 0) ) { A_StopSound(CHAN_WEAPON); player.SetPSprite(PSP_WEAPON,invoker.FindState("Release")); return ResolveState("Null"); } return ResolveState(null); } Wait; Fire: AltFire: MGNW A 0 A_PlaySound("umini/wind",CHAN_WEAPON,Dampener.Active(self)?.1:1.); MGNW ABCDEFGHIJKLMNO 1; MGNW O 0 { invoker.bcnt = 5; A_Refire(1); } Goto Release; Hold: MGNF A 0 { A_Overlay(-9999,"FireDummy"); A_PlaySound("umini/fire",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true); } MGNF ABCDEFGHIJKLMNO 1 A_FireBullet(false); MGNF A 0 A_JumpIf(player.cmd.buttons&BT_ALTATTACK,1); Goto Hold+1; AltHold: MGNA A 0 { A_Overlay(-9999,"FireDummy"); A_PlaySound("umini/altfire",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true); } MGNA ABCDEFGHIJKLMNOPQRST 1 A_FireBullet(true); Goto AltHold+1; Release: MGNU A 0 A_PlaySound("umini/unwind",CHAN_WEAPON,Dampener.Active(self)?.1:1.); MGNU ABCDEFGHIJKLMNO 2 A_MinigunRefire(); Goto Idle; Deselect: MGND A 0 A_StopSound(CHAN_WEAPON); MGND ABCDEFGHIJ 1; MGND J 1 A_Lower(int.max); Wait; MuzzleFlash: MMUZ # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Minigun](0,8); } Stop; } }