Class BCasing : UTCasing { Default { BounceSound "automag/casing"; } } Class BCasing2 : UTCasing { Default { BounceSound "automag/casing"; } } Class Betamag : UnrealWeapon { bool SlaveActive, SlaveDown, SlaveAltFire, SlaveWhip, SlaveSpin; int SlaveRefire; double AltAccuracy; override bool TryPickup( in out Actor toucher ) { if ( !sting_proto ) return false; // not allowed return Super.TryPickup(toucher); } override void Tick() { Super.Tick(); if ( sting_protomags && (MaxAmount <= 1) ) MaxAmount = 2; else if ( !sting_protomags && (MaxAmount > 1) ) MaxAmount = 1; if ( Owner && !sting_protomags && (Amount > 1) ) { // 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")); } if ( sting_proto ) return; if ( !Owner ) { let r = Spawn("Pistol",pos,ALLOW_REPLACE); r.spawnangle = spawnangle; r.spawnpoint = spawnpoint; r.angle = angle; r.pitch = pitch; r.roll = roll; r.special = special; r.args[0] = args[0]; r.args[1] = args[1]; r.args[2] = args[2]; r.args[3] = args[3]; r.args[4] = args[4]; r.ChangeTid(tid); r.SpawnFlags = SpawnFlags&~MTF_SECRET; r.HandleSpawnFlags(); r.SpawnFlags = SpawnFlags; r.bCountSecret = SpawnFlags&MTF_SECRET; r.vel = vel; r.master = master; r.target = target; r.tracer = tracer; r.bDropped = bDropped; r.bNeverRespawn = bNeverRespawn; } else Owner.RemoveInventory(self); Destroy(); } override bool HandlePickup( Inventory item ) { if ( sting_protomags && (item.GetClass() == GetClass()) ) { let rslt = Super.HandlePickup(item); if ( rslt ) SetTag(StringTable.Localize("$T_PROTOMAG2")); return rslt; } return Super.HandlePickup(item); } override String PickupMessage() { if ( Owner ) return Super.PickupMessage(); return StringTable.Localize("$I_PROTOMAG2"); } override Inventory CreateTossable( int amt ) { Inventory inv = Super.CreateTossable(amt); if ( inv ) { SetTag(StringTable.Localize("$T_PROTOMAG")); inv.SetTag(StringTable.Localize("$T_PROTOMAG")); 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_BetamagRefire( statelabel flash = null, bool slave = false ) { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( invoker.altaccuracy < 0.06 ) invoker.altaccuracy += 0.01; if ( slave ) { if ( 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 ( weap.Ammo1.Amount <= 0 ) { A_ClearRefire(); return; } A_Refire(flash); } } action void A_LeftWeaponReady() { Weapon weap = Weapon(invoker); if ( !weap || !player ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( player.cmd.buttons&BT_ATTACK && !player.ReadyWeapon.bAltFire ) { invoker.slaverefire = 0; invoker.slavealtfire = false; player.setpsprite(2,ResolveState("LeftFire")); } else if ( player.cmd.buttons&BT_ALTATTACK && player.ReadyWeapon.bAltFire ) { invoker.slaverefire = 0; invoker.slavealtfire = true; player.setpsprite(2,ResolveState("LeftAltFire")); } } private action bool TryWhip( double angle, bool slave = false ) { FTranslatedLineTarget t; double slope = AimLineAttack(angle,DEFMELEERANGE,t,0.,ALF_CHECK3D); FLineTraceData d; LineTrace(angle,DEFMELEERANGE,slope,0,player.viewheight,data:d); if ( d.HitType != TRACE_HitNone ) { if ( d.HitType == TRACE_HitActor ) { int dmg = 25; dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); UTMainHandler.DoKnockback(d.HitActor,d.HitDir,35000); if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25); if ( !d.HitActor.bNOBLOOD ) { d.HitActor.TraceBleed(dmg,invoker); d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); } } else if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4); A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); A_StartSound("betamag/hit",slave?CHAN_LEFTWEAPONMISC:CHAN_WEAPONMISC,CHANF_OVERLAP); A_AlertMonsters(); return true; } return false; } action void A_BetamagWhip( bool slave = false ) { invoker.FireEffect(); for ( int i=0; i<16; i++ ) if ( TryWhip(angle+i*(45./16),slave) || TryWhip(angle-i*(45./16),slave) ) return; } action void A_BetamagFire( 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; invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(32,255,128,0),1); A_StartSound("betamag/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*2.5+ydir*y*1.5); else origin = level.Vec3Offset(origin,-z*1.5+ydir*y*2); double a = FRandom[Betamag](0,360), s = FRandom[Betamag](0,alt?invoker.altaccuracy:0.); if ( invoker.Amount > 1 ) s = s*2.+FRandom[Betamag](0,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); UTBulletTrail.DoTrail(self,origin,dir,10000,int((invoker.Amount+alt)**2)); if ( d.HitType == TRACE_HitActor ) { int dmg = 10; dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); double mm = 3000; if ( FRandom[Betamag](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,1.5*ydir,-3); else UTViewSmoke(s).ofs = (10,4*ydir,-1); s.target = self; s.alpha *= 0.5; } origin = level.Vec3Offset(origin,x*5+ydir*y*8-z*2); let c = Spawn(slave?"BCasing2":"BCasing",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_PROTOMAG2"); return StringTable.Localize("$O_PROTOMAG"); } override void Travelled() { Super.Travelled(); slaveactive = false; } override void DetachFromOwner() { Owner.A_StopSound(CHAN_ITEM); Owner.A_StopSound(CHAN_6); Owner.A_StopSound(CHAN_7); Super.DetachFromOwner(); } override void OwnerDied() { slavespin = slavewhip = slaverefire = 0; if ( !Owner.player || (Owner.player.ReadyWeapon != self) ) return; Owner.A_StopSound(CHAN_ITEM); Owner.A_StopSound(CHAN_6); Owner.A_StopSound(CHAN_7); let psp = Owner.player.FindPSprite(2); if ( psp ) { slaveactive = false; Owner.player.SetPSprite(2,ResolveState("LeftDeselect")); } Super.OwnerDied(); } override void DoEffect() { Super.DoEffect(); if ( Ammo1.Amount <= 0 ) SelectionOrder = 6800; else if ( Amount > 1 ) SelectionOrder = 2200; else SelectionOrder = default.SelectionOrder; } Default { Tag "$T_PROTOMAG"; Inventory.Icon "I_ClipAm"; Inventory.PickupMessage "$I_PROTOMAG"; Inventory.MaxAmount 1; Inventory.InterHubAmount 2; Weapon.UpSound "betamag/select"; Weapon.SlotNumber 2; Weapon.SelectionOrder 3000; Weapon.SlotPriority 0.9; Weapon.AmmoType "UMiniAmmo"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UMiniAmmo"; Weapon.AmmoUse2 1; Weapon.AmmoGive 20; Weapon.Kickback 320; UTWeapon.DropAmmo 10; +WEAPON.AMMO_OPTIONAL; +WEAPON.ALT_AMMO_OPTIONAL; } 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("Betamag") > 1) ) { invoker.slavespin = invoker.slavewhip = invoker.slaverefire = 0; player.setpsprite(2,ResolveState("LeftReady")); } } AUTS ABCDEFGHIJK 2 A_WeaponReady(WRF_NOFIRE); Idle: AUTI A 0 A_Overlay(-9999,"Dummy"); AUTI ABCDEF 20; AUTI A 0 A_Jump(50,"Twiddle"); Goto Idle+1; LeftReady: 2UTS A 0 { A_StartSound("betamag/select",CHAN_LEFTWEAPON,CHANF_DEFAULT,!Dampener.Active(self)?1.:.1); invoker.slaveactive = true; } 2UTS ABCDEFGHIJK 2 A_JumpIf(invoker.slavedown,"LeftDeselect"); LeftIdle: 2UTI A 0 A_Overlay(-9998,"LeftDummy"); 2UTI ABCDEF 20; 2UTI A 0 A_Jump(50,"LeftTwiddle"); Goto LeftIdle+1; Twiddle: AUTT A 0 { invoker.special1 = Random[Betamag](2,3); } AUTT ABCDEFGHIJKLMNOPQ 3 A_SetTics(invoker.special1); Goto Idle+1; LeftTwiddle: 2UTT A 0 { invoker.special2 = Random[Betamag](2,3); } 2UTT ABCDEFGHIJKLMNOPQ 3 A_SetTics(invoker.special2); Goto LeftIdle+1; Dummy: TNT1 A 1 { A_WeaponReady(WRF_ALLOWRELOAD|WRF_ALLOWZOOM); if ( !invoker.slaveactive && (CountInv("Betamag") > 1) ) { invoker.slavespin = invoker.slavewhip = invoker.slaverefire = 0; player.setpsprite(2,ResolveState("LeftReady")); } } Wait; LeftDummy: TNT1 A 1 { if ( invoker.slavedown ) player.setpsprite(2,ResolveState("LeftDeselect")); else if ( invoker.slavewhip ) player.setpsprite(2,ResolveState("LeftReload")); else if ( invoker.slavespin ) player.setpsprite(2,ResolveState("LeftZoom")); else A_LeftWeaponReady(); } Wait; Fire: AUTF A 1 { if ( invoker.Ammo1.Amount <= 0 ) return ResolveState("Reload"); A_BetamagFire(); A_Overlay(-9999,null); return ResolveState(null); } AUTF BCD 1; AUTF E 2; AUTF F 0 A_StartSound("betamag/slide",CHAN_WEAPONMISC,CHANF_OVERLAP,!Dampener.Active(self)?.3:.03); AUTF FGHI 2; AUTF J 0 A_BetamagRefire("Hold"); AUTF J 2; AUTI A 0; Goto Idle; Hold: AUTF C 2 A_BetamagFire(); AUTF D 2; Goto Fire+4; LeftFire: #### # 9 A_Overlay(-9998,null); 2UTI A 0 A_BetamagRefire(1,true); Goto LeftIdle; 2UTF A 1 A_BetamagFire(false,true); 2UTF BCD 1; 2UTF E 2; 2UTF F 0 A_StartSound("betamag/slide",CHAN_LEFTWEAPONMISC,CHANF_OVERLAP,!Dampener.Active(self)?.3:.03); 2UTF FGHI 2; 2UTF J 0 A_BetamagRefire("LeftHold",true); 2UTF J 2; 2UTI A 0; Goto LeftIdle; LeftHold: 2UTF C 2 A_BetamagFire(false,true); 2UTF D 2; Goto LeftFire+6; AltFire: AUTA A 0 { if ( invoker.Ammo1.Amount <= 0 ) return ResolveState("Reload"); invoker.altaccuracy = 0.08; A_Overlay(-9999,null); return ResolveState(null); } AUTA ABCDEFG 2; AltHold: AUTA H 1 A_BetamagFire(true); AUTA I 1; AUTA J 0 A_StartSound("betamag/slide",CHAN_WEAPONMISC,CHANF_OVERLAP,!Dampener.Active(self)?.3:.03); AUTA JKLM 1; AUTA N 0 A_BetamagRefire("AltHold"); AUTA NOPQ 2; AUTI A 2; Goto Idle; LeftAltFire: #### # 4 A_Overlay(-9998,null); 2UTI A 0 A_BetamagRefire(1,true); Goto LeftIdle; 2UTA ABCDEFG 2; LeftAltHold: 2UTA H 1 A_BetamagFire(true,true); 2UTA I 1; 2UTA J 0 A_StartSound("betamag/slide",CHAN_LEFTWEAPONMISC,CHANF_OVERLAP,!Dampener.Active(self)?.3:.03); 2UTA JKLM 1; 2UTA N 0 A_BetamagRefire("LeftAltHold",true); 2UTA NOPQ 2; 2UTI A 2; Goto LeftIdle; Reload: AUTW A 0 { A_Overlay(-9999,null); invoker.slavewhip = true; } AUTW ABCDE 2; AUTW F 0 { if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); A_StartSound("betamag/whip",CHAN_WEAPON,CHANF_OVERLAP); } AUTW FGHIJ 2; AUTW KLM 1; AUTW N 0 A_BetamagWhip(); AUTW NOPQR 1; AUTW STUVWXYZ[\ 2; Goto Idle; LeftReload: #### # 25 { invoker.slavewhip = false; A_Overlay(-9998,null); } LeftReloadHold: 2UTI A 0 { if ( !((invoker.Ammo1.Amount<=0) && (player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK))) && !(player.cmd.buttons&BT_RELOAD) ) return ResolveState("LeftIdle"); invoker.slavewhip = false; A_Overlay(-9998,null); return ResolveState(null); } 2UTW ABCDE 2; 2UTW F 0 { if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); A_StartSound("betamag/whip",CHAN_LEFTWEAPON,CHANF_OVERLAP); } 2UTW FGHIJ 2; 2UTW KLM 1; 2UTW N 0 A_BetamagWhip(true); 2UTW NOPQR 1; 2UTW STUVWXYZ[\ 2; 2UTI A 0 A_JumpIf((invoker.Ammo1.Amount<=0)&&(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK))||(player.cmd.buttons&BT_RELOAD),"LeftReloadHold"); Goto LeftIdle; Zoom: AUTR A 1 { A_Overlay(-9999,null); invoker.slavespin = true; } AUTR BCDEFGHIJKLMN 1; ZoomLoop: AUTR OPQRSTUVWX 1; AUTR Y 0 A_JumpIf(player.cmd.buttons&BT_ZOOM,"ZoomLoop"); AUTR YZ[\] 1; AUR2 ABCDEFGHIJK 1; Goto Idle; LeftZoom: 2UTR A 1 { A_Overlay(-9998,null); invoker.slavespin = false; } 2UTR BCDEFGHIJKLMN 1; LeftZoomLoop: 2UTR OPQRSTUVWX 1; 2UTR Y 0 A_JumpIf(player.cmd.buttons&BT_ZOOM,"LeftZoomLoop"); 2UTR YZ[\] 1; 2UR2 ABCDEFGHIJK 1; Goto LeftIdle; Deselect: AUTD A 0 { A_Overlay(-9999,null); invoker.slavedown = true; } AUTD ABCDEFG 1; AUTD G 1 { if ( !player.FindPSprite(2) ) A_Lower(int.max); } Wait; LeftDeselect: 2UTD A 0 { A_Overlay(-9998,null); invoker.slaveactive = false; } 2UTD ABCDEFG 1; Stop; MuzzleFlash: AMZ1 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Betamag](0,3); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; AltMuzzleFlash: AMZ2 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Betamag](0,3); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; LeftMuzzleFlash: AMZ3 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Betamag](0,3); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; LeftAltMuzzleFlash: AMZ4 # 2 Bright { let psp = player.FindPSprite(OverlayID()); psp.frame = Random[Betamag](0,3); let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; } }