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 { } Class Automag : UnrealWeapon { int ClipCount, SlaveClipCount; bool SlaveActive, SlaveDown, SlaveReload, SlaveAltFire, SlaveSpin; int SlaveRefire; double AltAccuracy; property ClipCount : ClipCount; property SlaveClipCount : SlaveClipCount; override bool HandlePickup( Inventory item ) { if ( sting_automags && (item.GetClass() == GetClass()) ) { SetTag(StringTable.Localize("$T_AUTOMAG2")); return Super.HandlePickup(item); } return Super.HandlePickup(item); } 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_PlaySound("automag/click",CHAN_ITEM,!Dampener.Active(self)?1.:.35); if ( (invoker.slaveclipcount <= 0) || (weap.Ammo1.Amount <= 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 ( player.ReadyWeapon.CheckAmmo(Weapon.PrimaryFire,true) ) player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftHold")); } else if ( (player.cmd.buttons&BT_ALTATTACK) && invoker.slavealtfire && !pending && (player.health > 0) ) { invoker.slaverefire++; if ( player.ReadyWeapon.CheckAmmo(Weapon.AltFire,true) ) player.setpsprite(2,flash?ResolveState(flash):ResolveState("LeftAltHold")); } else { invoker.slaverefire = 0; player.ReadyWeapon.CheckAmmo(invoker.slavealtfire?Weapon.AltFire:Weapon.PrimaryFire,true); } } else { if ( invoker.clipcount < 5 ) A_PlaySound("automag/click",CHAN_ITEM,!Dampener.Active(self)?1.:.35); if ( (invoker.clipcount <= 0) || (weap.Ammo1.Amount <= 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) && (weap.Ammo1.Amount > 0) ) { 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) && (weap.Ammo1.Amount > 0) ) { 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 ( weap.Ammo1.Amount <= 0 ) return; if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return; if ( slave ) invoker.slaveclipcount--; else invoker.clipcount--; invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(32,255,128,0),1); A_PlaySound("automag/fire",slave?CHAN_6:CHAN_WEAPON,!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); UTMainHandler.DoSwing(self,(FRandom[Automag](0.5,0.2),FRandom[Automag](-0.3,0.2)),2,0,1,SWING_Spring,0,2); } 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); UTMainHandler.DoSwing(self,(FRandom[Automag](-0.2,-0.5),FRandom[Automag](-0.3,0.2)),2,0,1,SWING_Spring,0,2); } Vector3 x, y, z, x2, y2, z2; [x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll); Vector3 origin = Vec2OffsetZ(0,0,player.viewz)+10.0*x; int ydir = slave?-1:1; if ( alt ) origin = origin-z*3.0+ydir*y*1.0; else origin = origin-z*1.0+ydir*y*4.0; double a = FRandom[Automag](0,360), s = FRandom[Automag](0,alt?invoker.altaccuracy:0.01); [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); 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 += x*8.0+ydir*y*6.0-z*2.0; let c = Spawn("UCasing",origin); 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(); slaverefire = 0; } 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; } 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 2; Weapon.AmmoType "UMiniAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UMiniAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 30; Weapon.Kickback 180; UTWeapon.DropAmmo 10; Automag.ClipCount 20; Automag.SlaveClipCount 20; } States { Spawn: AUTP A -1; Stop; AUTP B -1; Stop; Select: AUTS A 1 A_Raise(int.max); Ready: AUTS A 0 { invoker.slavedown = false; if ( !invoker.slaveactive && (CountInv("Automag") > 1) ) 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_PlaySound("automag/select",CHAN_6,!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 { if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) A_Overlay(PSP_WEAPON,"Reload"); else if ( ((invoker.clipcount < min(20,invoker.Ammo1.Amount)) || (invoker.slaveclipcount < min(20,invoker.Ammo1.Amount))) ) A_WeaponReady(WRF_ALLOWRELOAD|WRF_ALLOWZOOM); else A_WeaponReady(WRF_ALLOWZOOM); if ( !invoker.slaveactive && (CountInv("Automag") > 1) ) A_Overlay(2,"LeftReady"); } Wait; LeftDummy: TNT1 A 1 { if ( health <= 0 ) { invoker.slaveactive = false; A_Overlay(2,"LeftDeselect"); } else if ( invoker.slavereload ) A_Overlay(2,"LeftReload"); else if ( invoker.slavedown ) A_Overlay(2,"LeftDeselect"); else if ( invoker.slavespin ) A_Overlay(2,"LeftZoom"); else A_LeftWeaponReady(); } Wait; Fire: AUTF A 1 A_Overlay(-9999,null); AUTF BCD 1; Hold: AUTF E 2 A_AutomagFire(); AUTF FG 2; AUTF N 0 A_AutomagRefire(1); Goto Release; AUTF NN 2; AUTF H 2 A_AutomagFire(); AUTF IJ 2; AUTF N 0 A_AutomagRefire(1); Goto Release; AUTF NN 2; AUTF K 2 A_AutomagFire(); AUTF LM 2; AUTF N 0 A_AutomagRefire(1); Goto Release; AUTF NN 2; Goto Hold; Release: AUTF NN 2; 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; LeftHold: 2UTF ABCD 1; 2UTF E 2 A_AutomagFire(false,true); 2UTF FG 2; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; 2UTF NN 2; 2UTF H 2 A_AutomagFire(false,true); 2UTF IJ 2; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; 2UTF NN 2; 2UTF K 2 A_AutomagFire(false,true); 2UTF LM 2; 2UTF N 0 A_AutomagRefire(1,true); Goto LeftRelease; 2UTF NN 2; Goto LeftHold+4; LeftRelease: 2UTF NN 2; 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 1; AUTA N 2 A_AutomagFire(true); AUTA OP 2; AUTA T 0 A_AutomagRefire(1); Goto AltRelease; AUTA T 1; AUTA Q 2 A_AutomagFire(true); AUTA RS 2; AUTA T 0 A_AutomagRefire(1); Goto AltRelease; AUTA T 1; 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 1; 2UTA N 2 A_AutomagFire(true,true); 2UTA OP 2; 2UTA T 0 A_AutomagRefire(1,true); Goto LeftAltRelease; 2UTA T 1; 2UTA Q 2 A_AutomagFire(true,true); 2UTA RS 2; 2UTA T 0 A_AutomagRefire(1,true); Goto LeftAltRelease; 2UTA T 1; Goto LeftAltHold+10; LeftAltRelease: 2UTA TUVWXYZ 1; 2UA2 ABCD 1; Goto LeftIdle; Reload: AUTR A 0 { invoker.slavereload = ((player.cmd.buttons&BT_RELOAD)&&invoker.slaveactive&&(invoker.slaveclipcount < min(20,invoker.Ammo1.Amount)))||(invoker.slaveclipcount <= 0); return A_JumpIf(invoker.clipcount>=min(20,invoker.Ammo1.Amount),"Idle"); } AUTR A 0 { invoker.clipcount = Min(20,invoker.Ammo1.Amount); A_Overlay(-9999,null); A_WeaponOffset(0,32); // fix sudden psprite lowering A_PlaySound("automag/click",CHAN_WEAPON,!Dampener.Active(self)?1.:.1); } AUTR ABCDEFGHIJKLMNOPQRSTUVWXY 1; AUTD ABCD 1; AUTD E 30 { A_PlaySound("automag/reload",CHAN_WEAPON,!Dampener.Active(self)?1.:.1); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); } AUTS A 0 A_PlaySound("automag/select",CHAN_WEAPON,!Dampener.Active(self)?1.:.1); Goto Ready; LeftReload: 2UTR A 0 { invoker.slaveclipcount = Min(20,invoker.Ammo1.Amount); invoker.slavereload = false; A_Overlay(-9998,null); A_PlaySound("automag/click",CHAN_6,!Dampener.Active(self)?1.:.1); } 2UTR ABCDEFGHIJKLMNOPQRSTUVWXY 1; 2UTD ABCD 1; 2UTD E 30 { A_PlaySound("automag/reload",CHAN_6,!Dampener.Active(self)?1.:.1); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); } 2UTS A 0 A_PlaySound("automag/select",CHAN_6,!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: AUTI A 1 { invoker.slavedown = true; } AUTD A 0 A_Overlay(-9999,null); AUTD A 0 A_JumpIf(invoker.slaveactive,"Deselect"); AUTD ABCD 1; AUTD E 1 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; } }