Class BCasing : UTCasing { } Class BCasing2 : UTCasing { } Class Betamag : UnrealWeapon { bool SlaveActive, SlaveDown, SlaveAltFire, SlaveWhip, SlaveSpin; int SlaveRefire; double AltAccuracy; override bool HandlePickup( Inventory item ) { if ( sting_protomags && (item.GetClass() == GetClass()) ) { SetTag(StringTable.Localize("$T_PROTOMAG2")); return Super.HandlePickup(item); } 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.1 ) invoker.altaccuracy += 0.015; 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 ) { 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 = 15; 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_PlaySound("betamag/hit",CHAN_WEAPON); A_AlertMonsters(); return true; } return false; } action void A_BetamagWhip() { invoker.FireEffect(); for ( int i=0; i<16; i++ ) if ( TryWhip(angle+i*(45./16)) || TryWhip(angle-i*(45./16)) ) 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_PlaySound("betamag/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[Betamag](0.5,0.2),FRandom[Betamag](-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[Betamag](-0.2,-0.5),FRandom[Betamag](-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 = 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.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 = 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,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.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; } 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) ) 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_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_PlaySound("betamag/select",CHAN_6,!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 2 { A_Overlay(-9999,null); return A_JumpIfNoAmmo("Reload"); } AUTF B 2; Hold: AUTF C 2 A_BetamagFire(); AUTF DE 2; AUTF F 0 A_PlaySound("betamag/slide",CHAN_ITEM,!Dampener.Active(self)?.3:.03); AUTF FGHI 2; AUTF J 0 A_BetamagRefire("Hold"); AUTF J 2; AUTI A 0; Goto Idle; LeftFire: #### # 10 A_Overlay(-9998,null); 2UTI A 0 A_BetamagRefire(1,true); Goto LeftIdle; 2UTF AB 1; LeftHold: 2UTF C 2 A_BetamagFire(false,true); 2UTF DE 2; 2UTF F 0 A_PlaySound("betamag/slide",CHAN_7,!Dampener.Active(self)?.3:.03); 2UTF FGHI 2; 2UTF J 0 A_BetamagRefire("LeftHold",true); 2UTF J 2; 2UTI A 0; Goto LeftIdle; AltFire: AUTA A 0 { invoker.altaccuracy = 0.08; A_Overlay(-9999,null); return A_JumpIfNoAmmo("Reload"); } AUTA ABCDEFG 2; AltHold: AUTA H 2 A_BetamagFire(true); AUTA I 2; AUTA J 0 A_PlaySound("betamag/slide",CHAN_ITEM,!Dampener.Active(self)?.3:.03); AUTA JKLM 2; AUTA N 0 A_BetamagRefire("AltHold"); AltRelease: AUTA NOPQ 2; AUTI A 2; Goto Idle; LeftAltFire: #### # 6 A_Overlay(-9998,null); 2UTI A 0 A_BetamagRefire(1,true); Goto LeftIdle; 2UTA ABCDEFG 2; LeftAltHold: 2UTA H 2 A_BetamagFire(true,true); 2UTA I 2; 2UTA J 0 A_PlaySound("betamag/slide",CHAN_7,!Dampener.Active(self)?.3:.03); 2UTA JKLM 2; 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; UTMainHandler.DoSwing(self,(FRandom[Betamag](-0.5,-0.4),FRandom[Betamag](0.2,0.3)),4,0,8,SWING_Spring,5); } AUTW ABCDE 2; AUTW F 0 { if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); A_PlaySound("betamag/whip",CHAN_ITEM); UTMainHandler.DoSwing(self,(FRandom[Betamag](-0.3,-0.2),FRandom[Betamag](-0.8,-0.5)),2,0,8,SWING_Spring,2,0.5); } AUTW FGHIJ 2; AUTW K 0 { UTMainHandler.DoSwing(self,(FRandom[Betamag](0.2,0.3),FRandom[Betamag](0.8,0.5)),3,0.5,6,SWING_Spring,3,3); } 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); UTMainHandler.DoSwing(self,(FRandom[Betamag](0.4,0.5),FRandom[Betamag](0.2,0.3)),4,0,8,SWING_Spring,5); return ResolveState(null); } 2UTW ABCDE 2; 2UTW F 0 { if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); A_PlaySound("betamag/whip",CHAN_7); UTMainHandler.DoSwing(self,(FRandom[Betamag](0.2,0.3),FRandom[Betamag](-0.8,-0.5)),2,0,8,SWING_Spring,2,0.5); } 2UTW FGHIJ 2; 2UTW K 0 { UTMainHandler.DoSwing(self,(FRandom[Betamag](-0.3,-0.2),FRandom[Betamag](0.8,0.5)),3,0.5,6,SWING_Spring,3,3); } 2UTW KLM 1; 2UTW N 0 A_BetamagWhip(); 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; } }