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; property ClipCount : ClipCount; override int, int, bool, bool GetClipAmount() { return ClipCount, -1, (ClipCount<2), false; } action void ProcessTraceHit( Linetracer t ) { if ( t.Results.HitType == TRACE_HitActor ) { int dmg = Random[Quadshot](4,8); 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 ( invoker.clipcount <= 0 ) return; if ( bAlt && (invoker.clipcount <= 1) ) { // 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); double blend = invoker.clipcount/4.; A_PlaySound("quadshot/alt",CHAN_WEAPON,(!Dampener.Active(self)?1.:.2)*blend); A_PlaySound("quadshot/fire",CHAN_7,(!Dampener.Active(self)?1.:.2)*(1.-blend)); double spread = invoker.clipcount; for ( int i=0; i 0 ) { player.SetPSprite(PSP_WEAPON,invoker.FindState("Reload")); return true; } else if ( bDryFire ) { player.SetPSprite(PSP_WEAPON,invoker.FindState("DryFire")); return true; } } return false; } override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) { if ( ClipCount > 0 ) return true; return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); } Default { Tag "$T_QUADSHOT"; Inventory.PickupMessage "$I_QUADSHOT"; Weapon.UpSound "quadshot/select"; Weapon.SlotNumber 3; Weapon.SelectionOrder 2; Weapon.SlotPriority 0.9; Weapon.AmmoType "UShells"; Weapon.AmmoUse 1; Weapon.AmmoType2 "UShells"; Weapon.AmmoUse2 1; Weapon.AmmoGive 20; Weapon.Kickback 320; UTWeapon.DropAmmo 10; QuadShot.ClipCount 4; } 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() ) { let weap = Weapon(invoker); if ( invoker.clipcount < min(weap.Ammo1.Amount,invoker.default.clipcount) ) A_WeaponReady(WRF_ALLOWRELOAD); else 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(); let weap = Weapon(invoker); invoker.special1 = min(weap.Ammo1.Amount,invoker.default.clipcount); invoker.special2 = invoker.special1-invoker.clipcount; invoker.clipcount = -1; } QUAR VWXYZ[\] 1; QUR2 ABCDEFGHIJKLMNOPQR 1; QUR2 S 0 { A_PlaySound("quadshot/load",CHAN_6,Dampener.Active(self)?.1:1.); let weap = Weapon(invoker); invoker.clipcount = 0; if ( invoker.special1 > 0 ) { weap.Ammo1.Amount -= min(2,invoker.special2); invoker.clipcount += min(2,invoker.special1); invoker.special1 = max(0,invoker.special1-2); invoker.special2 = max(0,invoker.special2-2); } 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.); let weap = Weapon(invoker); if ( invoker.special1 > 0 ) { weap.Ammo1.Amount -= min(2,invoker.special2); invoker.clipcount += min(2,invoker.special1); invoker.special1 = max(0,invoker.special1-2); invoker.special2 = max(0,invoker.special2-2); } 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; 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; } }