Addition to HUD for showing clip counts (kinda cheap-looking, but still better than what Oldskool did). Charge for some weapons and loaded rockets for eightball also use the clipcount display, like in Doom Tournament. Small fixups for dual wielding logic.
432 lines
11 KiB
Text
432 lines
11 KiB
Text
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<invoker.clipcount; 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));
|
|
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 = 0;
|
|
}
|
|
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);
|
|
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));
|
|
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<numpt; i++ )
|
|
{
|
|
let s = Spawn("UTStaticViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (10,2,-2);
|
|
UTViewSmoke(s).vvel += (FRandom[Quadshot](-0.05,0.25),FRandom[Quadshot](-0.3,0.3),FRandom[Sniper](-0.3,0.3));
|
|
s.target = self;
|
|
s.scale *= 1.2;
|
|
s.alpha *= 0.4;
|
|
}
|
|
}
|
|
action void A_DropShells()
|
|
{
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),-x*4-y*8-z*8);
|
|
for ( int i=0; i<(4-invoker.clipcount); i++ )
|
|
{
|
|
let c = Spawn("QCasing",origin);
|
|
c.vel = x*FRandom[Junk](-1.5,0.25)-y*FRandom[Junk](1,4)-z*FRandom[Junk](1,4);
|
|
}
|
|
}
|
|
action bool A_QuadshotCheckForReload( bool bDryFire = false )
|
|
{
|
|
let weap = Weapon(invoker);
|
|
if ( invoker.clipcount <= 0 )
|
|
{
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
}
|