swwmgz_m/zscript/swwm_cbt.zsc

1469 lines
41 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 WallbusterReloadMenu : GenericMenu
{
transient Font TewiFont, MPlusFont, MiniwiFont, k6x8Font;
TextureID MainWindow, AmmoIcon[4];
int sel0;
Array<int> queue;
int AmmoSets[4];
bool isrclick, ismclick;
transient CVar pauseme, lang;
// if playing in Japanese, returns an alternate font of the same height
// Tewi -> MPlus
// Miniwi -> k6x8
Font LangFont( Font req )
{
if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
if ( lang.GetString() ~== "jp" )
{
if ( req == MiniwiFont ) return k6x8Font;
return MPlusFont;
}
return req;
}
override void Init( Menu parent )
{
Super.Init(parent);
if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].ReadyWeapon is 'Wallbuster') )
{
EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer);
Close();
return;
}
TewiFont = Font.GetFont('TewiShaded');
MPlusFont = Font.GetFont('MPlusShaded');
MiniwiFont = Font.GetFont('MiniwiShaded');
k6x8Font = Font.GetFont('k6x8Shaded');
MainWindow = TexMan.CheckForTexture("graphics/HUD/WallbusterMenu.png",TexMan.Type_Any);
AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any);
AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any);
AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any);
AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any);
MenuSound("menu/demotab");
queue.Clear();
sel0 = 0;
}
override void Ticker()
{
Super.Ticker();
if ( !pauseme ) pauseme = CVar.GetCVar('swwm_cbtpause',players[consoleplayer]);
if ( pauseme.GetBool() ) menuactive = Menu.On;
else menuactive = Menu.OnNoPause;
if ( (players[consoleplayer].Health > 0) && (players[consoleplayer].ReadyWeapon is 'Wallbuster') ) return;
MenuEvent(MKEY_BACK,false);
}
private bool IsDone()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
if ( queue.Size() >= 25 ) return true;
for ( int i=0; i<4; i++ )
{
if ( (players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]) > 0 )
return false;
}
return true;
}
private bool PushAmmo( bool autoshift = false )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
if ( queue.Size() >= 25 ) return true;
if ( (players[consoleplayer].mo.CountInv(types[sel0])-AmmoSets[sel0]) <= 0 )
{
if ( autoshift )
{
// switch to next available ammo
for ( int i=0; i<4; i++ )
{
int idx = (sel0+i)%4;
if ( (players[consoleplayer].mo.CountInv(types[idx])-AmmoSets[idx]) > 0 )
{
sel0 = idx;
return PushAmmo(true);
}
}
}
MenuSound("menu/noinvuse");
return false;
}
if ( !autoshift ) MenuSound("menu/demosel");
AmmoSets[sel0]++;
queue.Push(sel0);
return true;
}
override bool MenuEvent( int mkey, bool fromcontroller )
{
switch ( mkey )
{
case MKEY_BACK:
queue.Clear();
for ( int i=0; i<4; i++ ) AmmoSets[i] = 0;
MenuSound("menu/democlose");
EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer);
Close();
return true;
case MKEY_ENTER:
if ( queue.Size() <= 0 )
{
while ( queue.Size() < 25 )
{
if ( !PushAmmo(true) )
break;
}
}
String cbt = "swwmcbt.";
for ( int i=0; i<queue.Size(); i++ )
cbt.AppendFormat("%d,",queue[i]);
MenuSound("menu/democlose");
EventHandler.SendNetworkEvent(cbt,consoleplayer);
Close();
return true;
case MKEY_UP:
if ( queue.Size() <= 0 ) MenuSound("menu/noinvuse");
else
{
MenuSound("menu/demoscroll");
AmmoSets[queue[queue.Size()-1]]--;
queue.Pop();
}
return true;
case MKEY_DOWN:
if ( IsDone() )
{
MenuSound("menu/noinvuse");
return true;
}
PushAmmo();
return true;
case MKEY_RIGHT:
MenuSound("menu/demotab");
sel0++;
if ( sel0 > 3 ) sel0 = 0;
return true;
case MKEY_LEFT:
MenuSound("menu/demotab");
sel0--;
if ( sel0 < 0 ) sel0 = 3;
return true;
case MKEY_PAGEDOWN:
if ( IsDone() )
{
MenuSound("menu/noinvuse");
return true;
}
while ( queue.Size() < 25 )
{
if ( !PushAmmo(true) )
return true;
}
MenuSound("menu/demosel");
return true;
case MKEY_CLEAR:
case MKEY_PAGEUP:
if ( queue.Size() <= 0 ) MenuSound("menu/noinvuse");
else
{
MenuSound("menu/demoscroll");
queue.Clear();
for ( int i=0; i<4; i++ ) AmmoSets[i] = 0;
}
return true;
}
return Super.MenuEvent(mkey,fromcontroller);
}
override bool MouseEvent( int type, int mx, int my )
{
bool res = Super.MouseEvent(type,mx,my);
// TODO mouse input?
return res;
}
override bool OnUiEvent( UIEvent ev )
{
int y;
bool res;
switch ( ev.type )
{
case UIEvent.Type_LButtonDown:
isrclick = false;
ismclick = false;
return Super.OnUIEvent(ev);
break;
case UIEvent.Type_RButtonDown:
isrclick = true;
ismclick = false;
// copy over what base menus do for L click
y = ev.MouseY;
res = MouseEventBack(MOUSE_Click,ev.MouseX,y);
if ( res ) y = -1;
res |= MouseEvent(MOUSE_Click,ev.MouseX,y);
if ( res ) SetCapture(true);
return false;
break;
case UIEvent.Type_MButtonDown:
isrclick = false;
ismclick = true;
// copy over what base menus do for L click
y = ev.MouseY;
res = MouseEventBack(MOUSE_Click,ev.MouseX,y);
if ( res ) y = -1;
res |= MouseEvent(MOUSE_Click,ev.MouseX,y);
if ( res ) SetCapture(true);
return false;
break;
case UIEvent.Type_RButtonUp:
case UIEvent.Type_MButtonUp:
// copy over what base menus do for L release
if ( mMouseCapture )
{
SetCapture(false);
y = ev.MouseY;
res = MouseEventBack(MOUSE_Release,ev.MouseX,y);
if ( res ) y = -1;
res |= MouseEvent(MOUSE_Release,ev.MouseX,y);
}
return false;
break;
}
return Super.OnUIEvent(ev);
}
override void Drawer()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
Super.Drawer();
double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.);
Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs;
Vector2 origin = (ss.x-132,ss.y-26)/2.;
Screen.DrawTexture(MainWindow,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int ox = 27, oy = 2;
for ( int i=0; i<4; i++ )
{
Screen.DrawTexture(AmmoIcon[i],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0));
String astr = String.Format("%3d",players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]);
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+ox-(TewiFont.StringWidth(astr)+1),origin.y+oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0));
ox += 33;
}
// pointer (▸)
Screen.DrawChar(TewiFont,Font.CR_GREEN,origin.x+2+33*sel0,origin.y,0x25B8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int siz = queue.Size()-1;
ox = 2+siz*5+(siz/5);
oy = 15;
for ( int i=0; i<=siz; i++ )
{
Screen.DrawTexture(AmmoIcon[queue[i]],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= 5;
if ( !((i+1)%5) ) ox--;
}
// text stuff
String str;
Font fnt;
int boxw, sw;
double x, y;
fnt = LangFont(TewiFont);
str = StringTable.Localize("$SWWM_BUSTERTITLE");
sw = fnt.StringWidth(str);
boxw = sw;
fnt = LangFont(MiniwiFont);
str = "(C)2148 Akari Labs";
sw = fnt.StringWidth(str);
if ( sw > boxw ) boxw = sw;
x = floor((ss.x-boxw)/2.);
y = origin.y-30;
Screen.Dim("Black",.8,int((x-2)*hs),int((y-1)*hs),int((boxw+4)*hs),int(25*hs));
fnt = LangFont(TewiFont);
str = StringTable.Localize("$SWWM_BUSTERTITLE");
sw = fnt.StringWidth(str);
x = floor((ss.x-sw)/2.);
Screen.DrawText(fnt,Font.CR_FIRE,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
y += 14;
fnt = LangFont(MiniwiFont);
str = "(C)2148 Akari Labs";
sw = fnt.StringWidth(str);
x = floor((ss.x-sw)/2.);
Screen.DrawText(fnt,Font.CR_GOLD,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
y = origin.y+36;
fnt = LangFont(MiniwiFont);
str = StringTable.Localize("$SWWM_BUSTERKEYS");
BrokenLines l = fnt.BreakLines(str,300);
boxw = 0;
for ( int i=0; i<l.Count(); i++ )
{
sw = l.StringWidth(i);
if ( sw > boxw ) boxw = sw;
}
x = floor((ss.x-boxw)/2.);
Screen.Dim("Black",.8,int((x-2)*hs),int((y-2)*hs),int((boxw+4)*hs),int((9*l.Count()+2)*hs));
for ( int i=0; i<l.Count(); i++ )
{
Screen.DrawText(fnt,Font.CR_WHITE,x,y,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
y += 9;
}
}
}
Class CBTHandler : StaticEventHandler
{
bool initme;
double cbtvol;
transient CVar cbtme;
override void WorldTick()
{
if ( !cbtme ) cbtme = CVar.GetCVar('swwm_cbtmeme',players[consoleplayer]);
if ( !players[consoleplayer].mo ) return;
let cbt = players[consoleplayer].mo.FindInventory("Wallbuster");
if ( !cbt ) return;
if ( cbt == players[consoleplayer].ReadyWeapon )
{
if ( !initme && (cbtme.GetInt() > 0) )
{
initme = true;
cbtvol = 1.;
}
cbtvol = min(1.,cbtvol+.2);
if ( (cbtme.GetInt() >= 1) && !cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
cbt.A_StartSound("wallbuster/olddays",CHAN_AMBEXTRA+1,CHANF_UI|CHANF_LOOPING,cbtvol,0.);
else if ( (cbtme.GetInt() < 1) && cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
cbt.A_StopSound(CHAN_AMBEXTRA+1);
if ( (cbtme.GetInt() >= 2) && !cbt.IsActorPlayingSound(CHAN_AMBEXTRA+2) )
{
// restart the music
if ( cbt.IsActorPlayingSound(CHAN_AMBEXTRA+1) )
{
cbt.A_StopSound(CHAN_AMBEXTRA+1);
cbt.A_StartSound("wallbuster/olddays",CHAN_AMBEXTRA+1,CHANF_UI|CHANF_LOOPING,cbtvol,0.);
}
cbt.A_StartSound("wallbuster/cbt",CHAN_AMBEXTRA+2,CHANF_UI|CHANF_LOOPING,cbtvol,0.);
}
else if ( (cbtme.GetInt() < 2) && cbt.IsActorPlayingSound(CHAN_AMBEXTRA+2) )
cbt.A_StopSound(CHAN_AMBEXTRA+2);
}
else cbtvol = max(0.,cbtvol-.2);
cbt.A_SoundVolume(CHAN_AMBEXTRA+1,cbtvol);
cbt.A_SoundVolume(CHAN_AMBEXTRA+2,cbtvol);
}
}
Class Wallbuster : SWWMWeapon
{
Class<Ammo> loaded[25];
bool fired[25];
int rotation[6];
bool initialized;
Array<Class<Ammo> > reloadqueue;
transient bool waitreload;
int whichspin;
transient CVar cbtmuffler, timeme;
bool timing;
int reloadtime[2];
transient bool cancelreload;
double srecoil;
transient ui TextureID WeaponBox, AmmoIcon[4], LoadIcon[4], UsedIcon[4], EmptyIcon;
transient ui Font TewiFont;
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
/*
(Layout of indices)
0
4 1
3 2
L 9
K M 8 5
O N 7 6
H D
G I C E
F J B A
*/
static const int barrelposx[] =
{
29,24,26,32,34,
10,12,18,20,15,
17,23,25,20,15,
41,43,38,33,35,
48,43,38,40,46
};
static const int barrelposy[] =
{
46,42,36,36,42,
31,25,25,31,35,
8, 8,14,18,14,
8,14,18,14, 8,
31,35,31,25,25
};
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
if ( !WeaponBox )
{
WeaponBox = TexMan.CheckForTexture("graphics/HUD/WallbusterDisplay.png",TexMan.Type_Any);
AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any);
AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any);
AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any);
AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any);
LoadIcon[0] = TexMan.CheckForTexture("graphics/HUD/WallbusterRed.png",TexMan.Type_Any);
LoadIcon[1] = TexMan.CheckForTexture("graphics/HUD/WallbusterGreen.png",TexMan.Type_Any);
LoadIcon[2] = TexMan.CheckForTexture("graphics/HUD/WallbusterBlue.png",TexMan.Type_Any);
LoadIcon[3] = TexMan.CheckForTexture("graphics/HUD/WallbusterPurple.png",TexMan.Type_Any);
UsedIcon[0] = TexMan.CheckForTexture("graphics/HUD/WallbusterRedUsed.png",TexMan.Type_Any);
UsedIcon[1] = TexMan.CheckForTexture("graphics/HUD/WallbusterGreenUsed.png",TexMan.Type_Any);
UsedIcon[2] = TexMan.CheckForTexture("graphics/HUD/WallbusterBlueUsed.png",TexMan.Type_Any);
UsedIcon[3] = TexMan.CheckForTexture("graphics/HUD/WallbusterPurpleUsed.png",TexMan.Type_Any);
EmptyIcon = TexMan.CheckForTexture("graphics/HUD/WallbusterEmpty.png",TexMan.Type_Any);
}
Screen.DrawTexture(WeaponBox,false,bx-54,by-72,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int ox = 6;
int oy = 60;
for ( int i=0; i<4; i++ )
{
Screen.DrawTexture(AmmoIcon[i],false,bx-ox,by-oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
String astr = String.Format("%3d",Owner.CountInv(types[i]));
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-ox-(TewiFont.StringWidth(astr)+1),by-oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
oy += 10;
if ( i == 1 )
{
oy = 60;
ox = 33;
}
}
// and here's the big clusterdick
for ( int i=0; i<25; i++ )
{
int idx = i;
int group = idx/5;
// shift based on group rotation
int gidx = i%5;
gidx = (gidx-rotation[group]);
while ( gidx < 0 ) gidx += 5;
idx = gidx+group*5;
// shift based on full rotation
idx = (idx-rotation[5]*5);
while ( idx < 0 ) idx += 25;
if ( !loaded[i] )
{
Screen.DrawTexture(EmptyIcon,false,bx-barrelposx[idx],by-barrelposy[idx],DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
continue;
}
int which = 0;
for ( int j=0; j<4; j++ )
{
if ( !(loaded[i] is types[j]) ) continue;
which = j;
break;
}
Screen.DrawTexture(fired[i]?UsedIcon[which]:LoadIcon[which],false,bx-barrelposx[idx],by-barrelposy[idx],DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
override bool UsesAmmo( Class<Ammo> kind )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
for ( int i=0; i<4; i++ ) if ( kind is types[i] ) return true;
return false;
}
override bool ReportHUDAmmo()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true;
for ( int i=0; i<25; i++ ) if ( loaded[i] && !fired[i] ) return true;
return false;
}
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
if ( (firemode == PrimaryFire) || (firemode == AltFire) )
{
for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true;
for ( int i=0; i<25; i++ ) if ( loaded[i] && !fired[i] ) return true;
return false;
}
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
}
bool CanReload()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true;
return false;
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
if ( !initialized )
{
// first wallbuster is fully loaded
initialized = true;
for ( int i=0; i<25; i++ )
loaded[i] = "RedShell";
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 large = false )
{
// TODO wall busting
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Spawn(large?"InvisibleSplasher":"SmolInvisibleSplasher",t.WaterHitList[i].hitpos);
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);
}
for ( int i=0; i<t.HitList.Size(); i++ )
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Wallbuster](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
p.angle = atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180;
p.pitch = asin(t.HitList[i].x.z);
p.special1 = max(0,(bc-5)/4);
}
else
{
t.HitList[i].HitActor.TraceBleed(realdmg,self);
t.HitList[i].HitActor.SpawnBlood(t.HitList[i].HitLocation,atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180,realdmg);
if ( large ) t.HitList[i].HitActor.A_StartSound("spreadgun/slugf",CHAN_BODY,CHANF_OVERLAP,1.,2.);
else t.HitList[i].HitActor.A_StartSound("spreadgun/pelletf",CHAN_BODY,CHANF_OVERLAP,.4,4.);
}
}
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HasHitSky) )
{
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;
}
if ( t.Results.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
let p = Spawn(impact,t.Results.HitPos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.special1 = max(0,(bc-5)/4);
}
}
action void A_FireShells( int num = 1 )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
static const statelabel flashes[] = {"FlashRed","FlashGreen","FlashBlue","FlashPurple"};
static const String sounds[] = {"spreadgun/redfire","spreadgun/greenfire","spreadgun/bluefire","spreadgun/purplefire"};
static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(48,32,176,255),Color(24,255,224,96)};
int redflashstr = 0;
int blueflashstr = 0;
// 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 x, y, z, origin, x2, y2, z2, dir;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
if ( !invoker.cbtmuffler ) invoker.cbtmuffler = CVar.GetCVar('swwm_earbuster',players[consoleplayer]);
// 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;
int which = 0;
for ( int j=0; j<4; j++ )
{
if ( !(invoker.loaded[idx] is types[j]) ) continue;
which = j;
break;
}
double rfact = invoker.cbtmuffler.GetBool()?.85:.6;
A_StartSound(sounds[which],CHAN_WEAPON,CHANF_OVERLAP,1./(howmany**rfact),.6-howmany*.004,1.-howmany*.012);
if ( which == 0 ) redflashstr = max(120,redflashstr+10);
else if ( which == 1 ) redflashstr = max(90,redflashstr+8);
else if ( which == 2 ) blueflashstr = max(160,blueflashstr+10);
else if ( which == 3 ) redflashstr = max(60,redflashstr+6);
A_CBTFlash(flashes[which],i);
}
if ( howmany < 3 ) player.SetPsprite(PSP_WEAPON,ResolveState("FireOne"));
else if ( howmany < 15 ) player.SetPsprite(PSP_WEAPON,ResolveState("FireFive"));
else player.SetPsprite(PSP_WEAPON,ResolveState("FireTwentyFive"));
int qk = min(9,1+howmany/5);
int ql = min(25,6+howmany/2);
A_QuakeEx(qk,qk,qk,ql,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:qk*.15);
A_ZoomFactor(1.+qk*.04,ZOOM_INSTANT);
A_ZoomFactor(1.);
if ( redflashstr > 0 )
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = redflashstr;
l.target = self;
}
if ( blueflashstr > 0 )
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[0] = 96;
l.args[1] = 224;
l.args[2] = 255;
l.args[3] = blueflashstr;
l.target = self;
}
// third pass, actually fire them
A_Overlay(-9999,"InvRecoil");
invoker.srecoil = 0.;
Vector3 base = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+8*y-10*z);
SpreadgunTracer st;
SpreadSlugTracer sst;
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;
int which = 0;
for ( int j=0; j<4; j++ )
{
if ( !(invoker.loaded[idx] is types[j]) ) continue;
which = j;
break;
}
invoker.fired[idx] = true;
double t1 = int(i%5)*72+90;
double t2 = int(i/5)*72+90;
Vector2 b = (cos(t1),sin(t1))*2;
b.y += 8;
Vector2 n = (b.x*cos(t2)-b.y*sin(t2),b.x*sin(t2)+b.y*cos(t2));
origin = level.Vec3Offset(base,n.x*y+n.y*z);
switch ( which )
{
case 1:
// slug
if ( !sst ) sst = new("SpreadSlugTracer");
sst.ignoreme = self;
sst.penetration = 250.;
a = FRandom[Wallbuster](0,360);
s = FRandom[Wallbuster](0,.002);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
sst.hitlist.Clear();
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1+howmany/8,true);
for ( int i=0; i<(3-howmany/10); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
s.alpha *= 0.5;
}
for ( int i=0; i<(6-howmany/4); i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .8;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
s.vel += vel*.5+x*FRandom[Wallbuster](3.,5.);
}
for ( int i=0; i<(10-howmany/3); i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-1,1)+z*FRandom[Wallbuster](-1,1);
}
SWWMHandler.DoKnockback(self,-x,6000.);
A_Recoil(2.);
invoker.srecoil += -.8;
break;
case 2:
// saltshot
for ( int j=0; j<(8-howmany/5); j++ )
{
a = FRandom[Wallbuster](0,360);
s = FRandom[Wallbuster](0,.3);
let b = Spawn("SaltBeam",level.Vec3Offset(origin,y*cos(a)*s+z*sin(a)*s));
b.target = self;
b.angle = atan2(x2.y,x2.x);
b.pitch = asin(-x2.z);
b.Stamina += howmany*16;
}
for ( int i=0; i<(9-howmany/3); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,3,4)*Random[Wallbuster](32,63));
s.A_SetRenderStyle(.2,STYLE_AddShaded);
}
for ( int i=0; i<(16-howmany/2); i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.SetShade(Color(1,3,4)*Random[Wallbuster](32,63));
s.A_SetRenderStyle(.3,STYLE_AddShaded);
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);
}
SWWMHandler.DoKnockback(self,-x,4000.);
A_Recoil(1.4);
invoker.srecoil += -.5;
break;
case 3:
// lead ball
a = FRandom[Wallbuster](0,360);
s = FRandom[Wallbuster](0,.006);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let p = Spawn("TheBall",origin);
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed;
for ( int i=0; i<(4-howmany/8); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
s.alpha *= 0.4;
}
for ( int i=0; i<(8-howmany/4); i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .6;
s.alpha *= .25;
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
s.vel += vel*.5+x*FRandom[Wallbuster](3.,5.);
}
for ( int i=0; i<(8-howmany/4); i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Wallbuster](4.,8.)+y*FRandom[Wallbuster](-1,1)+z*FRandom[Wallbuster](-1,1);
}
SWWMHandler.DoKnockback(self,-x,2500.);
A_Recoil(1.);
invoker.srecoil += -.35;
break;
default:
// buckshot
if ( !st ) st = new("SpreadgunTracer");
st.ignoreme = self;
for ( int j=0; j<(30-howmany); j++ )
{
a = FRandom[Wallbuster](0,360);
s = FRandom[Wallbuster](0,.1);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
st.hitlist.Clear();
st.shootthroughlist.Clear();
st.waterhitlist.Clear();
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(st,origin,dir,6,7000,bc:5+howmany/6);
}
for ( int i=0; i<(9-howmany/3); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Wallbuster](96,192));
s.alpha *= .2;
}
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);
}
SWWMHandler.DoKnockback(self,-x,4000.);
A_Recoil(1.4);
invoker.srecoil += -.5;
break;
}
}
}
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_OpenMenu()
{
invoker.reloadqueue.Clear();
invoker.waitreload = true;
if ( player == players[consoleplayer] )
Menu.SetMenu('WallbusterReloadMenu');
}
action void A_WaitMenu()
{
if ( invoker.waitreload ) return;
if ( invoker.reloadqueue.Size() <= 0 ) player.SetPsprite(PSP_WEAPON,ResolveState("EndReload"));
else player.SetPsprite(PSP_WEAPON,ResolveState("Detach"));
}
action void A_StartDetachOverlays()
{
static const statelabel shells[] = {"DetachShell0","DetachShell1","DetachShell2","DetachShell3","DetachShell4"};
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] ) continue;
A_Overlay(PSP_WEAPON+1+i,shells[i]);
}
A_StartSound("wallbuster/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
}
action void A_StartAttachOverlays()
{
static const statelabel shells[] = {"AttachShell0","AttachShell1","AttachShell2","AttachShell3","AttachShell4"};
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] ) continue;
A_Overlay(PSP_WEAPON+1+i,shells[i]);
}
A_StartSound("wallbuster/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
}
action void A_DropShells()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
static const Class<Actor> casetypes[] = {"RedShellCasing","GreenShellCasing","BlueShellCasing","PurpleShellCasing"};
for ( int i=0; i<5; i++ )
{
Class<Ammo> loaded = invoker.loaded[invoker.rotation[5]*5+i];
int which = -1;
for ( int j=0; j<4; j++ )
{
if ( loaded != types[j] ) continue;
which = j;
break;
}
if ( which != -1 )
{
if ( invoker.fired[invoker.rotation[5]*5+i] )
{
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z);
let c = Spawn(casetypes[which],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
{
let amo = FindInventory(types[which]);
if ( !amo )
{
amo = Inventory(Spawn(types[which]));
amo.AttachToOwner(self);
amo.Amount = 0;
}
if ( amo.Amount < amo.MaxAmount ) amo.Amount++;
else if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
Spawn(types[which],Vec3Angle(5,(angle-20)+i*10,height/2));
}
}
invoker.loaded[invoker.rotation[5]*5+i] = null;
invoker.fired[invoker.rotation[5]*5+i] = false;
}
invoker.rotation[invoker.rotation[5]] = 4;
}
action void A_LoadShell()
{
if ( invoker.reloadqueue.Size() <= 0 ) return;
Class<Ammo> toload = invoker.reloadqueue[invoker.reloadqueue.Size()-1];
invoker.reloadqueue.Pop();
invoker.loaded[invoker.rotation[5]*5+invoker.rotation[invoker.rotation[5]]] = toload;
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
{
let am = FindInventory(toload);
if ( am && (am.Amount > 0) ) am.Amount--;
}
if ( (invoker.reloadqueue.Size() > 0) && (invoker.rotation[invoker.rotation[5]] > 0) )
invoker.rotation[invoker.rotation[5]]--;
A_StartSound("wallbuster/load",CHAN_WEAPON,CHANF_OVERLAP);
}
action void A_StartReloadTiming()
{
invoker.timing = true;
invoker.reloadtime[0] = level.time;
}
action void A_StopReloadTiming()
{
invoker.timing = false;
invoker.reloadtime[1] = level.time;
if ( !invoker.timeme ) invoker.timeme = CVar.GetCVar('swwm_cbttime',players[consoleplayer]);
if ( (player == players[consoleplayer]) && invoker.timeme.GetBool() )
Console.Printf("Total reload time: %gs",(invoker.reloadtime[1]-invoker.reloadtime[0])/35.);
}
Default
{
Tag "$T_WALLBUSTER";
Inventory.PickupMessage "$I_WALLBUSTER";
Obituary "$O_WALLBUSTER";
Weapon.SlotNumber 4;
Weapon.SelectionOrder 2200;
Weapon.UpSound "wallbuster/select";
Weapon.AmmoType1 "RedShell";
Weapon.AmmoGive1 25;
Stamina 35000;
+SWWMWEAPON.NOFIRSTGIVE;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Select:
XZW2 G 1 A_FullRaise();
XZW2 HIJKLMNOP 2;
Goto Ready;
Ready:
XZW2 A 1
{
int flg = WRF_ALLOWUSER1;
if ( invoker.CheckAmmo(PrimaryFire,false) ) flg |= WRF_ALLOWZOOM;
if ( invoker.CanReload() ) 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;
InvRecoil:
TNT1 A 3;
TNT1 A 1 A_Recoil(invoker.srecoil);
Stop;
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 DEFGHIJK 2;
XZW4 L 1 A_StartSound("wallbuster/unlock",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 MNOPQ 1;
XZW4 RSTUV 2;
XZW4 W 2 A_OpenMenu();
XZW4 W 1 A_WaitMenu();
Wait;
Detach:
XZW4 W 0
{
A_StartReloadTiming();
invoker.cancelreload = false;
A_Overlay(-9999,"CheckCancelReload");
}
XZW4 WXYZ 1;
XZW5 ABCDEF 1;
XZW5 G 1 A_StartSound("wallbuster/detach",CHAN_WEAPON,CHANF_OVERLAP);
DetachHold:
XZW5 H 1 A_StartDetachOverlays();
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
{
if ( invoker.cancelreload ) invoker.reloadqueue.Clear();
player.SetPSprite(-9999,null);
return A_JumpIf((invoker.reloadqueue.Size()>0),"AttachSpin");
}
Goto Attach;
CheckCancelReload:
TNT1 A 1
{
if ( player.cmd.buttons&BT_RELOAD )
invoker.cancelreload = true;
}
Wait;
DetachShell0:
XZWA EFGHIJKLMN 1;
Stop;
DetachShell1:
XZWB CDEFGHIJKL 1;
Stop;
DetachShell2:
XZWC ABCDEFGHIJ 1;
Stop;
DetachShell3:
XZWC YZ 1;
XZWD ABCDEFGH 1;
Stop;
DetachShell4:
XZWD WXYZ 1;
XZWE ABCDEF 1;
Stop;
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 A_HandSpin();
XZW8 EFGHIJKL 2;
XZW8 M 2 A_StartSound("wallbuster/detach",CHAN_WEAPON,CHANF_OVERLAP);
XZW8 N 0;
Goto DetachHold;
AttachShell0:
XZWA OPQRSTUVWXYZ 1;
XZWB AB 1;
Stop;
AttachShell1:
XZWB MNOPQRSTUVWXYZ 1;
Stop;
AttachShell2:
XZWC KLMNOPQRSTUVWX 1;
Stop;
AttachShell3:
XZWD IJKLMNOPQRSTUV 1;
Stop;
AttachShell4:
XZWE GHIJKLMNOPQRST 1;
Stop;
EndReload:
XZW4 W 2 A_StopReloadTiming();
XZW8 OPQRS 2;
XZW8 T 1 A_StartSound("wallbuster/lock",CHAN_WEAPON,CHANF_OVERLAP);
XZW8 UVWXY 1;
XZW8 Z 2 A_StartSound("wallbuster/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
XZW9 ABCDEF 2;
Goto Ready;
User1:
XZW2 A 2
{
A_StartSound("wallbuster/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW9 GHI 2;
XZW9 J 2 A_Parry(9);
XZW9 KLM 1;
XZW9 N 1 A_Melee(70,"demolitionist/whitl");
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 BCDEFG 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;
FlashGreen:
XZWX A 2 Bright;
Stop;
XZWX B 2 Bright;
Stop;
XZWX C 2 Bright;
Stop;
XZWX D 2 Bright;
Stop;
XZWX E 2 Bright;
Stop;
XZWX F 2 Bright;
Stop;
XZWX G 2 Bright;
Stop;
XZWX H 2 Bright;
Stop;
XZWX I 2 Bright;
Stop;
XZWX J 2 Bright;
Stop;
XZWX K 2 Bright;
Stop;
XZWX L 2 Bright;
Stop;
XZWX M 2 Bright;
Stop;
XZWX N 2 Bright;
Stop;
XZWX O 2 Bright;
Stop;
XZWX P 2 Bright;
Stop;
XZWX Q 2 Bright;
Stop;
XZWX R 2 Bright;
Stop;
XZWX S 2 Bright;
Stop;
XZWX T 2 Bright;
Stop;
XZWX U 2 Bright;
Stop;
XZWX V 2 Bright;
Stop;
XZWX W 2 Bright;
Stop;
XZWX X 2 Bright;
Stop;
XZWX Y 2 Bright;
Stop;
FlashBlue:
XZWY A 2 Bright;
Stop;
XZWY B 2 Bright;
Stop;
XZWY C 2 Bright;
Stop;
XZWY D 2 Bright;
Stop;
XZWY E 2 Bright;
Stop;
XZWY F 2 Bright;
Stop;
XZWY G 2 Bright;
Stop;
XZWY H 2 Bright;
Stop;
XZWY I 2 Bright;
Stop;
XZWY J 2 Bright;
Stop;
XZWY K 2 Bright;
Stop;
XZWY L 2 Bright;
Stop;
XZWY M 2 Bright;
Stop;
XZWY N 2 Bright;
Stop;
XZWY O 2 Bright;
Stop;
XZWY P 2 Bright;
Stop;
XZWY Q 2 Bright;
Stop;
XZWY R 2 Bright;
Stop;
XZWY S 2 Bright;
Stop;
XZWY T 2 Bright;
Stop;
XZWY U 2 Bright;
Stop;
XZWY V 2 Bright;
Stop;
XZWY W 2 Bright;
Stop;
XZWY X 2 Bright;
Stop;
XZWY Y 2 Bright;
Stop;
FlashPurple:
XZWZ A 2 Bright;
Stop;
XZWZ B 2 Bright;
Stop;
XZWZ C 2 Bright;
Stop;
XZWZ D 2 Bright;
Stop;
XZWZ E 2 Bright;
Stop;
XZWZ F 2 Bright;
Stop;
XZWZ G 2 Bright;
Stop;
XZWZ H 2 Bright;
Stop;
XZWZ I 2 Bright;
Stop;
XZWZ J 2 Bright;
Stop;
XZWZ K 2 Bright;
Stop;
XZWZ L 2 Bright;
Stop;
XZWZ M 2 Bright;
Stop;
XZWZ N 2 Bright;
Stop;
XZWZ O 2 Bright;
Stop;
XZWZ P 2 Bright;
Stop;
XZWZ Q 2 Bright;
Stop;
XZWZ R 2 Bright;
Stop;
XZWZ S 2 Bright;
Stop;
XZWZ T 2 Bright;
Stop;
XZWZ U 2 Bright;
Stop;
XZWZ V 2 Bright;
Stop;
XZWZ W 2 Bright;
Stop;
XZWZ X 2 Bright;
Stop;
XZWZ Y 2 Bright;
Stop;
}
}