Also, fix incorrect bounce behavior of candy beams and biospark arcs.
804 lines
22 KiB
Text
804 lines
22 KiB
Text
// Blackmann Arms "Wallbuster" Heavy Armor Perforator Shotgun (planned for unreleased Total Destruction UT mod as the "Armor Perforator")
|
|
// Slot 3, replaces Super Shotgun, Ethereal Crossbow, Frost Shards
|
|
|
|
Class Wallbuster : SWWMWeapon
|
|
{
|
|
bool loaded[25];
|
|
bool fired[25];
|
|
int rotation[6];
|
|
int whichspin;
|
|
transient bool cancelreload;
|
|
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
// push back selection order if weapon is unloaded
|
|
if ( !bInitialized )
|
|
{
|
|
SelectionOrder = default.SelectionOrder;
|
|
return;
|
|
}
|
|
for ( int i=0; i<25; i++ )
|
|
{
|
|
if ( !loaded[i] || fired[i] ) continue;
|
|
SelectionOrder = default.SelectionOrder;
|
|
return;
|
|
}
|
|
SelectionOrder = 1400;
|
|
}
|
|
|
|
override bool ReportHUDAmmo()
|
|
{
|
|
for ( int i=0; i<25; i++ ) if ( loaded[i] && !fired[i] ) return true;
|
|
return Super.ReportHUDAmmo();
|
|
}
|
|
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
|
|
{
|
|
if ( (firemode == PrimaryFire) || (firemode == AltFire) )
|
|
{
|
|
for ( int i=0; i<25; i++ ) if ( loaded[i] && !fired[i] ) return true;
|
|
return (Ammo1.Amount > 0);
|
|
}
|
|
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
|
|
}
|
|
override void InitializeWeapon()
|
|
{
|
|
// first wallbuster has five barrels loaded
|
|
for ( int i=0; i<25; i++ )
|
|
loaded[i] = (i<5);
|
|
for ( int i=0; i<25; i++ )
|
|
fired[i] = false;
|
|
}
|
|
action void A_CBTFlash( StateLabel flashlabel = null, int index = 0 )
|
|
{
|
|
if ( !player || !player.ReadyWeapon )
|
|
return;
|
|
Weapon weap = player.ReadyWeapon;
|
|
State flashstate = null;
|
|
if ( !flashlabel )
|
|
{
|
|
if ( weap.bAltFire )
|
|
flashstate = weap.FindState('AltFlash');
|
|
if ( !flashstate )
|
|
flashstate = weap.FindState('Flash');
|
|
}
|
|
else flashstate = weap.FindState(flashlabel);
|
|
player.SetPSprite(PSP_FLASH-index,flashstate+index);
|
|
A_OverlayFlags(PSP_FLASH-index,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
|
|
A_OverlayRenderStyle(PSP_FLASH-index,STYLE_Add);
|
|
}
|
|
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool bust = false )
|
|
{
|
|
// Wall busting
|
|
if ( bust || swwm_omnibust )
|
|
{
|
|
int bustdmg = dmg;
|
|
BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z);
|
|
}
|
|
foreach ( l:t.ShootThroughList )
|
|
{
|
|
l.Activate(self,0,SPAC_PCross);
|
|
l.Activate(self,0,SPAC_Impact);
|
|
}
|
|
foreach ( w:t.WaterHitList )
|
|
{
|
|
let b = Spawn("SmolInvisibleSplasher",w.hitpos);
|
|
b.target = self;
|
|
b.A_CheckTerrain();
|
|
}
|
|
for ( int i=5; i<t.Results.Distance; i+=10 )
|
|
{
|
|
if ( !Random[Boolet](0,bc) ) continue;
|
|
let b = Actor.Spawn("SWWMBubble",level.Vec3Offset(origin,dir*i));
|
|
b.Scale *= FRandom[Boolet](.1,.3);
|
|
}
|
|
foreach( hit:t.HitList )
|
|
{
|
|
int realdmg = dmg?dmg:hit.HitDamage;
|
|
let p = SWWMPuff.Setup(hit.HitLocation,hit.x,invoker,self,hit.HitActor);
|
|
SWWMDamageAccumulator.Accumulate(hit.HitActor,realdmg,p,self,'shot',false,DMG_INFLICTOR_IS_PUFF);
|
|
SWWMUtility.DoKnockback(hit.HitActor,hit.x+(0,0,0.025),mm*FRandom[Wallbuster](0.4,1.2));
|
|
if ( hit.HitActor.bNOBLOOD || hit.HitActor.bINVULNERABLE || hit.HitActor.bDORMANT )
|
|
{
|
|
let p = Spawn(impact,hit.HitLocation);
|
|
p.angle = atan2(hit.x.y,hit.x.x)+180;
|
|
p.pitch = asin(hit.x.z);
|
|
p.target = self;
|
|
p.special1 = max(0,(bc-5)/4);
|
|
}
|
|
else
|
|
{
|
|
hit.HitActor.TraceBleed(realdmg,self);
|
|
hit.HitActor.SpawnBlood(hit.HitLocation,atan2(hit.x.y,hit.x.x)+180,realdmg);
|
|
hit.HitActor.A_StartSound("spreadgun/pelletf",CHAN_DAMAGE,CHANF_OVERLAP,.4,4.);
|
|
}
|
|
}
|
|
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HitActor) )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTracerHitNormal(t.Results);
|
|
let p = Spawn(impact,t.Results.HitPos+hitnormal*4);
|
|
p.angle = atan2(hitnormal.y,hitnormal.x);
|
|
p.pitch = asin(-hitnormal.z);
|
|
p.target = self;
|
|
p.special1 = max(0,(bc-5)/4);
|
|
if ( t.Results.HitType == TRACE_HitFloor ) p.CheckSplash(40);
|
|
if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
|
}
|
|
}
|
|
override Vector3 GetTraceOffset( int index )
|
|
{
|
|
double t1 = 90-int(index%5)*72;
|
|
double t2 = 360-int(index/5)*72;
|
|
Vector2 b = AngleToVector(t1,1.2);
|
|
b.y += 3.;
|
|
Vector2 n = RotateVector(b,t2);
|
|
return (10.,3.5+n.x,-6.+n.y);
|
|
}
|
|
action void A_FireShells( int num = 1 )
|
|
{
|
|
// auto-reload if we're fully empty and there's ammo to use
|
|
bool isloaded = false;
|
|
for ( int i=0; i<25; i++ )
|
|
{
|
|
if ( !invoker.loaded[i] || invoker.fired[i] )
|
|
continue;
|
|
isloaded = true;
|
|
break;
|
|
}
|
|
if ( !isloaded && (GetShellsToLoad() > 0) )
|
|
{
|
|
player.SetPSprite(PSP_WEAPON,ResolveState("Reload"));
|
|
return;
|
|
}
|
|
// speen
|
|
if ( num == 1 ) invoker.whichspin = 1;
|
|
else if ( num == 5 ) invoker.whichspin = 2;
|
|
else invoker.whichspin = 0;
|
|
// first pass, count fired rounds
|
|
int howmany = 0;
|
|
for ( int i=0; i<num; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] || invoker.fired[idx] ) continue;
|
|
howmany++;
|
|
}
|
|
if ( howmany <= 0 )
|
|
{
|
|
A_StartSound("wallbuster/dryfire",CHAN_WEAPON,CHANF_OVERLAP);
|
|
player.SetPsprite(PSP_WEAPON,ResolveState("DryFire"));
|
|
return;
|
|
}
|
|
Vector3 origin, dir;
|
|
double a, s;
|
|
let [x, y, z] = SWWMUtility.GetPlayerAxes(self);
|
|
let [x2, y2, z2] = SWWMUtility.GetPlayerAxesAutoAimed(self);
|
|
int flashstr = 0;
|
|
int alertness = 0;
|
|
// second pass, play the fire effects
|
|
for ( int i=0; i<num; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] || invoker.fired[idx] ) continue;
|
|
A_StartSound("spreadgun/redfire",CHAN_WEAPON,CHANF_OVERLAP,1./(howmany**.65),.6-howmany*.004,1.-howmany*.015);
|
|
flashstr = max(120,flashstr+10);
|
|
A_CBTFlash("FlashRed",i);
|
|
if ( alertness > 800 ) alertness += 200;
|
|
else alertness += 800;
|
|
}
|
|
if ( howmany < 3 )
|
|
{
|
|
player.SetPsprite(PSP_WEAPON,ResolveState("FireOne"));
|
|
SWWMHandler.DoFlash(self,Color(40,255,192,64),5);
|
|
}
|
|
else if ( howmany < 15 )
|
|
{
|
|
player.SetPsprite(PSP_WEAPON,ResolveState("FireFive"));
|
|
SWWMHandler.DoFlash(self,Color(60,255,192,64),6);
|
|
}
|
|
else
|
|
{
|
|
player.SetPsprite(PSP_WEAPON,ResolveState("FireTwentyFive"));
|
|
SWWMHandler.DoFlash(self,Color(80,255,192,64),7);
|
|
}
|
|
double qk = min(9.,1.+howmany/4.);
|
|
int ql = min(25,6+howmany/2);
|
|
A_QuakeEx(qk,qk,qk,ql,0,8,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:qk*.15);
|
|
A_BumpFOV(1.-qk*.04);
|
|
A_BumpView(-qk*.6);
|
|
A_AlertMonsters(swwm_uncapalert?0:alertness);
|
|
A_PlayerFire();
|
|
if ( flashstr > 0 )
|
|
{
|
|
let l = Spawn("SWWMWeaponLight",pos);
|
|
l.args[3] = flashstr;
|
|
l.target = self;
|
|
}
|
|
// third pass, actually fire them
|
|
Vector3 base = SWWMUtility.GetFireOffset(self,10,3.5,-6);
|
|
SpreadgunTracer st;
|
|
for ( int i=0; i<num; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] || invoker.fired[idx] ) continue;
|
|
invoker.fired[idx] = true;
|
|
double t1 = 90-int(i%5)*72;
|
|
double t2 = 360-int(i/5)*72;
|
|
Vector2 b = AngleToVector(t1,1.2);
|
|
b.y += 3.;
|
|
Vector2 n = RotateVector(b,t2);
|
|
origin = level.Vec3Offset(base,n.x*y+n.y*z);
|
|
// the shooting
|
|
if ( !st ) st = new("SpreadgunTracer");
|
|
// attempt to uniformize expected damage while reducing traces (mainly for performance)
|
|
int expecteddmg = 200;
|
|
int numshot = max(21-howmany,5);
|
|
int individualdmg = int(ceil(expecteddmg/double(numshot)));
|
|
for ( int j=0; j<numshot; j++ )
|
|
{
|
|
a = FRandom[Wallbuster](0,360);
|
|
s = FRandom[Wallbuster](0,.05+.006*howmany);
|
|
dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
|
|
st.hitlist.Clear();
|
|
st.shootthroughlist.Clear();
|
|
st.waterhitlist.Clear();
|
|
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,0,ignore:self);
|
|
ProcessTraceHit(st,origin,dir,individualdmg,7000,bc:5+howmany/6,(howmany>1));
|
|
}
|
|
for ( int i=0; i<(16-howmany/2); i++ )
|
|
{
|
|
let s = Spawn("SWWMSmoke",origin);
|
|
s.special1 = 1;
|
|
s.scale *= .9;
|
|
s.alpha *= .3;
|
|
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
|
|
s.vel += vel*.5+x*FRandom[Wallbuster](3.,5.)+y*FRandom[Wallbuster](-1,1)+z*FRandom[Wallbuster](-1,1);
|
|
}
|
|
for ( int i=0; i<(20-howmany/2); i++ )
|
|
{
|
|
let s = Spawn("SWWMSpark",origin);
|
|
s.scale *= .3;
|
|
s.alpha *= .4;
|
|
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-2,2)+z*FRandom[Wallbuster](-2,2);
|
|
}
|
|
SWWMUtility.DoKnockback(self,-x,12000.);
|
|
}
|
|
}
|
|
action void A_SpinOne()
|
|
{
|
|
A_StartSound("wallbuster/spin",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.rotation[invoker.rotation[5]]++;
|
|
while ( invoker.rotation[invoker.rotation[5]] > 4 ) invoker.rotation[invoker.rotation[5]] -= 5;
|
|
}
|
|
action void A_SpinBig()
|
|
{
|
|
A_StartSound("wallbuster/spinbig",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.rotation[5]++;
|
|
while ( invoker.rotation[5] > 4 ) invoker.rotation[5] -= 5;
|
|
}
|
|
action void A_HandSpin()
|
|
{
|
|
A_StartSound("wallbuster/handspin",CHAN_WEAPON,CHANF_OVERLAP);
|
|
invoker.rotation[5]--;
|
|
while ( invoker.rotation[5] < 0 ) invoker.rotation[5] += 5;
|
|
}
|
|
action void A_StartDetachOverlays()
|
|
{
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] ) A_ChangeModel("",1,"","",4+i,"models","",CMDL_USESURFACESKIN,-1);
|
|
else if ( invoker.fired[idx] ) A_ChangeModel("",1,"","",4+i,"models","Shell_Normal_Used.png",CMDL_USESURFACESKIN,-1);
|
|
else A_ChangeModel("",1,"","",4+i,"models","Shell_Normal.png",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
A_StartSound("wallbuster/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
action void A_StartAttachOverlays()
|
|
{
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] ) A_ChangeModel("",1,"","",4+i,"models","",CMDL_USESURFACESKIN,-1);
|
|
else A_ChangeModel("",1,"","",4+i,"models","Shell_Normal.png",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
A_StartSound("wallbuster/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
action void A_DropShells()
|
|
{
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
if ( invoker.loaded[invoker.rotation[5]*5+i] )
|
|
{
|
|
if ( invoker.fired[invoker.rotation[5]*5+i] )
|
|
{
|
|
let [x, y, z] = SWWMUtility.GetPlayerAxes(self);
|
|
Vector3 origin = SWWMUtility.GetFireOffset(self,10,-3,-13);
|
|
let c = Spawn("RedShellCasing",origin);
|
|
c.angle = angle;
|
|
c.pitch = pitch;
|
|
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3));
|
|
c.vel += vel*.5;
|
|
}
|
|
else
|
|
{
|
|
if ( (invoker.Ammo1.Amount >= invoker.Ammo1.MaxAmount) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
|
|
invoker.BufferAmmo(invoker.AmmoType1,1);
|
|
else invoker.Ammo1.Amount = min(invoker.Ammo1.Amount+1,invoker.Ammo1.MaxAmount);
|
|
}
|
|
}
|
|
invoker.loaded[invoker.rotation[5]*5+i] = null;
|
|
invoker.fired[invoker.rotation[5]*5+i] = false;
|
|
A_ChangeModel("",1,"","",4+i,"models","",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
invoker.rotation[invoker.rotation[5]] = 4;
|
|
}
|
|
action int GetShellsToLoad()
|
|
{
|
|
int cnt = 25;
|
|
for ( int i=0; i<25; i++ ) cnt -= (invoker.loaded[i]&&!invoker.fired[i]);
|
|
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
|
|
{
|
|
int realammo = invoker.CountBufferedAmmo(invoker.AmmoType1)+invoker.Ammo1.Amount;
|
|
cnt = min(cnt,realammo);
|
|
}
|
|
return max(0,cnt);
|
|
}
|
|
action void A_LoadShell()
|
|
{
|
|
int toload = GetShellsToLoad();
|
|
if ( toload <= 0 ) return;
|
|
invoker.loaded[invoker.rotation[5]*5+invoker.rotation[invoker.rotation[5]]] = true;
|
|
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) && !invoker.FetchBufferedAmmo(invoker.AmmoType1,1) )
|
|
{
|
|
let am = FindInventory(invoker.AmmoType1);
|
|
if ( am && (am.Amount > 0) ) am.Amount--;
|
|
}
|
|
if ( (toload > 1) && (invoker.rotation[invoker.rotation[5]] > 0) ) // keep spinning if there's more to load
|
|
invoker.rotation[invoker.rotation[5]]--;
|
|
A_StartSound("wallbuster/load",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
|
|
override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
|
|
{
|
|
bool good = Super.PickupForAmmoSWWM(ownedWeapon);
|
|
let Owner = ownedWeapon.Owner;
|
|
if ( (AmmoGive1 == 0) && bInitialized )
|
|
{
|
|
for ( int i=0; i<25; i++ )
|
|
{
|
|
if ( !loaded[i] || fired[i] ) continue;
|
|
let cur = Owner.FindInventory(AmmoType1);
|
|
if ( !cur )
|
|
{
|
|
cur = Inventory(Spawn(AmmoType1));
|
|
cur.Amount = 0;
|
|
cur.AttachToOwner(Owner);
|
|
}
|
|
// give the loaded shell (or drop)
|
|
if ( cur.Amount >= cur.MaxAmount ) cur.CreateTossable(1);
|
|
cur.Amount++;
|
|
good = true;
|
|
}
|
|
}
|
|
return good;
|
|
}
|
|
|
|
override void MarkPrecacheSounds()
|
|
{
|
|
Super.MarkPrecacheSounds();
|
|
MarkSound("wallbuster/select");
|
|
MarkSound("wallbuster/deselect");
|
|
MarkSound("wallbuster/dryfire");
|
|
MarkSound("wallbuster/spin");
|
|
MarkSound("wallbuster/spinbig");
|
|
MarkSound("wallbuster/unlock");
|
|
MarkSound("wallbuster/lock");
|
|
MarkSound("wallbuster/meleestart");
|
|
MarkSound("wallbuster/meleeend");
|
|
MarkSound("wallbuster/detach");
|
|
MarkSound("wallbuster/attach");
|
|
MarkSound("wallbuster/handspin");
|
|
MarkSound("wallbuster/load1");
|
|
MarkSound("wallbuster/load2");
|
|
MarkSound("wallbuster/load3");
|
|
MarkSound("wallbuster/load4");
|
|
MarkSound("wallbuster/smallbust1");
|
|
MarkSound("wallbuster/smallbust2");
|
|
MarkSound("wallbuster/smallbust3");
|
|
MarkSound("wallbuster/bigbust1");
|
|
MarkSound("wallbuster/bigbust2");
|
|
MarkSound("wallbuster/bigbust3");
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_WALLBUSTER";
|
|
Inventory.PickupMessage "$I_WALLBUSTER";
|
|
Obituary "$O_WALLBUSTER";
|
|
SWWMWeapon.Tooltip "$TT_WALLBUSTER";
|
|
SWWMWeapon.GetLine "getwallbuster";
|
|
SWWMWeapon.NumCrosshairs 25;
|
|
Weapon.SlotNumber 4;
|
|
Weapon.SelectionOrder 400;
|
|
Weapon.UpSound "wallbuster/select";
|
|
Weapon.AmmoType1 "RedShell";
|
|
Weapon.AmmoGive1 5;
|
|
SWWMWeapon.DropAmmoType "SWWMShellAmmoBig";
|
|
Stamina 35000;
|
|
+SWWMWEAPON.NOFIRSTGIVE;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
Select:
|
|
XZW2 G 1 A_FullRaise();
|
|
XZW2 HI 2 A_BumpView(.2,tics:2);
|
|
XZW2 JKL 2 A_BumpView(-.3,tics:2);
|
|
XZW2 MNOP 2;
|
|
Goto Ready;
|
|
Ready:
|
|
XZW2 A 1
|
|
{
|
|
int flg = WRF_ALLOWUSER1;
|
|
if ( invoker.CheckAmmo(PrimaryFire,false) ) flg |= WRF_ALLOWZOOM;
|
|
if ( GetShellsToLoad() ) flg |= WRF_ALLOWRELOAD;
|
|
A_WeaponReady(flg);
|
|
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_ZOOM) )
|
|
invoker.CheckAmmo(EitherFire,true);
|
|
}
|
|
Wait;
|
|
Fire:
|
|
XZW2 A 0 A_FireShells(1);
|
|
Goto FireOne;
|
|
AltFire:
|
|
XZW2 A 0 A_FireShells(5);
|
|
Goto FireFive;
|
|
Zoom:
|
|
XZW2 A 0 A_FireShells(25);
|
|
Goto FireTwentyFive;
|
|
FireOne:
|
|
XZW2 AQRSTUVWX 1;
|
|
XZW2 A 0
|
|
{
|
|
if ( invoker.whichspin == 2 ) return ResolveState("BigSpin");
|
|
if ( invoker.whichspin == 1 ) return ResolveState("OneSpin");
|
|
return ResolveState("Ready");
|
|
}
|
|
Goto Ready;
|
|
FireFive:
|
|
XZW2 A 2;
|
|
XZW3 CDEFGHIJK 2;
|
|
XZW2 A 0
|
|
{
|
|
if ( invoker.whichspin == 2 ) return ResolveState("BigSpin");
|
|
if ( invoker.whichspin == 1 ) return ResolveState("OneSpin");
|
|
return ResolveState("Ready");
|
|
}
|
|
Goto Ready;
|
|
FireTwentyFive:
|
|
XZW2 A 2;
|
|
XZW3 STUVWXYZ 2;
|
|
XZW4 ABC 2;
|
|
XZW2 A 0
|
|
{
|
|
if ( invoker.whichspin == 2 ) return ResolveState("BigSpin");
|
|
if ( invoker.whichspin == 1 ) return ResolveState("OneSpin");
|
|
return ResolveState("Ready");
|
|
}
|
|
Goto Ready;
|
|
DryFire:
|
|
XZW2 A 2;
|
|
XZW2 XA 4;
|
|
XZW2 A 0
|
|
{
|
|
if ( invoker.whichspin == 2 ) return ResolveState("BigSpin");
|
|
if ( invoker.whichspin == 1 ) return ResolveState("OneSpin");
|
|
return ResolveState("Ready");
|
|
}
|
|
Goto Ready;
|
|
OneSpin:
|
|
XZW2 A 1 A_SpinOne();
|
|
XZW2 YZ 2;
|
|
XZW3 A 2;
|
|
XZW3 B 0;
|
|
XZW2 A 0 A_JumpIf(invoker.rotation[invoker.rotation[5]]==0,"BigSpin");
|
|
Goto Ready;
|
|
BigSpin:
|
|
XZW2 A 1 A_SpinBig();
|
|
XZW3 LMNOPQ 2;
|
|
XZW3 R 0;
|
|
XZW2 A 0;
|
|
Goto Ready;
|
|
Reload:
|
|
XZW2 A 2 A_StartSound("wallbuster/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW4 DEFG 2;
|
|
XZW4 HIJK 1;
|
|
XZW4 L 1
|
|
{
|
|
A_QuakeEx(.8,.8,.8,6,0,15,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.3);
|
|
A_StartSound("wallbuster/unlock",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
XZW4 MNOPQ 1;
|
|
XZW4 RSTUV 1;
|
|
Goto Detach;
|
|
Detach:
|
|
XZW4 W 0
|
|
{
|
|
int rnum = GetShellsToLoad();
|
|
invoker.cancelreload = false;
|
|
A_Overlay(-9999,"CheckCancelReload");
|
|
// if it's a full rotation, don't hand-spin
|
|
if ( rnum > 20 ) return ResolveState(null);
|
|
// jump to spin if current group is full
|
|
int nset = 0, nsetp = 0;
|
|
for ( int i=0; i<5; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] || invoker.fired[idx] ) continue;
|
|
nset++;
|
|
}
|
|
// but previous isn't
|
|
for ( int i=20; i<25; i++ )
|
|
{
|
|
// get physical index
|
|
int idx = i;
|
|
// shift based on full rotation
|
|
idx = (idx+invoker.rotation[5]*5);
|
|
while ( idx > 24 ) idx -= 25;
|
|
int group = idx/5;
|
|
// shift based on group rotation
|
|
int gidx = i%5;
|
|
gidx = (gidx+invoker.rotation[group]);
|
|
while ( gidx > 4 ) gidx -= 5;
|
|
idx = gidx+group*5;
|
|
if ( !invoker.loaded[idx] || invoker.fired[idx] ) continue;
|
|
nsetp++;
|
|
}
|
|
return A_JumpIf((nset>=5)&&(nsetp<5),"DetachSpin");
|
|
}
|
|
XZW4 WXYZ 1;
|
|
XZW5 ABCDEF 1;
|
|
XZW5 G 1 A_StartSound("wallbuster/detach",CHAN_WEAPON,CHANF_OVERLAP);
|
|
Goto DetachHold;
|
|
DetachSpin:
|
|
XZWE UVWXY 1;
|
|
DetachSpinReturn:
|
|
XZWE Z 1;
|
|
XZWF A 2
|
|
{
|
|
invoker.cancelreload = false;
|
|
A_Overlay(-9999,"CheckCancelReload");
|
|
A_HandSpin();
|
|
}
|
|
XZWF BCDEFG 2;
|
|
XZWF J 1 A_StartSound("wallbuster/detach",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZWF K 0;
|
|
Goto DetachHold;
|
|
DetachSpinAgain:
|
|
XZWF G 2;
|
|
XZWF H 0;
|
|
XZWH JKL 1;
|
|
Goto DetachSpinReturn;
|
|
DetachHold:
|
|
XZW5 H 1
|
|
{
|
|
A_StartDetachOverlays();
|
|
A_PlayerReload();
|
|
}
|
|
XZW5 IJKLMNOPQ 1;
|
|
XZW5 R 2 A_DropShells();
|
|
XZW5 ST 2;
|
|
XZW5 V 2 A_LoadShell();
|
|
XZW5 X 2 A_LoadShell();
|
|
XZW5 Y 2 A_LoadShell();
|
|
XZW6 A 2 A_LoadShell();
|
|
XZW6 C 2 A_LoadShell();
|
|
XZW6 E 0
|
|
{
|
|
int rnum = invoker.cancelreload?0:GetShellsToLoad();
|
|
player.SetPSprite(-9999,null);
|
|
return A_JumpIf(rnum,"AttachSpin");
|
|
}
|
|
Goto Attach;
|
|
CheckCancelReload:
|
|
TNT1 A 1
|
|
{
|
|
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_RELOAD|BT_ZOOM|BT_USER1) )
|
|
invoker.cancelreload = true;
|
|
}
|
|
Wait;
|
|
Attach:
|
|
XZW6 EF 2;
|
|
XZW6 G 1 A_StartAttachOverlays();
|
|
XZW6 HIJKL 1;
|
|
XZW6 M 1 A_StartSound("wallbuster/attach",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW6 NOPQ 1;
|
|
XZW6 RS 2;
|
|
XZW6 TUVWXYZ 1;
|
|
XZW7 ABC 1;
|
|
Goto EndReload;
|
|
AttachSpin:
|
|
XZW7 JK 2;
|
|
XZW7 L 1 A_StartAttachOverlays();
|
|
XZW7 MNOPQR 1;
|
|
XZW7 S 1 A_StartSound("wallbuster/attach",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW7 TUVW 1;
|
|
XZW7 XYZ 1;
|
|
XZW8 ABC 1;
|
|
XZW8 D 2
|
|
{
|
|
invoker.cancelreload = false;
|
|
A_Overlay(-9999,"CheckCancelReload");
|
|
A_HandSpin();
|
|
}
|
|
XZW8 EFGHIL 2;
|
|
XZW8 M 1 A_StartSound("wallbuster/detach",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW8 N 0;
|
|
Goto DetachHold;
|
|
AttachSpinAgain:
|
|
XZW8 IJ 2;
|
|
XZW8 K 1;
|
|
XZW8 K 0;
|
|
XZWH MNO 1;
|
|
Goto DetachSpinReturn;
|
|
EndReload:
|
|
XZW4 W 1
|
|
{
|
|
invoker.ClearBufferedAmmo();
|
|
for ( int i=0; i<5; i++ )
|
|
A_ChangeModel("",1,"","",4+i,"models","",CMDL_USESURFACESKIN,-1);
|
|
}
|
|
XZW8 OPQRS 1;
|
|
XZW8 T 1
|
|
{
|
|
A_QuakeEx(1.,1.,1.,8,0,15,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.4);
|
|
A_StartSound("wallbuster/lock",CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
XZW8 UVWXY 1;
|
|
XZW8 Z 1 A_StartSound("wallbuster/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW9 ABC 1;
|
|
XZW9 DEF 2;
|
|
Goto Ready;
|
|
User1:
|
|
XZW2 A 2
|
|
{
|
|
A_StartSound("wallbuster/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_PlayerMelee();
|
|
}
|
|
XZW9 GHI 2 A_BumpView(1,tics:2);
|
|
XZW9 J 2
|
|
{
|
|
A_BumpFOV(.96);
|
|
A_QuakeEx(1.2,1.2,1.2,8,0,15,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.5);
|
|
A_Parry(9);
|
|
}
|
|
XZW9 KLM 1 A_BumpView(-4);
|
|
XZW9 N 1 A_Melee(70,"demolitionist/whitl",1.2,1.4,1.3);
|
|
XZW9 OPQRS 2;
|
|
XZW9 T 2 A_StartSound("wallbuster/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW9 UVW 2;
|
|
XZW9 XYZ 1;
|
|
XZWA ABCD 2;
|
|
Goto Ready;
|
|
Deselect:
|
|
XZW2 A 2 A_StartSound("wallbuster/deselect",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW2 B 2;
|
|
XZW2 CD 2 A_BumpView(.1,tics:2);
|
|
XZW2 EF 2 A_BumpView(.2,tics:2);
|
|
XZW2 G -1 A_FullLower();
|
|
Stop;
|
|
FlashRed:
|
|
XZWW A 2 Bright;
|
|
Stop;
|
|
XZWW B 2 Bright;
|
|
Stop;
|
|
XZWW C 2 Bright;
|
|
Stop;
|
|
XZWW D 2 Bright;
|
|
Stop;
|
|
XZWW E 2 Bright;
|
|
Stop;
|
|
XZWW F 2 Bright;
|
|
Stop;
|
|
XZWW G 2 Bright;
|
|
Stop;
|
|
XZWW H 2 Bright;
|
|
Stop;
|
|
XZWW I 2 Bright;
|
|
Stop;
|
|
XZWW J 2 Bright;
|
|
Stop;
|
|
XZWW K 2 Bright;
|
|
Stop;
|
|
XZWW L 2 Bright;
|
|
Stop;
|
|
XZWW M 2 Bright;
|
|
Stop;
|
|
XZWW N 2 Bright;
|
|
Stop;
|
|
XZWW O 2 Bright;
|
|
Stop;
|
|
XZWW P 2 Bright;
|
|
Stop;
|
|
XZWW Q 2 Bright;
|
|
Stop;
|
|
XZWW R 2 Bright;
|
|
Stop;
|
|
XZWW S 2 Bright;
|
|
Stop;
|
|
XZWW T 2 Bright;
|
|
Stop;
|
|
XZWW U 2 Bright;
|
|
Stop;
|
|
XZWW V 2 Bright;
|
|
Stop;
|
|
XZWW W 2 Bright;
|
|
Stop;
|
|
XZWW X 2 Bright;
|
|
Stop;
|
|
XZWW Y 2 Bright;
|
|
Stop;
|
|
}
|
|
}
|