Class UClip : UMiniAmmo { Default { Tag "$T_CLIP"; Inventory.PickupMessage "$I_CLIP"; Inventory.Amount 20; Ammo.DropAmount 5; } States { Spawn: UCLP A -1; Stop; } } Class UCasing : UTCasing { Default { BounceSound "automag/casing"; } } Class AutomagMag : EnforcerMag { } Class Automag : UnrealWeapon { int ClipCount, SlaveClipCount; bool SlaveActive, SlaveDown, SlaveReload, SlaveAltFire, SlaveSpin; bool ClipOut, SlaveClipOut; int SlaveRefire; double AltAccuracy; property ClipCount : ClipCount; property SlaveClipCount : SlaveClipCount; override int, int, bool, bool GetClipAmount() { if ( Amount > 1 ) return ClipOut?-1:ClipCount, SlaveClipOut?-1:SlaveClipCount, (ClipCount<5), (SlaveClipCount<5); return ClipOut?-1:ClipCount, -1, (ClipCount<5), false; } override bool HandlePickup( Inventory item ) { if ( item.GetClass() == GetClass()) { if ( MaxAmount > 1 ) { if ( Amount == 1 ) Weapon(item).AmmoGive1 = Weapon(item).AmmoGive2 = 0; if ( Weapon(item).PickupForAmmo(self) ) item.bPickupGood = true; let rslt = Inventory.HandlePickup(item); if ( rslt ) SetTag(StringTable.Localize("$T_AUTOMAG2")); return rslt; } else if ( Weapon(item).PickupForAmmo(self) ) item.bPickupGood = true; return true; } return false; } override String PickupMessage() { if ( Owner ) return Super.PickupMessage(); return StringTable.Localize("$I_AUTOMAG2"); } override Inventory CreateTossable( int amt ) { Inventory inv = Super.CreateTossable(amt); if ( inv ) { SetTag(StringTable.Localize("$T_AUTOMAG")); inv.SetTag(StringTable.Localize("$T_AUTOMAG")); if ( Owner && (Owner.player.ReadyWeapon == self) ) { // delete the slave overlay PSprite psp; for ( psp = Owner.player.psprites; psp; psp = psp.next ) { if ( (psp.Caller == self) && ((psp.id == 2) || (psp.id == -9998)) ) psp.Destroy(); slaveactive = false; slavedown = false; } } } return inv; } action void A_AutomagRefire( statelabel flash = null, bool slave = false ) { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( invoker.altaccuracy < 0.2 ) invoker.altaccuracy += 0.05; if ( slave ) { if ( invoker.slaveclipcount < 5 ) A_StartSound("automag/click",CHAN_LEFTWEAPONMISC,CHANF_DEFAULT,!Dampener.Active(self)?1.:.35); if ( invoker.slaveclipcount <= 0 ) { invoker.slaverefire = 0; return; } bool pending = (player.PendingWeapon != WP_NOCHANGE) && (player.WeaponState & WF_REFIRESWITCHOK); if ( (player.cmd.buttons&BT_ATTACK) && !invoker.slavealtfire && !pending && (player.health > 0) ) { invoker.slaverefire++; if ( invoker.slaveclipcount > 0 ) player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftHold")); } else if ( (player.cmd.buttons&BT_ALTATTACK) && invoker.slavealtfire && !pending && (player.health > 0) ) { invoker.slaverefire++; if ( invoker.slaveclipcount > 0 ) player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftAltHold")); } else invoker.slaverefire = 0; } else { if ( invoker.clipcount < 5 ) A_StartSound("automag/click",CHAN_WEAPONMISC,CHANF_DEFAULT,!Dampener.Active(self)?1.:.35); if ( invoker.clipcount <= 0 ) { A_ClearRefire(); return; } A_Refire(flash); } } action void A_LeftWeaponReady() { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( player.cmd.buttons&BT_ATTACK && !player.ReadyWeapon.bAltFire ) { if ( invoker.slaveclipcount <= 0 ) { let psp = player.FindPSprite(PSP_WEAPON); if ( (weap.Ammo1.Amount > 0) && !psp.CurState.InStateSequence(ResolveState("Reload")) ) player.setpsprite(2,ResolveState("LeftReload")); return; } invoker.slaverefire = 0; invoker.slavealtfire = false; player.setpsprite(2,ResolveState("LeftFire")); } else if ( player.cmd.buttons&BT_ALTATTACK && player.ReadyWeapon.bAltFire ) { if ( invoker.slaveclipcount <= 0 ) { let psp = player.FindPSprite(PSP_WEAPON); if ( (weap.Ammo1.Amount > 0) && !psp.CurState.InStateSequence(ResolveState("Reload")) ) player.setpsprite(2,ResolveState("LeftReload")); return; } invoker.slaverefire = 0; invoker.slavealtfire = true; player.setpsprite(2,ResolveState("LeftAltFire")); } } action void A_AutomagFire( bool alt = false, bool slave = false ) { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( slave ) { if ( invoker.slaveclipcount <= 0 ) return; invoker.slaveclipcount--; } else { if ( invoker.clipcount <= 0 ) return; invoker.clipcount--; } invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(32,255,128,0),1); A_StartSound("automag/fire",slave?CHAN_LEFTWEAPON:CHAN_WEAPON,CHANF_OVERLAP,!Dampener.Active(self)?1.:.2); if ( !Dampener.Active(self) ) A_AlertMonsters(); A_QuakeEx(2,2,2,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.08); if ( slave ) { if ( alt ) A_Overlay(-3,"LeftAltMuzzleFlash"); else A_Overlay(-3,"LeftMuzzleFlash"); A_OverlayFlags(-3,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-3,STYLE_Add); } else { if ( alt ) A_Overlay(-2,"AltMuzzleFlash"); else A_Overlay(-2,"MuzzleFlash"); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-2,STYLE_Add); } 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); int ydir = slave?-1:1; if ( alt ) origin = level.Vec3Offset(origin,-z*3+ydir*y); else origin = level.Vec3Offset(origin,-z+ydir*y*4); double a = FRandom[Automag](0,360), s = FRandom[Automag](0,alt?invoker.altaccuracy:0.01); if ( invoker.Amount > 1 ) s *= 1.6; [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,int((invoker.Amount+alt)**2)); if ( d.HitType == TRACE_HitActor ) { int dmg = 17; dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); double mm = 3000; if ( FRandom[Automag](0,1) < 0.2 ) mm *= 5; UTMainHandler.DoKnockback(d.HitActor,d.HitDir,mm); if ( d.HitActor.bNOBLOOD ) { let p = Spawn("BulletImpact",d.HitLocation); 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.angle = atan2(hitnormal.y,hitnormal.x); p.pitch = asin(-hitnormal.z); if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); } for ( int i=0; i<3; i++ ) { let s = Spawn("UTViewSmoke",origin); if ( alt ) UTViewSmoke(s).ofs = (10,ydir,-3); else UTViewSmoke(s).ofs = (10,4*ydir,-1); s.target = self; s.alpha *= 0.5; } origin = level.Vec3Offset(origin,x*8+ydir*y*6-z*2); let c = Spawn("UCasing",origin); c.angle = angle; c.pitch = pitch; c.vel = x*FRandom[Junk](-1.5,1.5)+y*ydir*FRandom[Junk](2,4)+z*FRandom[Junk](2,3); } override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( Amount > 1 ) return StringTable.Localize("$O_AUTOMAG2"); return StringTable.Localize("$O_AUTOMAG"); } override void Travelled() { Super.Travelled(); slaveactive = false; } override void OwnerDied() { Super.OwnerDied(); slavespin = slavereload = slaverefire = 0; if ( Owner.player.ReadyWeapon != self ) return; let psp = Owner.player.FindPSprite(2); if ( psp ) { slaveactive = false; Owner.player.SetPSprite(2,ResolveState("LeftDeselect")); } } override void DoEffect() { Super.DoEffect(); if ( Amount > 1 ) SelectionOrder = 1250; else SelectionOrder = default.SelectionOrder; } override void Tick() { Super.Tick(); if ( sting_automags && (MaxAmount <= 1) ) MaxAmount = 2; else if ( !sting_automags && (MaxAmount > 1) ) MaxAmount = 1; if ( !Owner || sting_automags || (Amount <= 1) ) return; // no dual wielding if ( Owner.player.ReadyWeapon == self ) { // delete the slave overlay PSprite psp; for ( psp = Owner.player.psprites; psp; psp = psp.next ) { if ( (psp.Caller == self) && ((psp.id == 2) || (psp.id == -9998)) ) psp.Destroy(); slaveactive = false; slavedown = false; } } Amount = 1; SetTag(StringTable.Localize("$T_AUTOMAG")); } override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) { if ( (ClipCount > 0) || ((Amount>1) && (SlaveClipCount > 0)) ) return true; return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); } Default { Tag "$T_AUTOMAG"; Inventory.Icon "I_ClipAm"; Inventory.PickupMessage "$I_AUTOMAG"; Inventory.MaxAmount 1; Inventory.InterHubAmount 2; Weapon.UpSound "automag/select"; Weapon.SlotNumber 2; Weapon.SelectionOrder 2800; Weapon.SlotPriority 1; Weapon.AmmoType "UMiniAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UMiniAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 20; Weapon.Kickback 180; UTWeapon.DropAmmo 10; Automag.ClipCount 20; Automag.SlaveClipCount 20; +UNREALWEAPON.NOFIRSTGIVE; } States { Spawn: AUTP A -1; Stop; AUTP B -1; Stop; Select: AUTS A 1 A_Raise(int.max); Wait; Ready: AUTS A 0 { invoker.slaveclipout = invoker.clipout = false; invoker.slavedown = false; if ( !invoker.slaveactive && (CountInv("Automag") > 1) ) { invoker.slavespin = invoker.slavereload = invoker.slaverefire = 0; A_Overlay(2,"LeftReady"); } } AUTS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOFIRE); AUS2 ABCD 1 A_WeaponReady(WRF_NOFIRE); Idle: AUTI A 0 A_Overlay(-9999,"Dummy"); AUTI AB 30; AUTI A 0 A_Jump(50,"Twiddle"); Goto Idle+1; LeftReady: 2UTS A 0 { A_StartSound("automag/select",CHAN_LEFTWEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); invoker.slaveactive = true; } 2UTS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_JumpIf(invoker.slavedown,"LeftDeselect"); 2US2 ABCD 1 A_JumpIf(invoker.slavedown,"LeftDeselect"); LeftIdle: 2UTI A 0 A_Overlay(-9998,"LeftDummy"); 2UTI AB 30; 2UTI A 0 A_Jump(50,"LeftTwiddle"); Goto LeftIdle+1; Twiddle: AUTT ABCDEFGHIJKLMNOPQRSTUVWXY 2; Goto Idle+1; LeftTwiddle: 2UTT ABCDEFGHIJKLMNOPQRSTUVWXY 2; Goto LeftIdle+1; Dummy: TNT1 A 1 { A_CheckReload(); int flags = (invoker.clipcount<=0)?WRF_NOFIRE:0; if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) player.SetPSprite(PSP_WEAPON,ResolveState("Reload")); else if ( ((invoker.clipcount < invoker.default.clipcount) || (invoker.slaveclipcount < invoker.default.slaveclipcount)) && (invoker.Ammo1.Amount > 0) ) A_WeaponReady(flags|WRF_ALLOWRELOAD|WRF_ALLOWZOOM); else A_WeaponReady(flags|WRF_ALLOWZOOM); if ( !invoker.slaveactive && (CountInv("Automag") > 1) ) { invoker.slavespin = invoker.slavereload = invoker.slaverefire = 0; A_Overlay(2,"LeftReady"); } } Wait; LeftDummy: TNT1 A 1 { if ( invoker.slavereload ) player.SetPSprite(2,ResolveState("LeftReload")); else if ( invoker.slavedown ) player.SetPSprite(2,ResolveState("LeftDeselect")); else if ( invoker.slavespin ) player.SetPSprite(2,ResolveState("LeftZoom")); else A_LeftWeaponReady(); } Wait; Fire: AUTF A 1 { A_Overlay(-9999,null); A_AutomagFire(); } AUTF BCD 1; AUTF LM 3; AUTF N 0 A_AutomagRefire(1); Goto Release; Hold: AUTF N 1; AUTF E 3 A_AutomagFire(); AUTF FG 3; AUTF N 0 A_AutomagRefire(1); Goto Release; AUTF N 1; AUTF H 3 A_AutomagFire(); AUTF IJ 3; AUTF N 0 A_AutomagRefire(1); Goto Release; AUTF N 1; AUTF K 3 A_AutomagFire(); AUTF LM 3; AUTF N 0 A_AutomagRefire("Hold"); Goto Release; Release: AUTF N 1; AUTF OPQRST 1; AUTI A 0; Goto Idle; LeftFire: 2UTI A 5 A_Overlay(-9998,null); 2UTI A 0 A_AutomagRefire(1,true); Goto LeftIdle; 2UTF A 1 A_AutomagFire(false,true); 2UTF BCD 1; 2UTF LM 3; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; LeftHold: 2UTF N 1; 2UTF E 3 A_AutomagFire(false,true); 2UTF FG 3; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; 2UTF N 1; 2UTF H 3 A_AutomagFire(false,true); 2UTF IJ 3; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; 2UTF N 1; 2UTF K 3 A_AutomagFire(false,true); 2UTF LM 3; 2UTF N 0 A_AutomagRefire("LeftHold",true); Goto LeftRelease; LeftRelease: 2UTF N 1; 2UTF OPQRST 1; 2UTI A 0; Goto LeftIdle; AltFire: AUTA A 0 { invoker.altaccuracy = 0.08; A_Overlay(-9999,null); } AUTA ABCDEFGHIJ 1; AltHold: AUTA K 2 A_AutomagFire(true); AUTA LM 2; AUTA T 0 A_AutomagRefire(1); Goto AltRelease; AUTA T 2; AUTA N 2 A_AutomagFire(true); AUTA OP 2; AUTA T 0 A_AutomagRefire(1); Goto AltRelease; AUTA T 2; AUTA Q 2 A_AutomagFire(true); AUTA RS 2; AUTA T 0 A_AutomagRefire(1); Goto AltRelease; AUTA T 2; Goto AltHold; AltRelease: AUTA TUVWXYZ 1; AUA2 ABCD 1; Goto Idle; LeftAltFire: 2UTI A 5 A_Overlay(-9998,null); 2UTI A 0 A_AutomagRefire(1,true); LeftAltHold: 2UTA ABCDEFGHIJ 1; 2UTA K 2 A_AutomagFire(true,true); 2UTA LM 2; 2UTA T 0 A_AutomagRefire(1,true); Goto LeftAltRelease; 2UTA T 2; 2UTA N 2 A_AutomagFire(true,true); 2UTA OP 2; 2UTA T 0 A_AutomagRefire(1,true); Goto LeftAltRelease; 2UTA T 2; 2UTA Q 2 A_AutomagFire(true,true); 2UTA RS 2; 2UTA T 0 A_AutomagRefire(1,true); Goto LeftAltRelease; 2UTA T 2; Goto LeftAltHold+10; LeftAltRelease: 2UTA TUVWXYZ 1; 2UA2 ABCD 1; Goto LeftIdle; Reload: AUTR A 0 { if ( invoker.clipcount >= invoker.default.clipcount ) { invoker.slavereload = (invoker.slaveactive&&(invoker.slaveclipcount0)); return ResolveState("Idle"); } return ResolveState(null); } AUTR A 0 { invoker.clipout = true; A_Overlay(-9999,null); A_StartSound("automag/click",CHAN_WEAPONMISC,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); } AUTR ABCDEFGHIJKLMNOPQRSTUVWXY 1; AUTD ABCD 1; AUTD E 30 { invoker.clipout = false; let aadd = min(invoker.Ammo1.Amount,invoker.default.clipcount-invoker.clipcount); invoker.clipcount += aadd; if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) invoker.Ammo1.Amount -= aadd; A_StartSound("automag/reload",CHAN_WEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); invoker.slavereload = (invoker.slaveactive&&(invoker.slaveclipcount0)); Vector3 x, y, z, origin; [x,y,z] = dt_CoordUtil.GetAxes(pitch,angle,roll); origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*4.+y*4.-z*8.); let c = Spawn("AutomagMag",origin); c.angle = angle; c.pitch = pitch; c.vel = vel*.5+x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-.5,.5)-z*FRandom[Junk](1,2); } AUTS A 0 A_StartSound("automag/select",CHAN_WEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); Goto Ready; LeftReload: 2UTR A 0 { if ( invoker.slaveclipcount >= invoker.default.slaveclipcount ) { invoker.slavereload = 0; return ResolveState("LeftIdle"); } invoker.slaveclipout = true; A_Overlay(-9998,null); A_StartSound("automag/click",CHAN_LEFTWEAPONMISC,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); return ResolveState(null); } 2UTR ABCDEFGHIJKLMNOPQRSTUVWXY 1; 2UTD ABCD 1; 2UTD E 30 { invoker.slaveclipout = false; let aadd = min(invoker.Ammo1.Amount,invoker.default.slaveclipcount-invoker.slaveclipcount); invoker.slaveclipcount += aadd; if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) invoker.Ammo1.Amount -= aadd; A_StartSound("automag/reload",CHAN_LEFTWEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); invoker.slavereload = false; Vector3 x, y, z, origin; [x,y,z] = dt_CoordUtil.GetAxes(pitch,angle,roll); origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*4.-y*4.-z*8.); let c = Spawn("AutomagMag",origin); c.angle = angle; c.pitch = pitch; c.vel = vel*.5+x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-.5,.5)-z*FRandom[Junk](1,2); } 2UTS A 0 A_StartSound("automag/select",CHAN_LEFTWEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); Goto LeftReady; Zoom: AUT2 A 1 { A_Overlay(-9999,null); invoker.slavespin = true; } AUT2 BCDEFGHIJKLMNOPQRSTUVWXYZ 1; AUT3 AB 1; ZoomLoop: AUT3 CDEFGH 1; AUT3 I 0 A_JumpIf(player.cmd.buttons&BT_ZOOM,"ZoomLoop1"); AUT3 IJKLMNOPQRS 1; Goto Idle; ZoomLoop1: AUT3 I 1; AUT3 C 0; Goto ZoomLoop; LeftZoom: 2UT2 A 1 { A_Overlay(-9998,null); invoker.slavespin = false; } 2UT2 BCDEFGHIJKLMNOPQRSTUVWXYZ 1; 2UT3 AB 1; LeftZoomLoop: 2UT3 CDEFGH 1; 2UT3 I 0 A_JumpIf(player.cmd.buttons&BT_ZOOM,"LeftZoomLoop1"); 2UT3 IJKLMNOPQRS 1; Goto LeftIdle; LeftZoomLoop1: 2UT3 I 1; 2UT3 C 0; Goto LeftZoomLoop; Deselect: AUTD A 0 { A_Overlay(-9999,null); invoker.slavedown = true; } AUTD ABCD 1; AUTD E 1 { if ( !player.FindPSprite(2) ) A_Lower(int.max); } Wait; LeftDeselect: 2UTD A 0 { A_Overlay(-9998,null); invoker.slaveactive = false; } 2UTD ABCD 1; 2UTD E 0; Stop; MuzzleFlash: AMZ1 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Automag](0,4); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; AltMuzzleFlash: AMZ2 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Automag](0,4); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; LeftMuzzleFlash: AMZ3 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Automag](0,4); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; LeftAltMuzzleFlash: AMZ4 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Automag](0,4); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; } }