Class UShells : Ammo { Default { Tag "$T_SHELLS"; Inventory.Icon "I_ShotSh"; Inventory.PickupMessage ""; Inventory.Amount 12; Inventory.MaxAmount 48; Ammo.BackpackAmount 12; Ammo.BackpackMaxAmount 96; Ammo.DropAmount 6; } override String PickupMessage() { if ( PickupMsg.Length() > 0 ) return Super.PickupMessage(); return String.Format("%s%d%s",StringTable.Localize("$I_SHELLSL"),Amount,StringTable.Localize("$I_SHELLSR")); } States { Spawn: QAMO A -1; Stop; } } Class UShells2 : UShells { Default { Tag "$T_SHELLS2"; Inventory.Amount 4; Ammo.DropAmount 4; +INVENTORY.IGNORESKILL; } } Class QCasing : UCasing { Default { BounceSound "quadshot/shell"; } override void PostBeginPlay() { Super.PostBeginPlay(); heat = 0.; // no smoke } } Class QuadshotTracer : LineTracer { Actor ignoreme; override ETraceStatus TraceCallback() { if ( Results.HitType == TRACE_HitActor ) { if ( Results.HitActor == ignoreme ) return TRACE_Skip; if ( Results.HitActor.bSHOOTABLE ) { int amt = FlakAccumulator.GetAmount(Results.HitActor); // getgibhealth isn't clearscope, fuck int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor); if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth); // go through actors that are already gibbed if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip; return TRACE_Stop; } return TRACE_Skip; } else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) { if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) return TRACE_Stop; return TRACE_Skip; } return TRACE_Stop; } } Class QuadShot : UnrealWeapon { int clipcount; QuadshotTracer t; action void ProcessTraceHit( Linetracer t ) { if ( t.Results.HitType == TRACE_HitActor ) { int dmg = Random[Quadshot](8,12); FlakAccumulator.Accumulate(t.Results.HitActor,dmg,invoker,self,'shot'); double mm = 2400; UTMainHandler.DoKnockback(t.Results.HitActor,t.Results.HitVector+(0,0,0.025),mm*FRandom[Quadshot](0.4,1.2)); if ( t.Results.HitActor.bNOBLOOD ) { let p = Spawn("BulletImpact",t.Results.HitPos); p.scale *= FRandom[Quadshot](0.2,0.4); p.angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x)+180; p.pitch = asin(t.Results.HitVector.z); } else { t.Results.HitActor.TraceBleed(dmg,self); t.Results.HitActor.SpawnBlood(t.Results.HitPos,atan2(t.Results.HitVector.y,t.Results.HitVector.x)+180,dmg); } } else if ( t.Results.HitType != TRACE_HitNone ) { Vector3 hitnormal = -t.Results.HitVector; if ( t.Results.HitType == TRACE_HitFloor ) { if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.top.Normal; else hitnormal = t.Results.HitSector.floorplane.Normal; } else if ( t.Results.HitType == TRACE_HitCeiling ) { if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.bottom.Normal; else hitnormal = t.Results.HitSector.ceilingplane.Normal; } else if ( t.Results.HitType == TRACE_HitWall ) { hitnormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit(); if ( !t.Results.Side ) hitnormal *= -1; } let p = Spawn("BulletImpact",t.Results.HitPos+hitnormal*0.01); p.scale *= FRandom[Quadshot](0.2,0.4); p.angle = atan2(hitnormal.y,hitnormal.x); p.pitch = asin(-hitnormal.z); if ( t.Results.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos); } } action void A_QuadshotFire( bool bAlt = false ) { Weapon weap = Weapon(invoker); if ( !weap ) return; if ( weap.Ammo1.Amount <= 0 ) return; if ( bAlt && (invoker.clipcount > 2) ) { // fall back to normal fire strength player.SetPSprite(PSP_WEAPON,invoker.FindState("Fire")); return; } 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*2-z*2); [x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll); double a, s; Vector3 dir; FLineTraceData d; if ( bAlt ) { A_QuakeEx(1,1,1,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); A_PlaySound("quadshot/alt",CHAN_WEAPON,!Dampener.Active(self)?1.:.2); A_PlaySound("quadshot/alt",CHAN_7,!Dampener.Active(self)?1.:.2); double spread = (4-invoker.clipcount); for ( int i=invoker.clipcount; i<4; i++ ) { for ( int i=0; i<3; i++ ) UTMainHandler.DoSwing(self,(FRandom[Quadshot](-0.04,-0.2),FRandom[Quadshot](-0.2,0.2)),FRandom[Quadshot](2,4),FRandom[Quadshot](-0.2,0.5),Random[Quadshot](3,4),SWING_Spring,Random[Quadshot](1,6),FRandom[Quadshot](0.8,1.4)); weap.DepleteAmmo(weap.bAltFire,true,1); for ( int i=0; i<10; i++ ) { a = FRandom[Quadshot](0,360); s = FRandom[Quadshot](0,0.09+0.05*spread); dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); if ( !invoker.t ) invoker.t = new("QuadshotTracer"); invoker.t.ignoreme = self; invoker.t.Trace(origin,CurSector,dir,10000,0); ProcessTraceHit(invoker.t); } } vel += (0,0,(0.3+0.3*spread))-x*(1.5+1.1*spread); invoker.clipcount = 4; } else { A_QuakeEx(1,1,1,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12); A_PlaySound("quadshot/fire",CHAN_WEAPON,!Dampener.Active(self)?1.:.2); A_PlaySound("quadshot/fire",CHAN_7,!Dampener.Active(self)?1.:.2); for ( int i=0; i<3; i++ ) UTMainHandler.DoSwing(self,(FRandom[Quadshot](-0.04,-0.2),FRandom[Quadshot](-0.2,0.2)),FRandom[Quadshot](2,3),FRandom[Quadshot](-0.2,0.5),Random[Quadshot](2,3),SWING_Spring,Random[Quadshot](0,3),FRandom[Quadshot](0.8,1.4)); weap.DepleteAmmo(weap.bAltFire,true,1); for ( int i=0; i<10; i++ ) { a = FRandom[Quadshot](0,360); s = FRandom[Quadshot](0,0.08); dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); if ( !invoker.t ) invoker.t = new("QuadshotTracer"); invoker.t.ignoreme = self; invoker.t.Trace(origin,CurSector,dir,10000,0); ProcessTraceHit(invoker.t); } vel += (0,0,0.3)-x*1.5; invoker.clipcount++; } if ( bAlt ) A_Overlay(-2,"MuzzleFlashAlt"); else A_Overlay(-2,"MuzzleFlash"); A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderstyle(-2,STYLE_Add); if ( !Dampener.Active(self) ) A_AlertMonsters(); invoker.FireEffect(); UTMainHandler.DoFlash(self,Color(32,255,128,0),1); int numpt = bAlt?40:15; for ( int i=0; i 3 ) { if ( weap.Ammo1.Amount > 0 ) { player.SetPSprite(PSP_WEAPON,invoker.FindState("Reload")); return true; } else if ( bDryFire ) { player.SetPSprite(PSP_WEAPON,invoker.FindState("DryFire")); return true; } } return false; } Default { Tag "$T_QUADSHOT"; Inventory.PickupMessage "$I_QUADSHOT"; Weapon.UpSound "quadshot/select"; Weapon.SlotNumber 3; Weapon.SelectionOrder 2; Weapon.AmmoType "UShells"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UShells"; Weapon.AmmoUse2 1; Weapon.AmmoGive 20; Weapon.Kickback 320; UTWeapon.DropAmmo 10; } States { Spawn: QSPK A -1; Stop; QSPK B -1; Stop; Select: QUAS A 1 A_Raise(int.max); Wait; Ready: QUAS ABCDEFGHIJK 2 A_WeaponReady(WRF_NOFIRE); Goto Idle; Dummy: TNT1 A 1 { A_CheckReload(); if ( !A_QuadshotCheckForReload() ) A_WeaponReady(); } Wait; Idle: QUAI A 0 A_Overlay(-9999,"Dummy"); QUAI A 50; QUAI A 0 A_Jump(40,"Twiddle"); Goto Idle+1; Twiddle: #### # 2; QUAT ABCDEFGHIJKLMNOPQRSTU 2; Goto Idle+1; Fire: #### # 1 { if ( !A_QuadshotCheckForReload(true) ) { A_Overlay(-9999,"Null"); A_QuadshotFire(); } } QUAF ABCDEFGHIJK 1; Goto Pump; DryFire: #### # 1 { A_Overlay(-9999,"Null"); A_PlaySound("automag/click",CHAN_WEAPON,Dampener.Active(self)?.05:.5); } QUAF AJK 2; Goto Idle; AltFire: #### # 1 { if ( !A_QuadshotCheckForReload(true) ) { A_Overlay(-9999,"Null"); A_QuadshotFire(true); } } QUAA ABCDEFGHIJKLMNOPQRS 1; Goto Idle; Pump: QUAP A 0 { if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); } QUAP ABCD 1; QUAP E 0 A_PlaySound("quadshot/pump1",CHAN_6,Dampener.Active(self)?.1:1.); QUAP EFGHIJKLM 1; QUAP N 0 A_PlaySound("quadshot/pump2",CHAN_6,Dampener.Active(self)?.1:1.); QUAP NOPQRSTUVWXYZ[\ 1; QUAP \ 0; QUAI A 0; // force no tween Goto Idle; Reload: QUAR A 0 A_Overlay(-9999,"Null"); QUAR ABCDEFGHIJK 1; QUAR L 0 { A_PlaySound("quadshot/open",CHAN_6,Dampener.Active(self)?.1:1.); if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); } QUAR LMNOPQRSTU 1; QUAR V 0 A_DropShells(); QUAR VWXYZ[\] 1; QUR2 ABCDEFGHIJKLMNOPQR 1; QUR2 S 0 { A_PlaySound("quadshot/load",CHAN_6,Dampener.Active(self)?.1:1.); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); } QUR2 STUVWXYZ[\] 1; QUR3 ABCDEFGHIJKLMNO 1; QUR3 P 0 { A_PlaySound("quadshot/load",CHAN_6,Dampener.Active(self)?.1:1.); if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading(); } QUR3 PQRSTUVWXYZ[\] 1; QUR4 ABCDE 1; QUR4 F 0 { A_PlaySound("quadshot/close",CHAN_6,Dampener.Active(self)?.1:1.); if ( self is 'UTPlayer' ) UTPlayer(self).PlayAttacking3(); } QUR4 FGHIJKLMNOPQ 1; QUAI A 0 { let weap = Weapon(invoker); invoker.clipcount = max(0,4-weap.Ammo1.Amount); } Goto Idle; Deselect: #### # 1 A_Overlay(-9999,"Null"); QUAD ABCDEFG 1; QUAD G 1 A_Lower(int.max); Wait; MuzzleFlash: QFLA A 3 Bright { let l = Spawn("EnforcerLight",pos); l.target = self; } Stop; MuzzleFlashAlt: QFLA B 3 Bright { let l = Spawn("EnforcerLight",pos); l.target = self; l.args[3] += 20; } Stop; } }