Omnisight now handles 3d floors and midtextures, and exit line merging works properly. Reduced Pusher primary fire damage slightly. Cleared improper usages of GetAge().
2168 lines
63 KiB
Text
2168 lines
63 KiB
Text
// The Demolitionist
|
|
Class Demolitionist : PlayerPawn
|
|
{
|
|
int last_jump_held, last_boost, last_kick;
|
|
Vector3 dashdir;
|
|
double dashfuel, dashboost;
|
|
int dashcooldown, boostcooldown, fuelcooldown;
|
|
bool dashsnd;
|
|
bool sendtoground;
|
|
bool key_reentrant;
|
|
|
|
int lastdamage;
|
|
transient int lastdamagetic;
|
|
bool lastground;
|
|
int lastgroundtic;
|
|
double lastvelz, prevvelz;
|
|
double ssup;
|
|
|
|
transient CVar myvoice, mute, fly6dof, clrun;
|
|
|
|
SWWMStats mystats;
|
|
int cairtime;
|
|
|
|
int lastmpain;
|
|
|
|
double guideangle, guidepitch, guideroll;
|
|
|
|
// for weapon bobbing stuff
|
|
bool bumpdown;
|
|
double bumpvelz;
|
|
double oldangle, oldpitch;
|
|
double oldlagangle, oldlagpitch, oldlagready;
|
|
Vector3 oldlagvel;
|
|
double lagangle, lagpitch, lagready;
|
|
Vector3 lagvel;
|
|
|
|
enum EUnderType
|
|
{
|
|
UNDER_NONE,
|
|
UNDER_WATER,
|
|
UNDER_SLIME,
|
|
UNDER_LAVA
|
|
};
|
|
|
|
int lastunder;
|
|
int deadtimer;
|
|
transient int revivefail;
|
|
|
|
Actor selflight;
|
|
Actor oldencroached;
|
|
Vector3 oldencroachedpos;
|
|
|
|
Default
|
|
{
|
|
Speed 1;
|
|
Radius 16;
|
|
Height 56;
|
|
Mass 500;
|
|
PainChance 255;
|
|
Player.DisplayName "Demolitionist";
|
|
Player.StartItem "ExplodiumGun";
|
|
Player.StartItem "DeepImpact";
|
|
Player.ViewHeight 52;
|
|
Player.AirCapacity 0;
|
|
Player.GruntSpeed 20;
|
|
Player.SoundClass "demolitionist";
|
|
DamageFactor "Drowning", 0.0;
|
|
DamageFactor "Poison", 0.0;
|
|
DamageFactor "PoisonCloud", 0.0;
|
|
DamageFactor "Falling", 0.0;
|
|
+NOBLOOD;
|
|
+DONTGIB;
|
|
+NOICEDEATH;
|
|
+DONTMORPH;
|
|
+DONTDRAIN;
|
|
}
|
|
// oh yay, more cheat modification
|
|
override void CheatGive( String name, int amount )
|
|
{
|
|
if ( !player.mo || (player.health <= 0) ) return;
|
|
int giveall = ALL_NO;
|
|
if ( name ~== "all" ) giveall = ALL_YES;
|
|
else if ( name ~== "everything" ) giveall = ALL_YESYES;
|
|
if ( giveall || (name ~== "health") )
|
|
{
|
|
if ( amount > 0 )
|
|
{
|
|
health = min(health+amount,1000);
|
|
player.health = health;
|
|
}
|
|
else player.health = health = 1000;
|
|
}
|
|
if ( giveall || (name ~== "backpack") )
|
|
{
|
|
// Select the correct type of backpack based on the game
|
|
let type = (class<Inventory>)(gameinfo.backpacktype);
|
|
let def = GetDefaultByType(type);
|
|
if ( type ) GiveInventory(type,def.MaxAmount,true);
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "ammo") )
|
|
{
|
|
// Find every unique type of ammo. Give it to the player if
|
|
// he doesn't have it already, and set each to its maximum.
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (class<Ammo>)(AllActorClasses[i]);
|
|
if ( !type || (type.GetParentClass() != "Ammo") )
|
|
continue;
|
|
// Only give if it's for a valid weapon, unless using "give everything"
|
|
bool isvalid = false;
|
|
for ( int j=0; j<AllActorClasses.Size(); j++ )
|
|
{
|
|
let type2 = (class<Weapon>)(AllActorClasses[j]);
|
|
if ( !type2 ) continue;
|
|
let rep = GetReplacement(type2);
|
|
if ( (rep != type2) && !(rep is "DehackedPickup") ) continue;
|
|
readonly<Weapon> weap = GetDefaultByType(type2);
|
|
if ( !player.weapons.LocateWeapon(type2) || (weap.bCheatNotWeapon && (giveall != ALL_YESYES)) ) continue;
|
|
if ( (type2 is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(type) )
|
|
{
|
|
isvalid = true;
|
|
break;
|
|
}
|
|
if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) )
|
|
{
|
|
isvalid = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !isvalid ) continue;
|
|
let ammoitem = FindInventory(type);
|
|
if ( !ammoitem )
|
|
{
|
|
ammoitem = Inventory(Spawn(type));
|
|
ammoitem.AttachToOwner(self);
|
|
ammoitem.Amount = ammoitem.MaxAmount;
|
|
}
|
|
else if ( ammoitem.Amount < ammoitem.MaxAmount )
|
|
ammoitem.Amount = ammoitem.MaxAmount;
|
|
}
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "armor") )
|
|
{
|
|
// Give only subclasses of SWWMArmor
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (Class<SWWMArmor>)(AllActorClasses[i]);
|
|
if ( !type || (type == 'SWWMArmor') ) continue;
|
|
if ( GetReplacement(type) == type )
|
|
{
|
|
let item = Inventory(Spawn(type));
|
|
item.ClearCounters(); // don't increase item counts
|
|
item.Amount = item.MaxAmount;
|
|
if ( !item.CallTryPickup(self) ) item.Destroy();
|
|
}
|
|
}
|
|
// Also give spares
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (Class<SWWMSpareArmor>)(AllActorClasses[i]);
|
|
if ( !type || (type == 'SWWMSpareArmor') ) continue;
|
|
if ( GetReplacement(type) == type )
|
|
{
|
|
let item = Inventory(Spawn(type));
|
|
item.ClearCounters(); // don't increase item counts
|
|
item.Amount = item.MaxAmount;
|
|
if ( !item.CallTryPickup(self) ) item.Destroy();
|
|
}
|
|
}
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "keys") )
|
|
{
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
if ( !(AllActorClasses[i] is "Key") ) continue;
|
|
// clean up redundant keys
|
|
if ( !(gameinfo.gametype&GAME_Chex) && ((AllActorClasses[i] is 'ChexRedCard') || (AllActorClasses[i] is 'ChexBlueCard') || (AllActorClasses[i] is 'ChexYellowCard')) ) continue;
|
|
else if ( (gameinfo.gametype&GAME_Chex) && !(AllActorClasses[i] is 'ChexRedCard') && !(AllActorClasses[i] is 'ChexBlueCard') && !(AllActorClasses[i] is 'ChexYellowCard') ) continue;
|
|
// don't give heretic keys in doom
|
|
if ( (gameinfo.gametype&GAME_DoomChex) && ((AllActorClasses[i] is 'KeyYellow') || (AllActorClasses[i] is 'KeyGreen') || (AllActorClasses[i] is 'KeyBlue')) ) continue;
|
|
let keyitem = GetDefaultByType(AllActorClasses[i]);
|
|
if ( keyitem.special1 )
|
|
{
|
|
let item = Inventory(Spawn(AllActorClasses[i]));
|
|
SWWMHandler.KeyTagFix(item);
|
|
if ( !item.CallTryPickup(self) ) item.Destroy();
|
|
}
|
|
}
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "weapons") )
|
|
{
|
|
let savedpending = player.PendingWeapon;
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (class<Weapon>)(AllActorClasses[i]);
|
|
if ( !type || (type == "Weapon") ) continue;
|
|
// Don't give replaced weapons unless the replacement was done by Dehacked.
|
|
let rep = GetReplacement(type);
|
|
if ( (rep == type) || (rep is "DehackedPickup") )
|
|
{
|
|
// Give the weapon only if it is set in a weapon slot.
|
|
if ( !player.weapons.LocateWeapon(type) ) continue;
|
|
readonly<Weapon> def = GetDefaultByType(type);
|
|
if ( (giveall == ALL_YESYES) || !def.bCheatNotWeapon )
|
|
GiveInventory(type,1,true);
|
|
}
|
|
}
|
|
player.PendingWeapon = savedpending;
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "artifacts") )
|
|
{
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (class<Inventory>)(AllActorClasses[i]);
|
|
if ( !type ) continue;
|
|
if ( !(gameinfo.gametype&GAME_Hexen) && (type is 'AmmoFabricator') ) continue; // no fabricators outside of hexen
|
|
let def = GetDefaultByType (type);
|
|
if ( def.Icon.isValid() &&
|
|
!(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor") && !(type is "Key") )
|
|
{
|
|
// Do not give replaced items unless using "give everything"
|
|
if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
|
|
GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
|
|
}
|
|
}
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "puzzlepieces") )
|
|
{
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let type = (class<PuzzleItem>)(AllActorClasses[i]);
|
|
if ( !type ) continue;
|
|
let def = GetDefaultByType(type);
|
|
if ( !def.Icon.isValid() ) continue;
|
|
// Do not give replaced items unless using "give everything"
|
|
if ( (giveall == ALL_YESYES) || (GetReplacement(type) == type) )
|
|
GiveInventory(type,(amount<=0)?def.MaxAmount:amount,true);
|
|
}
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall ) return;
|
|
let type = name;
|
|
if ( !type )
|
|
{
|
|
if ( PlayerNumber() == consoleplayer )
|
|
A_Log(String.Format("Unknown item \"%s\"\n",name));
|
|
}
|
|
else GiveInventory(type,amount,true);
|
|
}
|
|
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
|
|
{
|
|
if ( mod == 'Jump' ) return StringTable.Localize("$O_JUMP");
|
|
if ( mod == 'Dash' ) return StringTable.Localize("$O_DASH");
|
|
if ( mod == 'GroundPound' ) return StringTable.Localize("$O_POUND");
|
|
return Super.GetObituary(victim,inflictor,mod,playerattack);
|
|
}
|
|
override void GiveDefaultInventory()
|
|
{
|
|
Super.GiveDefaultInventory();
|
|
// preloaded gun
|
|
let dp = DeepImpact(FindInventory("DeepImpact"));
|
|
if ( dp ) SWWMLoreLibrary.Add(player,"DeepImpact");
|
|
let eg = ExplodiumGun(FindInventory("ExplodiumGun"));
|
|
if ( !eg ) return;
|
|
SWWMLoreLibrary.Add(player,"ExplodiumGun");
|
|
eg.clipcount = 7;
|
|
eg.chambered = true;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
mystats = SWWMStats.Find(player);
|
|
lastground = true;
|
|
}
|
|
void A_Dash()
|
|
{
|
|
vel += dashdir*dashboost;
|
|
player.vel = vel.xy;
|
|
if ( dashboost < 0.1 ) dashboost = 0.;
|
|
else
|
|
{
|
|
if ( swwm_extraalert ) A_AlertMonsters(800);
|
|
dashboost *= (player.cmd.buttons&BT_USER2)?.8:.1;
|
|
}
|
|
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
|
|
dashfuel = max(0.,dashfuel-dashboost);
|
|
dashcooldown = min(40,max(10,int(dashcooldown*1.4)));
|
|
fuelcooldown = max(60,fuelcooldown);
|
|
if ( (dashfuel <= 0.) || (dashboost <= 0.) )
|
|
SetStateLabel("DashEnd");
|
|
}
|
|
void A_BoostUp( bool initial = false )
|
|
{
|
|
vel += (0,0,1)*dashboost;
|
|
player.vel = vel.xy;
|
|
if ( dashboost < 0.1 ) dashboost = 0.;
|
|
else
|
|
{
|
|
if ( swwm_extraalert ) A_AlertMonsters(800);
|
|
dashboost *= (player.cmd.buttons&BT_JUMP)?.95:.4;
|
|
last_boost = level.maptime+1;
|
|
}
|
|
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
|
|
dashfuel = max(0.,dashfuel-dashboost);
|
|
if ( ((dashfuel <= 0.) || (dashboost <= 0.)) )
|
|
{
|
|
if ( !initial )
|
|
{
|
|
if ( player.onground ) SetStateLabel("JumpEnd");
|
|
else SetStateLabel("Fall");
|
|
}
|
|
return;
|
|
}
|
|
fuelcooldown = max(20,fuelcooldown);
|
|
}
|
|
void CheckUnderwaterAmb( bool restore = false )
|
|
{
|
|
Vector3 headpos = Vec3Offset(0,0,player.viewheight);
|
|
Vector3 centerpos = Vec3Offset(0,0,height/2);
|
|
Sector headregion = null;
|
|
if ( CurSector.moreflags&Sector.SECMF_UNDERWATER ) // check underwater sector
|
|
headregion = CurSector;
|
|
else if ( CurSector.heightsec ) // check height transfer
|
|
{
|
|
let hsec = CurSector.heightsec;
|
|
double fh = hsec.floorplane.ZAtPoint(pos.xy);
|
|
if ( pos.z < fh )
|
|
{
|
|
if ( headpos.z <= fh )
|
|
headregion = hsec;
|
|
}
|
|
else if ( !(hsec.moreflags&Sector.SECMF_FAKEFLOORONLY) && (headpos.z > hsec.ceilingplane.ZAtPoint(pos.xy)) )
|
|
headregion = hsec;
|
|
}
|
|
else // check 3D floors
|
|
{
|
|
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
let ff = CurSector.Get3DFloor(i);
|
|
if ( !(ff.flags&F3DFloor.FF_EXISTS) || (ff.flags&F3DFloor.FF_SOLID) || (!(ff.flags&F3DFloor.FF_SWIMMABLE) && (ff.alpha == 0)) ) continue;
|
|
double ff_bottom = ff.bottom.ZAtPoint(pos.xy);
|
|
double ff_top = ff.top.ZAtPoint(pos.xy);
|
|
if ( (ff_top <= pos.z) || (ff_bottom > centerpos.z) ) continue;
|
|
if ( headpos.z <= ff_top )
|
|
{
|
|
headregion = ff.model;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
int curunder = UNDER_NONE;
|
|
if ( headregion )
|
|
{
|
|
switch ( headregion.damagetype )
|
|
{
|
|
case 'Fire':
|
|
case 'Lava':
|
|
curunder = UNDER_LAVA;
|
|
break;
|
|
case 'Slime':
|
|
case 'Poison':
|
|
case 'PoisonCloud':
|
|
curunder = UNDER_SLIME;
|
|
break;
|
|
case 'Ice':
|
|
case 'Drowning':
|
|
default:
|
|
curunder = UNDER_WATER;
|
|
break;
|
|
}
|
|
}
|
|
if ( (curunder != lastunder) || restore )
|
|
{
|
|
static const string undersnd[] = {"","misc/underwater","misc/underslime","misc/underlava"};
|
|
static const string entersnd[] = {"","misc/waterenter","misc/slimeenter","misc/lavaenter"};
|
|
static const string exitsnd[] = {"","misc/waterexit","misc/slimeexit","misc/lavaexit"};
|
|
A_StopSound(CHAN_AMBEXTRA);
|
|
if ( curunder > UNDER_NONE )
|
|
{
|
|
A_StartSound(undersnd[curunder],CHAN_AMBEXTRA,CHANF_LOOPING|CHANF_UI);
|
|
if ( !restore && (players[consoleplayer].Camera == self) )
|
|
A_StartSound(entersnd[curunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI);
|
|
}
|
|
if ( !restore && (lastunder > UNDER_NONE) && (players[consoleplayer].Camera == self) )
|
|
A_StartSound(exitsnd[lastunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI);
|
|
}
|
|
if ( curunder > UNDER_NONE )
|
|
A_SoundVolume(CHAN_AMBEXTRA,(players[consoleplayer].Camera==self)?1.:0.);
|
|
lastunder = curunder;
|
|
}
|
|
override Vector2 BobWeapon( double ticfrac )
|
|
{
|
|
// non-mod weapons bob normally
|
|
if ( !(player.ReadyWeapon is 'SWWMWeapon') )
|
|
return Super.BobWeapon(ticfrac);
|
|
bool oldbob = !!(player.WeaponState&WF_WEAPONBOBBING);
|
|
player.WeaponState |= WF_WEAPONBOBBING; // always bob
|
|
Vector2 cur = Super.BobWeapon(ticfrac);
|
|
if ( !oldbob ) player.WeaponState &= ~WF_WEAPONBOBBING;
|
|
double fangle = oldangle*(1.-ticfrac)+angle*ticfrac;
|
|
double fpitch = (oldpitch*(1.-ticfrac)+pitch*ticfrac);
|
|
double flagangle = (oldlagangle*(1.-ticfrac)+lagangle*ticfrac);
|
|
double flagpitch = (oldlagpitch*(1.-ticfrac)+lagpitch*ticfrac);
|
|
double diffang = fangle-flagangle;
|
|
double diffpitch = fpitch-flagpitch;
|
|
if ( abs(diffang) > 1. )
|
|
{
|
|
int sgn = (diffang>0)?1:-1;
|
|
diffang = abs(diffang)**.7*sgn;
|
|
}
|
|
if ( abs(diffpitch) > 1. )
|
|
{
|
|
int sgn = (diffpitch>0)?1:-1;
|
|
diffpitch = abs(diffpitch)**.7*sgn;
|
|
}
|
|
Vector3 flagvel = oldlagvel*(1.-ticfrac)+lagvel*ticfrac;
|
|
double diffx = flagvel dot (cos(flagangle+90),sin(flagangle+90),0);
|
|
double diffy = flagvel dot (0,0,1);
|
|
if ( abs(diffx) > 1. )
|
|
{
|
|
int sgn = (diffx>0)?1:-1;
|
|
diffx = abs(diffx)**.5*sgn;
|
|
}
|
|
if ( abs(diffy) > 1. )
|
|
{
|
|
int sgn = (diffy>0)?1:-1;
|
|
diffy = abs(diffy)**.5*sgn;
|
|
}
|
|
// don't do inertial sway when in 6dof mode, causes issues
|
|
if ( !fly6dof ) fly6dof = CVar.GetCVar('swwm_fly6dof',players[consoleplayer]);
|
|
if ( !fly6dof.GetBool() || !((waterlevel < 2) && (bFly || bFlyCheat) && !(player.cheats&CF_NOCLIP2)) )
|
|
{
|
|
cur.x += diffang;
|
|
cur.y -= diffpitch;
|
|
cur.x += diffx*4.;
|
|
cur.y += diffy*4.;
|
|
}
|
|
return cur*(oldlagready*(1.-ticfrac)+lagready*ticfrac);
|
|
}
|
|
override void PlayerThink()
|
|
{
|
|
oldangle = angle;
|
|
oldpitch = pitch;
|
|
Super.PlayerThink();
|
|
oldlagangle = lagangle;
|
|
oldlagpitch = lagpitch;
|
|
lagangle = lagangle*.8+angle*.2;
|
|
lagpitch = lagpitch*.8+pitch*.2;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !player ) return;
|
|
if ( !selflight )
|
|
{
|
|
selflight = Spawn("DemolitionistSelfLight",pos);
|
|
selflight.target = self;
|
|
selflight.Tick();
|
|
}
|
|
oldlagvel = lagvel;
|
|
oldlagready = lagready;
|
|
if ( player.weaponstate&WF_WEAPONBOBBING ) lagready = lagready*.9+.1;
|
|
else lagready = lagready*.4;
|
|
lagvel = lagvel*.8+vel*.2;
|
|
double traveldist = level.Vec3Diff(prev,pos).length();
|
|
if ( waterlevel < 2 )
|
|
{
|
|
if ( !player.onground || bNoGravity )
|
|
{
|
|
cairtime++;
|
|
if ( cairtime > mystats.airtime ) mystats.airtime = cairtime;
|
|
mystats.airdist += traveldist;
|
|
}
|
|
else
|
|
{
|
|
cairtime = 0;
|
|
mystats.grounddist += traveldist;
|
|
}
|
|
}
|
|
CheckUnderwaterAmb();
|
|
if ( vel.length() > mystats.topspeed ) mystats.topspeed = vel.length();
|
|
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) && (health > 0) )
|
|
{
|
|
// bump down weapon
|
|
bumpdown = true;
|
|
bumpvelz = -lastvelz;
|
|
if ( lastvelz < -25 )
|
|
{
|
|
let s = Spawn("DemolitionistShockwave",pos);
|
|
s.target = self;
|
|
s.special1 = int(-lastvelz);
|
|
ReactionTime = 17;
|
|
A_Stop();
|
|
A_AlertMonsters(2500);
|
|
if ( player == players[consoleplayer] )
|
|
{
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4);
|
|
}
|
|
mystats.stompcount++;
|
|
}
|
|
if ( lastvelz < -10 ) A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
if ( (player == players[consoleplayer]) && (lastvelz < -gruntspeed) && (mute.GetInt() < 4) )
|
|
A_StartSound(String.Format("voice/%s/grunt",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
|
|
if ( lastvelz < -1 )
|
|
A_Footstep(0,true,clamp(-lastvelz*0.05,0.0,1.0));
|
|
}
|
|
// crush anything we're standing on
|
|
bool dummy;
|
|
Actor encroached;
|
|
[dummy, encroached] = TestMobjZ();
|
|
if ( encroached )
|
|
{
|
|
// try to follow movement (this method is awkward but works with monsters)
|
|
if ( encroached == oldencroached )
|
|
SetOrigin(level.Vec3Offset(pos,level.Vec3Diff(oldencroachedpos,encroached.pos)),true);
|
|
oldencroached = encroached;
|
|
oldencroachedpos = encroached.pos;
|
|
}
|
|
else
|
|
{
|
|
// launch in movement direction (useful for moving platforms)
|
|
if ( oldencroached && (dashboost <= 0.) && (lastvelz >= -25) ) vel += oldencroached.vel+level.Vec3Diff(oldencroachedpos,oldencroached.pos);
|
|
oldencroached = null;
|
|
}
|
|
if ( encroached && encroached.bSHOOTABLE && (lastvelz <= 0) && !encroached.player )
|
|
{
|
|
if ( (lastvelz < -5) || !(level.maptime%5) )
|
|
{
|
|
int realdmg = encroached.DamageMobj(self,self,int((2+max(0,-lastvelz*3))*max(1.,mass/encroached.mass)),'Jump',DMG_THRUSTLESS);
|
|
if ( FindInventory("RagekitPower") )
|
|
{
|
|
let ps = Spawn("BigPunchSplash",pos);
|
|
ps.damagetype = 'Jump';
|
|
ps.target = self;
|
|
ps.special1 = realdmg;
|
|
}
|
|
if ( !encroached.bNOBLOOD && !encroached.bINVULNERABLE )
|
|
{
|
|
encroached.TraceBleed(realdmg,self);
|
|
encroached.SpawnBlood(pos,angle,realdmg);
|
|
}
|
|
}
|
|
}
|
|
if ( bumpdown )
|
|
{
|
|
lagvel.z += bumpvelz*.2;
|
|
bumpvelz *= .8;
|
|
if ( bumpvelz < double.epsilon ) bumpdown = false;
|
|
}
|
|
lastground = player.onground;
|
|
lastvelz = prevvelz;
|
|
prevvelz = vel.z;
|
|
bNOFRICTION = (((waterlevel<2)&&(bFly||bFlyCheat&&!(player.cheats&CF_NOCLIP2)))||InStateSequence(CurState,FindState("Dash")));
|
|
fuelcooldown = max(0,fuelcooldown-1);
|
|
dashcooldown = max(0,dashcooldown-1);
|
|
boostcooldown = max(0,boostcooldown-1);
|
|
if ( fuelcooldown <= 0 )
|
|
dashfuel = min(240.,dashfuel+max(dashfuel*.01,.05));
|
|
if ( (dashboost > 0.) && (InStateSequence(CurState,FindState("Dash")) || (InStateSequence(CurState,FindState("Jump")) && player.cmd.buttons&BT_JUMP)) )
|
|
dashsnd = true;
|
|
else
|
|
{
|
|
if ( dashsnd )
|
|
A_StartSound("demolitionist/jetstop",CHAN_JETPACK);
|
|
dashsnd = false;
|
|
}
|
|
PainChance = InStateSequence(CurState,FindState("Dash"))?0:255;
|
|
if ( dashboost <= 0. ) return;
|
|
if ( InStateSequence(CurState,FindState("Dash")) )
|
|
{
|
|
Actor a;
|
|
for ( int i=-1; i<=1; i+=2 ) for ( int j=1; j<4; j++ )
|
|
{
|
|
a = Spawn("DashTrail",Vec3Angle(15,angle+i*140,35));
|
|
a.target = self;
|
|
a.vel = (RotateVector((j,0),angle+i*160),0)-(0,0,1)*j;
|
|
a.vel -= vel*.5;
|
|
}
|
|
Vector3 dir = vel+dashdir*dashboost;
|
|
double spd = dir.length();
|
|
dir = dir.unit();
|
|
Vector3 viewdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
// look for things we could potentially bump into
|
|
let bi = BlockThingsIterator.Create(self,500);
|
|
bool bumped = false;
|
|
while ( (spd > 0) && bi.Next() )
|
|
{
|
|
a = bi.Thing;
|
|
if ( !a || (a == self) || !a.bSHOOTABLE || a.bTHRUACTORS ) continue;
|
|
Vector3 diff = level.Vec3Diff(pos,a.pos);
|
|
if ( !SWWMUtility.ExtrudeIntersect(self,a,dir*spd,8,16) ) continue;
|
|
if ( !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
|
|
// large monsters will stop the player (unless hit from above if we're going at ground pound speed)
|
|
A_QuakeEx(4,4,4,10,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
|
|
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
|
|
a.A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
|
|
if ( (diff.z < a.height) && (lastvelz >= -25) && (a.bDONTTHRUST || (a.Mass >= Mass*1.5)) )
|
|
{
|
|
if ( bumped ) continue;
|
|
bumped = true;
|
|
A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
|
|
vel *= .4;
|
|
vel -= dir*(15+(spd*40/mass));
|
|
vel.z += 5+(spd*(10/mass));
|
|
dashboost *= .3;
|
|
}
|
|
Vector3 pushdir = Vec3To(a).unit()*.1+dir*.9;
|
|
if ( !a.bDONTTHRUST && (a.Mass < Mass*1.5) )
|
|
{
|
|
a.vel += pushdir*(15+(spd*20/max(50,a.mass)));
|
|
a.vel.z += 5+(spd*(5/max(50,a.mass)));
|
|
}
|
|
int flg = DMG_THRUSTLESS;
|
|
if ( FindInventory("RagekitPower") ) flg |= DMG_FOILINVUL;
|
|
if ( !a.player ) a.bBLASTED = true;
|
|
int dmg = int(15+spd*2.5);
|
|
if ( dir dot viewdir < -.3 ) dmg *= 2; // BUTTSLAM
|
|
dmg = a.DamageMobj(self,self,dmg,'Dash',flg);
|
|
if ( FindInventory("RagekitPower") )
|
|
{
|
|
let ps = Spawn("BigPunchSplash",level.Vec3Offset(pos,diff/2));
|
|
ps.damagetype = 'Dash';
|
|
ps.target = self;
|
|
ps.special1 = dmg;
|
|
}
|
|
}
|
|
}
|
|
else if ( InStateSequence(CurState,FindState("Jump")) )
|
|
{
|
|
Actor a;
|
|
for ( int i=-1; i<=1; i+=2 ) for ( int j=1; j<4; j++ )
|
|
{
|
|
a = Spawn("DashTrail",Vec3Angle(10,angle+i*140,40));
|
|
a.target = self;
|
|
a.vel = .5*(RotateVector((j,0),angle+i*160),0)-(0,0,1)*j;
|
|
a.vel -= vel*.5;
|
|
}
|
|
}
|
|
}
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
// lucky collar
|
|
if ( source == self ) damage /= 2;
|
|
if ( Health-damage < 25 )
|
|
{
|
|
int splitdmg[2];
|
|
splitdmg[0] = max(0,Health-25); // non-reduced part (>=25% health)
|
|
splitdmg[1] = (damage-splitdmg[0])/4; // reduced part (<25% health)
|
|
damage = splitdmg[0]+splitdmg[1];
|
|
}
|
|
if ( !inflictor && !source && (CurSector.flags&Sector.SECF_ENDLEVEL) )
|
|
{
|
|
// end level hax
|
|
damage = max(50,health-100);
|
|
flags |= DMG_FORCED|DMG_NO_ARMOR;
|
|
mod = 'EndLevel';
|
|
}
|
|
lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
|
lastdamagetic = max(lastdamagetic,gametic+clamp(lastdamage/2,10,40));
|
|
if ( (lastdamage > 0) && (PainChance == 0) && (level.maptime>lastmpain) )
|
|
{
|
|
lastmpain = level.maptime;
|
|
A_DemoPain();
|
|
}
|
|
return lastdamage;
|
|
}
|
|
override void CalcHeight()
|
|
{
|
|
Super.CalcHeight();
|
|
// handle smooth step down (hacky but looks ok)
|
|
player.viewz += ssup;
|
|
ssup = max(0,(ssup*.7)-.25);
|
|
}
|
|
override void CheckPitch()
|
|
{
|
|
if ( (waterlevel < 2) && (bFly || bFlyCheat) && !(player.cheats&CF_NOCLIP2) )
|
|
return; // handled in moveplayer
|
|
Super.CheckPitch();
|
|
}
|
|
override void CheckMoveUpDown()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash")) )
|
|
player.cmd.upmove = 0;
|
|
if ( (waterlevel < 2) && (bFly || bFlyCheat) && !(player.cheats&CF_NOCLIP2) )
|
|
{
|
|
double fs = TweakSpeeds(1.,0.);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 accel;
|
|
if ( (player.cmd.upmove == -32768) || sendtoground )
|
|
{
|
|
sendtoground = true;
|
|
player.centering = true;
|
|
accel = (0,0,-4096);
|
|
}
|
|
else accel = z*player.cmd.upmove*8.;
|
|
accel *= fs/128.;
|
|
vel = vel+accel/TICRATE;
|
|
if ( sendtoground ) vel.xy *= .6;
|
|
if ( (pos.z <= floorz) || bOnMobj ) sendtoground = false;
|
|
if ( vel.length() > 50. )
|
|
vel = vel.unit()*50.;
|
|
return;
|
|
}
|
|
else sendtoground = false;
|
|
Super.CheckMoveUpDown();
|
|
}
|
|
override void MovePlayer()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash")) )
|
|
player.cmd.forwardmove = player.cmd.sidemove = 0;
|
|
if ( (waterlevel < 2) && (bFly || bFlyCheat) && !(player.cheats&CF_NOCLIP2) )
|
|
{
|
|
player.onground = false;
|
|
if ( player.turnticks )
|
|
{
|
|
player.turnticks--;
|
|
guideangle = (180./TURN180_TICKS);
|
|
}
|
|
else guideangle += .2*player.cmd.yaw*(360./65536.);
|
|
guidepitch -= .2*player.cmd.pitch*(360./65536.);
|
|
if ( !fly6dof ) fly6dof = CVar.GetCVar('swwm_fly6dof',players[consoleplayer]);
|
|
if ( player.centering ) guidepitch = clamp(deltaangle(pitch,0),-3.,3.);
|
|
if ( player.centering || !fly6dof.GetBool() ) guideroll = clamp(deltaangle(roll,0),-3.,3.);
|
|
if ( fly6dof.GetBool() )
|
|
{
|
|
swwm_Quat orient = swwm_Quat.create_euler(pitch,angle,roll);
|
|
swwm_Quat angles = swwm_Quat.create_euler(guidepitch,guideangle,guideroll);
|
|
orient = orient.qmul(angles);
|
|
double npitch, nangle, nroll;
|
|
[npitch, nangle, nroll] = orient.to_euler();
|
|
angle = nangle;
|
|
pitch = npitch;
|
|
roll = nroll;
|
|
}
|
|
else
|
|
{
|
|
angle += guideangle;
|
|
pitch += guidepitch;
|
|
pitch = clamp(pitch,player.MinPitch,player.MaxPitch);
|
|
roll += guideroll;
|
|
}
|
|
if ( (abs(roll) <= 1./65536.) && (abs(pitch) <= 1./65536.) )
|
|
{
|
|
guideroll = guidepitch = roll = pitch = 0.;
|
|
player.centering = false;
|
|
}
|
|
double fs = TweakSpeeds(1.,0.);
|
|
double jcmove = 0.;
|
|
if ( player.cmd.buttons&BT_JUMP ) jcmove += 4096.;
|
|
if ( player.cmd.buttons&BT_CROUCH ) jcmove -= 4096.;
|
|
if ( FastCheck() ) jcmove *= 2.;
|
|
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 accel = x*player.cmd.forwardmove+y*player.cmd.sidemove+z*jcmove;
|
|
accel *= fs/320.;
|
|
double spd = vel.length();
|
|
if ( spd > 40. ) vel = (vel+accel/TICRATE).unit()*spd;
|
|
else vel = vel+accel/TICRATE;
|
|
vel *= .97;
|
|
player.vel = (1,1)*vel.length();
|
|
player.jumptics = -2;
|
|
if ( !(player.cheats & CF_PREDICTING) && (player.cmd.forwardmove|player.cmd.sidemove) )
|
|
PlayRunning();
|
|
if ( player.cheats&CF_REVERTPLEASE )
|
|
{
|
|
player.cheats &= ~CF_REVERTPLEASE;
|
|
player.camera = player.mo;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Super.MovePlayer();
|
|
// override air control because we REALLY need the extra mobility
|
|
if ( !player.onground && !bNOGRAVITY && (waterlevel < 2) )
|
|
{
|
|
double fs = TweakSpeeds(1.,0.);
|
|
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
|
|
Vector3 accel = (RotateVector((player.cmd.forwardmove,-player.cmd.sidemove),angle),0);
|
|
accel *= fs/512.;
|
|
double spd = vel.length();
|
|
if ( spd > 10. ) vel = (vel+accel/TICRATE).unit()*spd;
|
|
else vel = vel+accel/TICRATE;
|
|
}
|
|
if ( abs(roll) > 0. ) roll += clamp(deltaangle(roll,0),-3.,3.);
|
|
}
|
|
guideangle *= .9;
|
|
guidepitch *= .9;
|
|
guideroll *= .9;
|
|
// anchor to ground when going down steps
|
|
if ( lastground && !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) )
|
|
{
|
|
ssup = max(0,(pos.z-floorz));
|
|
SetOrigin(Vec2OffsetZ(0,0,floorz),true);
|
|
lastground = player.onground = true;
|
|
}
|
|
if ( player.onground ) lastgroundtic = level.maptime;
|
|
if ( !(player.cheats & CF_PREDICTING) && !(player.cmd.forwardmove|player.cmd.sidemove) )
|
|
PlayIdle();
|
|
Vector3 dodge = (0,0,0), x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
|
int fm = player.cmd.forwardmove;
|
|
int sm = player.cmd.sidemove;
|
|
if ( !(fm|sm) ) fm = 1;
|
|
if ( fm ) dodge += (fm>0)?X:-X;
|
|
if ( sm ) dodge += (sm>0)?Y:-Y;
|
|
if ( player.cmd.buttons&BT_CROUCH ) dodge = (0,0,-1); // death from above
|
|
if ( player.onground )
|
|
{
|
|
if ( !level.IsJumpingAllowed() ) dodge.z = 0;
|
|
else dodge.z = max(0,dodge.z);
|
|
}
|
|
if ( dodge == (0,0,0) ) dodge.xy = RotateVector((1,0),angle);
|
|
if ( (waterlevel < 2) && !(bFly || bFlyCheat) )
|
|
dodge.z += (player.cmd.buttons&BT_CROUCH)?.05:.15;
|
|
if ( (dodge.length() > 0) && (dashcooldown <= 0) && (dashfuel > 20.) && player.cmd.buttons&BT_USER2 && (player.onground || level.IsJumpingAllowed()) )
|
|
{
|
|
dashdir = dodge.unit();
|
|
dashcooldown = 10;
|
|
dashboost = 20.;
|
|
bOnMobj = false;
|
|
if ( player.cheats & CF_REVERTPLEASE )
|
|
{
|
|
player.cheats &= ~CF_REVERTPLEASE;
|
|
player.camera = player.mo;
|
|
}
|
|
vel.z += clamp(-vel.z*.4,0.,30.);
|
|
player.jumptics = -1;
|
|
SetStateLabel("Dash");
|
|
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
|
|
mystats.dashcount++;
|
|
}
|
|
}
|
|
override void CheckJump()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash")) ) return; // do not
|
|
Vector2 walldir = (cos(angle),sin(angle));
|
|
bool walljump = false, wallclimb = false;
|
|
FLineTraceData d;
|
|
Actor jumpactor = null;
|
|
for ( int i=-4; i<int(height*.8); i++ )
|
|
{
|
|
if ( !wallclimb )
|
|
{
|
|
for ( int k=0; k<30; k+=10 )
|
|
{
|
|
for ( int j=-1; j<=1; j+=2 )
|
|
{
|
|
double ang = angle+k*j;
|
|
wallclimb = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
|
|
if ( wallclimb )
|
|
{
|
|
jumpactor = d.HitActor;
|
|
walldir = (walldir*.7+(cos(ang),sin(ang))*.3);
|
|
break;
|
|
}
|
|
}
|
|
if ( wallclimb ) break;
|
|
}
|
|
if ( wallclimb ) break;
|
|
}
|
|
if ( wallclimb ) break;
|
|
if ( !walljump )
|
|
{
|
|
for ( int k=0; k<60; k+=10 )
|
|
{
|
|
for ( int j=-1; j<=1; j+=2 )
|
|
{
|
|
double ang = (angle-180)+k*j;
|
|
walljump = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
|
|
if ( walljump )
|
|
{
|
|
jumpactor = d.HitActor;
|
|
walldir = (walldir*.7+(cos(ang+180),sin(ang+180))*.3);
|
|
break;
|
|
}
|
|
}
|
|
if ( walljump ) break;
|
|
}
|
|
if ( walljump ) break;
|
|
}
|
|
if ( walljump ) break;
|
|
}
|
|
if ( player.cmd.buttons&BT_JUMP )
|
|
{
|
|
// cooldown before we can do these, avoids accidental walljumps off ledges we just fell off from
|
|
if ( level.maptime < (lastgroundtic+4) )
|
|
{
|
|
walljump = false;
|
|
wallclimb = false;
|
|
jumpactor = null;
|
|
}
|
|
double mult = FastCheck()?2.:1.;
|
|
if ( player.crouchoffset ) player.crouching = 1;
|
|
else if ( waterlevel >= 2 ) vel.z = 4*Speed*mult;
|
|
else if ( (waterlevel < 2) && (bFly || bFlyCheat) && !(player.cheats&CF_NOCLIP2) ) return;
|
|
else if ( bNoGravity ) vel.z = 3*mult;
|
|
else if ( level.IsJumpingAllowed()
|
|
&& ((player.onground && (player.jumptics == 0))
|
|
|| (!player.onground && (level.maptime > last_jump_held) && (((dashfuel > 10.) && (boostcooldown <= 0)) || walljump || wallclimb))) )
|
|
{
|
|
if ( !player.onground && (((walljump || wallclimb) && (level.maptime < last_kick+8)) || (!(walljump || wallclimb) && (level.maptime < last_boost+8))) )
|
|
return;
|
|
double jumpvelz = JumpZ*35./TICRATE;
|
|
double jumpfac = 0;
|
|
for ( let p=Inv; p; p=p.Inv )
|
|
{
|
|
let pp = PowerHighJump(p);
|
|
if ( pp )
|
|
{
|
|
double f = pp.Strength;
|
|
if ( f > jumpfac ) jumpfac = f;
|
|
}
|
|
}
|
|
if ( jumpfac > 0 ) jumpvelz *= jumpfac;
|
|
if ( FindInventory("RagekitPower") ) jumpvelz *= 2.;
|
|
double pvelz = vel.z;
|
|
if ( !player.onground && !(player.cheats&CF_PREDICTING) )
|
|
{
|
|
// check for wall stuff
|
|
if ( walljump )
|
|
{
|
|
if ( vel.z < 10. )
|
|
vel.z += jumpvelz+clamp(-pvelz*.6,0.,30.);
|
|
vel.xy += walldir*20*Speed;
|
|
}
|
|
else if ( wallclimb )
|
|
{
|
|
if ( vel.z < 10. )
|
|
vel.z += jumpvelz+clamp(-pvelz*.8,0.,30.);
|
|
vel.xy += walldir*5*Speed;
|
|
}
|
|
if ( jumpactor && jumpactor.bSHOOTABLE )
|
|
{
|
|
SWWMHandler.DoKnockback(jumpactor,(-walldir,0),12000);
|
|
int dmg = jumpactor.DamageMobj(self,self,10,'Jump');
|
|
if ( FindInventory("RagekitPower") )
|
|
{
|
|
let ps = Spawn("BigPunchSplash",pos);
|
|
ps.damagetype = 'Jump';
|
|
ps.target = self;
|
|
ps.special1 = dmg;
|
|
}
|
|
}
|
|
if ( walljump || wallclimb )
|
|
{
|
|
A_StartSound("demolitionist/kick",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
last_kick = level.maptime+1;
|
|
}
|
|
}
|
|
bOnMobj = false;
|
|
player.jumpTics = -1;
|
|
if ( !(player.cheats&CF_PREDICTING) )
|
|
A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
if ( (dashfuel > 10.) && !player.onground && !walljump && !wallclimb )
|
|
{
|
|
dashboost = 3.;
|
|
boostcooldown = 20;
|
|
if ( vel.z < 10. )
|
|
vel.z += jumpvelz+clamp(-pvelz*.4,0.,30.);
|
|
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
|
|
mystats.boostcount++;
|
|
last_boost = level.maptime+1;
|
|
}
|
|
else
|
|
{
|
|
dashboost = 0.;
|
|
if ( vel.z < 10. )
|
|
vel.z += jumpvelz*1.25;
|
|
}
|
|
SetStateLabel("Jump");
|
|
}
|
|
last_jump_held = level.maptime+1;
|
|
}
|
|
}
|
|
override void DeathThink()
|
|
{
|
|
player.Uncrouch();
|
|
TickPSprites();
|
|
player.onground = (pos.Z<=floorz);
|
|
// ded (demo-chan falls faster tho)
|
|
player.deltaviewheight = 0;
|
|
if ( player.viewheight > 6 ) player.viewheight -= 3;
|
|
if ( player.viewheight < 6 ) player.viewheight = 6;
|
|
// center pitch
|
|
double dpitch = clamp(deltaangle(pitch,0),-6,6);
|
|
if ( abs(dpitch) < 3. ) pitch = 0.;
|
|
else pitch += dpitch;
|
|
// add roll
|
|
double droll = clamp(deltaangle(roll,50)*.5,-5,5);
|
|
if ( abs(droll) < 2. ) roll = 50.;
|
|
else roll += droll;
|
|
player.mo.CalcHeight();
|
|
if ( player.damagecount ) player.damagecount--;
|
|
if ( player.poisoncount ) player.poisoncount--;
|
|
if ( player.viewheight <= 6 )
|
|
{
|
|
deadtimer++;
|
|
if ( (deadtimer == 60) && (player == players[consoleplayer]) )
|
|
A_StartSound("demolitionist/youdied",CHAN_DEMOVOICE,CHANF_OVERLAP|CHANF_UI);
|
|
if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) )
|
|
{
|
|
// standard behaviour, respawn normally
|
|
if ( (((player.cmd.buttons&BT_USE) && !player.Bot) || ((deathmatch || alwaysapplydmflags) && sv_forcerespawn && (Level.maptime >= player.respawn_time))) && !sv_norespawn )
|
|
{
|
|
player.cls = null;
|
|
player.playerstate = PST_REBORN;
|
|
if ( special1 > 2 ) special1 = 0;
|
|
}
|
|
}
|
|
else if ( (player.cmd.buttons&BT_USE) && (deadtimer > 120) )
|
|
{
|
|
// gross hackfix for library "vanishing"
|
|
SWWMExcuseMeWhatTheFuckHandler.ThisIsSomeSeriousBullshit();
|
|
// reload save
|
|
player.cls = null;
|
|
player.playerstate = PST_ENTER;
|
|
if ( special1 > 2 ) special1 = 0;
|
|
}
|
|
else if ( (player.cmd.buttons&BT_ATTACK) && (deadtimer > 120) && swwm_revive )
|
|
{
|
|
// reboot (if possible)
|
|
if ( !FindInventory("ReviveCooldown") )
|
|
{
|
|
player.Resurrect();
|
|
SetState(FindState("Spawn")+1); // skip tweening
|
|
roll = 0;
|
|
let s = Spawn("DemolitionistShockwave",pos);
|
|
s.target = self;
|
|
s.special1 = 30;
|
|
ReactionTime = 17;
|
|
A_Stop();
|
|
A_AlertMonsters(2500);
|
|
if ( player == players[consoleplayer] )
|
|
{
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4);
|
|
}
|
|
SWWMHandler.DoFlash(self,Color(255,255,255,255),10);
|
|
SWWMHandler.DoFlash(self,Color(255,128,192,255),30);
|
|
SWWMScoreObj.Spawn(default.health,Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Height/2),Font.CR_BLUE);
|
|
if ( special1 > 2 ) special1 = 0;
|
|
if ( swwm_revivecooldown > 0 )
|
|
GiveInventory("ReviveCooldown",1);
|
|
}
|
|
else if ( level.maptime > revivefail )
|
|
{
|
|
if ( player == players[consoleplayer] ) A_StartSound("menu/fail",CHAN_ITEM,CHANF_UI);
|
|
revivefail = level.maptime+120;
|
|
}
|
|
}
|
|
}
|
|
else deadtimer = 0;
|
|
}
|
|
override void PlayIdle()
|
|
{
|
|
if ( !player )
|
|
{
|
|
if ( !InStateSequence(CurState,FindState("Spawn")) )
|
|
SetStateLabel("Spawn");
|
|
return;
|
|
}
|
|
if ( player.health <= 0 ) return;
|
|
if ( !bNoGravity && player.onground && (waterlevel < 3) )
|
|
{
|
|
// Ground
|
|
if ( player.crouchdir == -1 )
|
|
{
|
|
// Crouching
|
|
if ( InStateSequence(CurState,FindState("CrouchMove")) )
|
|
SetStateLabel("Crouch");
|
|
else if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop"))
|
|
|| InStateSequence(CurState,FindState("SeeFastEnd"))
|
|
|| InStateSequence(CurState,FindState("Float"))
|
|
|| InStateSequence(CurState,FindState("FloatLoop")) )
|
|
SetStateLabel("StartCrouch");
|
|
}
|
|
else
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Crouch"))
|
|
|| InStateSequence(CurState,FindState("CrouchMove")) )
|
|
SetStateLabel("EndCrouch");
|
|
else if ( InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("Float"))
|
|
|| InStateSequence(CurState,FindState("FloatLoop")) )
|
|
{
|
|
SetStateLabel("Spawn");
|
|
A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
|
|
}
|
|
else if ( InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop")) )
|
|
SetStateLabel("SeeFastEnd");
|
|
}
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 1) )
|
|
{
|
|
// Falling
|
|
if ( (InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop"))
|
|
|| InStateSequence(CurState,FindState("SeeFastEnd"))
|
|
|| InStateSequence(CurState,FindState("Float"))
|
|
|| InStateSequence(CurState,FindState("FloatLoop")))
|
|
&& (abs(pos.z-floorz) > maxstepheight) )
|
|
SetStateLabel("Fall");
|
|
}
|
|
else
|
|
{
|
|
// Floating
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop"))
|
|
|| InStateSequence(CurState,FindState("SeeFastEnd"))
|
|
|| InStateSequence(CurState,FindState("Jump"))
|
|
|| InStateSequence(CurState,FindState("Float"))
|
|
|| InStateSequence(CurState,FindState("FloatLoop")) )
|
|
SetStateLabel("Fall");
|
|
}
|
|
}
|
|
override void PlayRunning()
|
|
{
|
|
if ( !player )
|
|
{
|
|
if ( !InStateSequence(CurState,FindState("See")) )
|
|
SetStateLabel("See");
|
|
return;
|
|
}
|
|
if ( player.health <= 0 ) return;
|
|
if ( !bNoGravity && player.onground && (waterlevel < 3) )
|
|
{
|
|
// Ground
|
|
if ( player.crouchdir == -1 )
|
|
{
|
|
// Crouching
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop"))
|
|
|| InStateSequence(CurState,FindState("SeeFastEnd")) )
|
|
SetStateLabel("StartCrouch");
|
|
else if ( InStateSequence(CurState,FindState("Crouch")) )
|
|
SetStateLabel("CrouchMove");
|
|
}
|
|
else
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Crouch"))
|
|
|| InStateSequence(CurState,FindState("CrouchMove")) )
|
|
SetStateLabel("EndCrouch");
|
|
else if ( FastCheck()
|
|
&& (InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))) )
|
|
SetStateLabel("SeeFast");
|
|
else if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
{
|
|
SetStateLabel("See");
|
|
A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
|
|
}
|
|
}
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 1) )
|
|
{
|
|
// Falling
|
|
PlayIdle();
|
|
}
|
|
else
|
|
{
|
|
// Floating
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("Turn"))
|
|
|| InStateSequence(CurState,FindState("See"))
|
|
|| InStateSequence(CurState,FindState("SeeFast"))
|
|
|| InStateSequence(CurState,FindState("SeeFastLoop"))
|
|
|| InStateSequence(CurState,FindState("SeeFastEnd"))
|
|
|| InStateSequence(CurState,FindState("Jump"))
|
|
|| InStateSequence(CurState,FindState("Fall"))
|
|
|| InStateSequence(CurState,FindState("FallLoop")) )
|
|
SetStateLabel("Float");
|
|
}
|
|
}
|
|
override void PlayAttacking()
|
|
{
|
|
// Do nothing if it's a SWWM weapon, since those do things themselves
|
|
if ( player && (player.ReadyWeapon is 'SWWMWeapon') )
|
|
return;
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMissile");
|
|
else SetStateLabel("Missile");
|
|
}
|
|
override void PlayAttacking2()
|
|
{
|
|
PlayAttacking();
|
|
}
|
|
void PlayFire()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMissile");
|
|
else SetStateLabel("Missile");
|
|
}
|
|
void PlayMelee()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchMelee");
|
|
else SetStateLabel("Melee");
|
|
}
|
|
void PlayFastMelee()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchFastMelee");
|
|
else SetStateLabel("FastMelee");
|
|
}
|
|
void PlayReload()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchReload");
|
|
else SetStateLabel("Reload");
|
|
}
|
|
void PlayCheckGun()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Dash"))
|
|
|| InStateSequence(CurState,FindState("Jump")) )
|
|
return; // don't cancel dash/jump
|
|
if ( player && (player.crouchdir == -1) ) SetStateLabel("CrouchCheckGun");
|
|
else SetStateLabel("CheckGun");
|
|
}
|
|
void A_DMFade()
|
|
{
|
|
if ( player ) return;
|
|
Spawn("TeleportFog",pos,ALLOW_REPLACE);
|
|
Destroy();
|
|
}
|
|
void A_DemoPain()
|
|
{
|
|
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
if ( lastdamage > 90 )
|
|
{
|
|
A_StartSound("demolitionist/hipain",CHAN_VOICE);
|
|
if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice
|
|
if ( (player == players[consoleplayer]) && mute && myvoice && (mute.GetInt() < 4) )
|
|
A_StartSound(String.Format("voice/%s/hipain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
|
|
}
|
|
else if ( lastdamage > 30 )
|
|
{
|
|
A_StartSound("demolitionist/pain",CHAN_VOICE);
|
|
if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice
|
|
if ( (player == players[consoleplayer]) && mute && myvoice && (mute.GetInt() < 4) )
|
|
A_StartSound(String.Format("voice/%s/pain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
|
|
}
|
|
else if ( lastdamage > 0 )
|
|
{
|
|
A_StartSound("demolitionist/lopain",CHAN_VOICE);
|
|
if ( !player || (player.mo != self) || (lastdamage < 5) ) return; // voodoo dolls have no voice
|
|
if ( (player == players[consoleplayer]) && mute && myvoice && (mute.GetInt() < 4) )
|
|
A_StartSound(String.Format("voice/%s/lopain",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
|
|
}
|
|
}
|
|
void A_DemoScream()
|
|
{
|
|
A_StopSound(CHAN_DEMOVOICE);
|
|
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
Sound snd = "demolitionist/death";
|
|
if ( special1 < 10 )
|
|
snd = "demolitionist/wdeath";
|
|
if ( health < 50 )
|
|
snd = "demolitionist/xdeath";
|
|
A_StartSound(snd,CHAN_VOICE);
|
|
if ( !player || (player.mo != self) ) return; // voodoo dolls have no voice
|
|
if ( (player == players[consoleplayer]) && mute && myvoice && (mute.GetInt() < 4) )
|
|
A_StartSound(String.Format("voice/%s/death",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
|
|
}
|
|
override bool OnGiveSecret( bool printmsg, bool playsound )
|
|
{
|
|
if ( !player ) return false;
|
|
int score = 500;
|
|
// last secret (this is called before counting it up, so have to subtract)
|
|
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
|
if ( (level.found_secrets == level.total_secrets-1) && (!hnd || !hnd.allsecrets) )
|
|
{
|
|
if ( hnd ) hnd.allsecrets = true;
|
|
score = 5000;
|
|
Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score);
|
|
}
|
|
else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score);
|
|
if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40);
|
|
SWWMCredits.Give(player,score);
|
|
SWWMScoreObj.Spawn(score,Vec3Offset(0,0,Height/2));
|
|
return true;
|
|
}
|
|
override void AddInventory( Inventory item )
|
|
{
|
|
Super.AddInventory(item);
|
|
if ( !player ) return;
|
|
// add lore if any
|
|
SWWMLoreLibrary.Add(player,item.GetClassName());
|
|
if ( (item is 'Weapon') && !(item is 'SWWMGesture') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() )
|
|
SWWMHandler.AddOneliner("getweapon",2);
|
|
if ( (item is 'Key') && !key_reentrant && !deathmatch )
|
|
{
|
|
// score
|
|
int score = 250;
|
|
Console.Printf(StringTable.Localize("$SWWM_FINDKEY"),player.GetUserName(),item.GetTag(),score);
|
|
SWWMCredits.Give(player,score);
|
|
SWWMScoreObj.Spawn(250,Vec3Offset(0,0,Height/2));
|
|
if ( !swwm_sharekeys ) return;
|
|
// share all keys in mp
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo || (i == PlayerNumber()) )
|
|
continue;
|
|
if ( players[i].mo is 'Demolitionist' ) Demolitionist(players[i].mo).key_reentrant = true;
|
|
players[i].mo.GiveInventory(item.GetClass(),1);
|
|
if ( players[i].mo is 'Demolitionist' ) Demolitionist(players[i].mo).key_reentrant = false;
|
|
}
|
|
}
|
|
}
|
|
override bool UseInventory( Inventory item )
|
|
{
|
|
let itemtype = item.GetClass();
|
|
if ( (player.cheats&CF_TOTALLYFROZEN) || isFrozen() ) return false;
|
|
if ( !Actor.UseInventory(item) )
|
|
{
|
|
if ( player == players[consoleplayer] )
|
|
{
|
|
if ( !(item is 'Weapon') )
|
|
A_StartSound("menu/noinvuse",CHAN_ITEMEXTRA);
|
|
if ( item is 'PuzzleItem' )
|
|
SWWMHandler.AddOneliner("puzzfail",2,20);
|
|
}
|
|
return false;
|
|
}
|
|
// use sounds of big powerups are heard by other players
|
|
if ( (player == players[consoleplayer]) || item.bBIGPOWERUP )
|
|
A_StartSound(item.UseSound,CHAN_ITEMEXTRA);
|
|
if ( (player == players[consoleplayer]) && (item is 'PuzzleItem') )
|
|
SWWMHandler.AddOneliner("puzzsucc",2,10);
|
|
return true;
|
|
}
|
|
void A_Footstep( double yofs, bool run = false, double vol = .3 )
|
|
{
|
|
if ( run )
|
|
{
|
|
A_StartSound("demolitionist/run",CHAN_FOOTSTEP,CHANF_OVERLAP,vol);
|
|
let b = Spawn("InvisibleSplasher",level.Vec3Offset(pos,(RotateVector((0,yofs*.25*radius),angle),0)));
|
|
b.A_CheckTerrain();
|
|
}
|
|
else
|
|
{
|
|
A_StartSound("demolitionist/walk",CHAN_FOOTSTEP,CHANF_OVERLAP,vol*.5);
|
|
let b = Spawn("SmolInvisibleSplasher",level.Vec3Offset(pos,(RotateVector((0,yofs*.25*radius),angle),0)));
|
|
b.A_CheckTerrain();
|
|
}
|
|
}
|
|
override bool Used( Actor user )
|
|
{
|
|
if ( !(user is 'Demolitionist') || !user.player ) return false;
|
|
if ( (user.player == players[consoleplayer]) && (health > 0) )
|
|
{
|
|
SWWMHandler.AddOneliner("greet",2);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool FastCheck( bool notfast = false )
|
|
{
|
|
if ( !player ) return false;
|
|
bool rslt = (player.cmd.buttons&BT_SPEED);
|
|
if ( !clrun ) clrun = CVar.GetCVar('cl_run',player);
|
|
if ( clrun.GetBool() ) rslt = !rslt;
|
|
if ( notfast ) rslt = !rslt;
|
|
return rslt;
|
|
}
|
|
bool AllowCrouch()
|
|
{
|
|
if ( player.cmd.buttons&BT_JUMP ) return false;
|
|
if ( swwm_crouchjump ) return true; // allowed
|
|
if ( (waterlevel >= 2) || bNOGRAVITY ) return false; // no crouch while swimming/floating
|
|
if ( !player.onground ) return false; // no crouch while in air
|
|
return true;
|
|
}
|
|
// Imagine having to duplicate two functions only to change a couple values in both
|
|
// I sure love constants
|
|
override void CrouchMove( int direction )
|
|
{
|
|
double defaultheight = FullHeight;
|
|
double savedheight = Height;
|
|
double crouchspeed = direction*CROUCHSPEED*.8; // slow down slightly so it matches the animation
|
|
double oldheight = player.viewheight;
|
|
player.crouchdir = direction;
|
|
player.crouchfactor += crouchspeed;
|
|
// check whether the move is ok
|
|
Height = defaultheight; // actually test the full height, or it'll look weird
|
|
if ( !TryMove(pos.xy,false) )
|
|
{
|
|
Height = savedheight;
|
|
if ( direction > 0 )
|
|
{
|
|
// doesn't fit
|
|
player.crouchfactor -= crouchspeed;
|
|
player.crouchdir = -1; // force crouch
|
|
return;
|
|
}
|
|
}
|
|
Height = savedheight;
|
|
player.crouchfactor = clamp(player.crouchfactor,.3,1.);
|
|
player.viewheight = ViewHeight*player.crouchfactor;
|
|
player.crouchviewdelta = player.viewheight-ViewHeight;
|
|
// Check for eyes going above/below fake floor due to crouching motion.
|
|
CheckFakeFloorTriggers(pos.z+oldheight,true);
|
|
}
|
|
override void CheckCrouch( bool totallyfrozen )
|
|
{
|
|
// crouch to swim/float down
|
|
if ( !totallyfrozen && (player.cmd.buttons&BT_CROUCH) )
|
|
{
|
|
double mult = FastCheck()?2.:1.;
|
|
if ( waterlevel >= 2 ) vel.z = -4*Speed*mult;
|
|
else if ( bNOGRAVITY ) vel.z = -3*mult;
|
|
}
|
|
bool wascrouching = !!(player.cmd.buttons&BT_CROUCH);
|
|
if ( !AllowCrouch() ) player.cmd.buttons &= ~BT_CROUCH;
|
|
if ( CanCrouch() && (player.health > 0) && level.IsCrouchingAllowed() )
|
|
{
|
|
if ( !totallyfrozen )
|
|
{
|
|
int crouchdir = player.crouching;
|
|
if ( !crouchdir ) crouchdir = (player.cmd.buttons&BT_CROUCH)?-1:1;
|
|
else if ( player.cmd.buttons&BT_CROUCH ) player.crouching = 0;
|
|
if ( (crouchdir == 1) && (player.crouchfactor < 1) && (pos.z+height < ceilingz) )
|
|
CrouchMove(1);
|
|
else if ( (crouchdir == -1) && (player.crouchfactor > .3) )
|
|
CrouchMove(-1);
|
|
}
|
|
}
|
|
else player.Uncrouch();
|
|
player.crouchoffset = -(ViewHeight)*(1-player.crouchfactor);
|
|
// we need the crouch button state to be preserved for other functions
|
|
if ( wascrouching ) player.cmd.buttons |= BT_CROUCH;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
// normal idle
|
|
#### # 2;
|
|
XZW1 A 1
|
|
{
|
|
if ( !player || (player.mo != self) ) return ResolveState("VoodooSpawn");
|
|
return A_JumpIf(player&&(player.mo==self)&&(abs(player.cmd.yaw|player.cmd.pitch)>128),"Turn");
|
|
}
|
|
Wait;
|
|
See:
|
|
// normal walking
|
|
#### # 2;
|
|
XZW1 BCD 2 A_JumpIf(FastCheck(false),"SeeFast");
|
|
XZW1 E 0 A_Footstep(1);
|
|
XZW1 EFGHIJKL 2 A_JumpIf(FastCheck(false),"SeeFast");
|
|
XZW1 M 0 A_Footstep(-1);
|
|
XZW1 MNOPA 2 A_JumpIf(FastCheck(false),"SeeFast");
|
|
Goto See+1;
|
|
Turn:
|
|
#### # 8 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.1);
|
|
XZW1 C 1 A_JumpIf(!player||!(player.cmd.yaw|player.cmd.pitch),1);
|
|
Wait;
|
|
XZW1 C 3 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.1);
|
|
Goto Spawn+1;
|
|
SeeFast:
|
|
// sprinting
|
|
#### # 2 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.3);
|
|
XZW1 QRST 2;
|
|
Goto SeeFastLoop;
|
|
SeeFastLoop:
|
|
// keep sprinting
|
|
XZW1 U 0 A_Footstep(1,true);
|
|
XZW1 UVWXYZ 2 A_JumpIf(FastCheck(true),"SeeFastEnd");
|
|
XZW2 A 2 A_JumpIf(FastCheck(true),"SeeFastEnd");
|
|
XZW2 B 0 A_Footstep(-1,true);
|
|
XZW2 BCDEFG 2 A_JumpIf(FastCheck(true),"SeeFastEnd");
|
|
XZW1 T 2 A_JumpIf(FastCheck(true),"SeeFastEnd");
|
|
Goto SeeFastLoop;
|
|
SeeFastEnd:
|
|
// brake
|
|
#### # 2 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.3);
|
|
XZW2 HIJKL 2;
|
|
Goto Spawn+1;
|
|
Pain:
|
|
// ouchy
|
|
XZW1 A 1
|
|
{
|
|
if ( !player || (player.mo != self) ) return ResolveState("VoodooPain");
|
|
return A_JumpIf(player&&(player.mo==self)&&(player.crouchdir==-1),"CrouchPain");
|
|
}
|
|
XZW2 M 1 A_DemoPain();
|
|
XZW2 NOPQ 1;
|
|
Goto Spawn+1;
|
|
Death:
|
|
XDeath:
|
|
// ded
|
|
XZW1 A 0
|
|
{
|
|
if ( !player || (player.mo != self) ) return ResolveState("VoodooDeath");
|
|
return A_JumpIf(player&&(player.mo==self)&&(player.crouchdir==-1),"CrouchDeath");
|
|
}
|
|
XZW1 A 2
|
|
{
|
|
A_DemoScream();
|
|
A_NoBlocking();
|
|
}
|
|
XZW2 RSTUVWXYZ 2;
|
|
XZW3 ABCDEFG 2;
|
|
XZW3 H 1 A_DMFade();
|
|
Wait;
|
|
Jump:
|
|
// start boost
|
|
#### # 2;
|
|
XZW3 IJKLMNO 2
|
|
{
|
|
if ( player.onground||bNoGravity||(waterlevel>=3) )
|
|
return ResolveState("JumpEnd");
|
|
A_BoostUp(true);
|
|
return ResolveState(null);
|
|
}
|
|
// keep boost
|
|
XZW3 P 1
|
|
{
|
|
if ( player.onground||bNoGravity||(waterlevel>=3) )
|
|
return ResolveState("JumpEnd");
|
|
A_BoostUp(false);
|
|
return ResolveState(null);
|
|
}
|
|
XZW3 P 1 A_JumpIf((vel.z<-10)&&(pos.z>(floorz+80)),"Fall");
|
|
Goto Jump+8;
|
|
JumpEnd:
|
|
// stop boost
|
|
#### # 2;
|
|
XZW3 PQRSTUVW 2;
|
|
Goto Spawn+1;
|
|
Fall:
|
|
// start fall
|
|
#### # 4;
|
|
XZW3 XYZ 2 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd");
|
|
XZW4 AB 2 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd");
|
|
Goto FallLoop;
|
|
FallLoop:
|
|
// falling
|
|
XZW4 CDEFGH 3 A_JumpIf(player.onground&&!bNoGravity&&(waterlevel<3),"FallEnd");
|
|
Goto FallLoop;
|
|
FallEnd:
|
|
// landing
|
|
XZW4 CIJKLMN 2;
|
|
Goto Spawn+1;
|
|
Float:
|
|
// start
|
|
#### # 2;
|
|
XZW4 O 3;
|
|
Goto FloatLoop;
|
|
FloatLoop:
|
|
XZW4 PQRS 6;
|
|
Loop;
|
|
FloatEnd:
|
|
#### # 2;
|
|
XZW4 TUVWX 3;
|
|
Goto Spawn+1;
|
|
Dash:
|
|
#### # 2;
|
|
XZW4 O 2 A_Dash();
|
|
XZW4 PQRS 2 A_Dash();
|
|
Goto Dash+2;
|
|
DashEnd:
|
|
XZW4 TUVWX 2;
|
|
Goto Spawn+1;
|
|
Wave:
|
|
#### # 3;
|
|
XZW4 YZ 3;
|
|
XZW5 ABCDEFGHIJKLM 3;
|
|
Goto Spawn+1;
|
|
Approve:
|
|
#### # 3;
|
|
XZW5 NOPQRSTUVWXYZ 3;
|
|
XZW6 ABCD 3;
|
|
Goto Spawn+1;
|
|
Victory:
|
|
#### # 3;
|
|
XZW6 EFGHIJKLMNOPQRSTUVW 3;
|
|
Goto Spawn+1;
|
|
Missile:
|
|
// attacking
|
|
#### # 2;
|
|
XZW6 XYZ 2;
|
|
XZW7 ABC 2;
|
|
Goto Spawn+1;
|
|
Melee:
|
|
// ponch
|
|
#### # 2;
|
|
XZW8 TUVWXYZ 2;
|
|
XZW9 ABCDEF 2;
|
|
Goto Spawn+1;
|
|
FastMelee:
|
|
// ponch (fast)
|
|
#### # 2;
|
|
XZW8 TUVWXYZ 1;
|
|
XZW9 ABCDE 1;
|
|
XZW9 F 2;
|
|
Goto Spawn+1;
|
|
Reload:
|
|
// reload
|
|
#### # 2;
|
|
XZW9 GHIJKLMNOPQRSTUVWXYZ 2;
|
|
XZWA ABCDE 2;
|
|
Goto Spawn+1;
|
|
CheckGun:
|
|
// speen
|
|
#### # 2;
|
|
XZWA FGHIJKLMNOPQRSTUVWXY 2;
|
|
Goto Spawn+1;
|
|
StartCrouch:
|
|
// go crouching
|
|
#### # 2 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.45);
|
|
XZW7 DEFGH 1;
|
|
XZW7 IJKL 2;
|
|
Goto Crouch+1;
|
|
Crouch:
|
|
#### # 4;
|
|
XZW7 M -1;
|
|
Stop;
|
|
CrouchMove:
|
|
XZW7 MN 2;
|
|
XZW7 O 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
|
|
XZW7 OPQRS 2;
|
|
XZW7 T 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
|
|
XZW7 TUV 2;
|
|
Loop;
|
|
CrouchMissile:
|
|
XZW7 M 2;
|
|
XZW7 WXYZ 2;
|
|
XZW8 AB 2;
|
|
Goto Crouch+1;
|
|
CrouchMelee:
|
|
XZW7 M 2;
|
|
XZWA Z 2;
|
|
XZWB ABCDEFGHIJKL 2;
|
|
Goto Crouch+1;
|
|
CrouchFastMelee:
|
|
XZW7 M 2;
|
|
XZWA Z 1;
|
|
XZWB ABCDEFGHIJK 1;
|
|
XZWB L 2;
|
|
Goto Crouch+1;
|
|
CrouchReload:
|
|
XZW7 M 2;
|
|
XZWB MNOPQRSTUVWXYZ 2;
|
|
XZWC ABCDEFGHIJ 2;
|
|
Goto Crouch+1;
|
|
CrouchCheckGun:
|
|
XZW7 M 2;
|
|
XZWC LMNOPQRSTUVWXYZ 2;
|
|
XZWD ABCD 2;
|
|
Goto Crouch+1;
|
|
CrouchPain:
|
|
XZW7 M 1;
|
|
XZW8 C 1 A_DemoPain();
|
|
XZW8 DEF 1;
|
|
Goto Crouch+1;
|
|
CrouchDeath:
|
|
XZW7 M 2
|
|
{
|
|
A_DemoScream();
|
|
A_NoBlocking();
|
|
}
|
|
XZW8 GHIJK 2;
|
|
XZW8 L 1 A_DMFade();
|
|
Wait;
|
|
EndCrouch:
|
|
#### # 2 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.45);
|
|
XZW8 MNOPQRS 2;
|
|
Goto Spawn+1;
|
|
VoodooSpawn:
|
|
XZWZ A -1;
|
|
Loop;
|
|
VoodooPain:
|
|
XZWZ A 1;
|
|
XZWZ B 1 A_DemoPain();
|
|
XZWZ CDEF 1;
|
|
Goto VoodooSpawn;
|
|
VoodooDeath:
|
|
XZWZ A 2
|
|
{
|
|
A_DemoScream();
|
|
A_NoBlocking();
|
|
}
|
|
XZWZ GHIJKLMNO 2;
|
|
XZWZ PQR 2;
|
|
XZWZ S -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class DashTrail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
Scale 0.3;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetState(FindState("Spawn")+Random[ExploS](0,7));
|
|
let t = Spawn("DashTrail2",level.Vec3Offset(pos,vel*.3));
|
|
t.target = target;
|
|
t.vel = vel*1.2;
|
|
let s = Spawn("SWWMSmoke",level.Vec3Offset(pos,vel*1.6));
|
|
s.vel = vel*.8;
|
|
s.SetShade(Color(1,1,1)*Random[ExploS](64,128));
|
|
s.special1 = Random[ExploS](2,4);
|
|
s.scale *= 1.4;
|
|
s.alpha *= .3;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
// hack
|
|
if ( target && (players[consoleplayer].Camera == target) ) Warp(target,pos.x,pos.y,pos.z,0,WARPF_ABSOLUTEPOSITION|WARPF_COPYINTERPOLATION);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
JFLB ABCDEFGH 1 Bright
|
|
{
|
|
A_FadeOut(.2);
|
|
A_SetScale(scale.x*.95);
|
|
}
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
Class DashTrail2 : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
Scale 0.2;
|
|
Alpha 0.4;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetState(FindState("Spawn")+Random[ExploS](0,7));
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
// hack
|
|
if ( target && (players[consoleplayer].Camera == target) ) Warp(target,pos.x,pos.y,pos.z,0,WARPF_ABSOLUTEPOSITION|WARPF_COPYINTERPOLATION);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
JFLR ABCDEFGH 1 Bright
|
|
{
|
|
A_FadeOut(.02);
|
|
A_SetScale(scale.x*1.04);
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.vel = vel;
|
|
b.scale *= scale.x;
|
|
Destroy();
|
|
}
|
|
}
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
Class DemolitionistRadiusShockwaveTail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Translucent";
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+WALLSPRITE;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SDST A 1
|
|
{
|
|
A_FadeOut(0.08);
|
|
A_SetScale(scale.x,scale.y*0.98);
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class DemolitionistRadiusShockwave : Actor
|
|
{
|
|
Actor lasthit;
|
|
|
|
Default
|
|
{
|
|
RenderStyle "Translucent";
|
|
Speed 10;
|
|
DamageFunction int(20*alpha);
|
|
DamageType "GroundPound";
|
|
Radius 16;
|
|
Height 8;
|
|
Alpha 0.8;
|
|
Scale 1.2;
|
|
PROJECTILE;
|
|
+DONTSPLASH;
|
|
+STEPMISSILE;
|
|
+NOEXPLODEFLOOR;
|
|
+WALLSPRITE;
|
|
+RIPPER;
|
|
+BLOODLESSIMPACT;
|
|
-NOGRAVITY;
|
|
}
|
|
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
|
|
{
|
|
if ( target == lasthit ) return 0;
|
|
lasthit = target;
|
|
if ( damage <= 0 ) return damage;
|
|
if ( (target.mass < LARGE_MASS) && !target.bDONTTHRUST )
|
|
{
|
|
target.vel.xy += vel.xy.unit()*(5000./max(50,target.mass))*alpha;
|
|
target.vel.z += (800./max(50,target.mass))*alpha;
|
|
}
|
|
return damage;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SDST A 1
|
|
{
|
|
SetZ(floorz);
|
|
Spawn("InvisibleSplasher",Vec3Offset(0,0,2));
|
|
let s = Spawn("DemolitionistRadiusShockwaveTail",pos);
|
|
s.vel = vel*.8;
|
|
s.scale = scale;
|
|
s.alpha = alpha;
|
|
s.angle = angle;
|
|
A_FadeOut(.04);
|
|
A_SetScale(scale.x*1.03,scale.y*0.99);
|
|
}
|
|
Wait;
|
|
Death:
|
|
SDST A 1
|
|
{
|
|
SetZ(floorz);
|
|
A_FadeOut(.1);
|
|
A_SetScale(scale.x*1.06,scale.y*0.98);
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class DemolitionistShockwave : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+NOTELEPORT;
|
|
+NODAMAGETHRUST;
|
|
+FORCERADIUSDMG;
|
|
+NOINTERACTION;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_QuakeEx(7,7,7,30,0,300+min(special1,50)*4,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:1.5);
|
|
if ( target.player != players[consoleplayer] )
|
|
{
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.3);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.2,pitch:.7);
|
|
A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.1,pitch:.4);
|
|
}
|
|
target.A_Blast(BF_AFFECTBOSSES,200+min(special1*3,100),70+min(special1*2,130),10+min(special1/10,20),"SWWMNothing","");
|
|
A_Explode(10+min(special1,90),70+min(special1*2,130),XF_EXPLICITDAMAGETYPE,damagetype:'GroundPound');
|
|
for ( int i=0; i<360; i+=5 )
|
|
{
|
|
let r = Spawn("DemolitionistRadiusShockwave",Vec3Angle(10,i));
|
|
r.target = target;
|
|
r.angle = i;
|
|
r.vel.xy = (cos(i),sin(i))*(r.speed+min(special1*.15,20));
|
|
r.alpha *= .1+min(special1*.03,.9);
|
|
}
|
|
for ( int i=0; i<360; i+=5 )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);
|
|
let s = Spawn("SWWMSmoke",Vec3Angle(4,i,8));
|
|
s.vel = pvel+(cos(i),sin(i),0)*7.;
|
|
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
|
s.special1 = Random[ExploS](1,4);
|
|
s.scale *= 1.5;
|
|
s.alpha *= .4;
|
|
}
|
|
int numpt = Random[ExploS](10,20);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
|
let s = Spawn("SWWMChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 140;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class ReviveCooldown : Powerup
|
|
{
|
|
Default
|
|
{
|
|
Inventory.Icon "graphics/HUD/Icons/I_Revive.png";
|
|
Powerup.Duration -30;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !Owner ) Destroy();
|
|
if ( Owner.Health <= 0 ) return; // timer does not go down when dead
|
|
if ( (EffectTics == 0) || ((EffectTics > 0) && (--EffectTics == 0)) )
|
|
Destroy ();
|
|
}
|
|
override void InitEffect()
|
|
{
|
|
Super.InitEffect();
|
|
// adjust the duration
|
|
EffectTics = max(0,swwm_revivecooldown)*Thinker.TICRATE;
|
|
}
|
|
override void EndEffect()
|
|
{
|
|
Super.EndEffect();
|
|
if ( !Owner ) return;
|
|
Owner.A_StartSound("demolitionist/revive",CHAN_ITEMEXTRA);
|
|
if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_REFAIL"));
|
|
}
|
|
override void OwnerDied()
|
|
{
|
|
// do nothing, this "powerup" is preserved on death
|
|
}
|
|
}
|
|
|
|
// not an actual light, just handles the attach/detach
|
|
Class DemolitionistSelfLight : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
}
|
|
bool activelight()
|
|
{
|
|
// active all the time except when invisible or in certain
|
|
// animation frames
|
|
if ( target.bINVISIBLE || (target.alpha <= double.epsilon) ) return false;
|
|
if ( target.InStateSequence(target.CurState,target.FindState("Death")) && ((target.frame == 20) || (target.frame == 22) || (target.frame == 23) || (target.frame == 25) || (target.frame == 27) || (target.frame == 28) || (target.frame == 30) || (target.frame == 31) || (target.frame == 32) || (target.frame == 33) || (target.frame == 34)) )
|
|
return false;
|
|
if ( target.InStateSequence(target.CurState,target.FindState("CrouchDeath")) && ((target.frame == 7) || (target.frame == 10) || (target.frame == 11)) )
|
|
return false;
|
|
return true;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( !target || !(target is 'Demolitionist') || (Demolitionist(target).selflight != self) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( !activelight() )
|
|
target.A_RemoveLight('DemoSelfLight');
|
|
else if ( activelight() )
|
|
target.A_AttachLight('DemoSelfLight',DynamicLight.PointLight,Color(56,72,88),200,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE|DynamicLight.LF_SPOT,(12,0,target.player?(target.player.viewz-target.pos.z):(target.height*.93)),0,30,90,target.pitch);
|
|
if ( target.bINVISIBLE || (target.alpha <= double.epsilon) )
|
|
target.A_RemoveLight('DemoSelfLight2');
|
|
else
|
|
target.A_AttachLight('DemoSelfLight2',DynamicLight.PointLight,Color(32,48,24),80,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE,(0,0,target.height/2));
|
|
}
|
|
}
|
|
|
|
// for the doom 2 cast
|
|
Class CastDemolitionist : Actor
|
|
{
|
|
Default
|
|
{
|
|
DeathSound "demolitionist/death";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
See:
|
|
CCD1 ABCDEFGHIJKLMNOP 2;
|
|
Loop;
|
|
Missile:
|
|
CCD1 A 2;
|
|
CCD2 ABCDEF 2;
|
|
Goto See;
|
|
Death:
|
|
CCD1 A 2;
|
|
CCD3 ABCDEFGHIJKLMNOP 2;
|
|
CCD3 Q -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// First person gestures
|
|
Class SWWMGesture : SWWMWeapon
|
|
{
|
|
Weapon formerweapon;
|
|
int whichgesture;
|
|
bool deaded;
|
|
int gonect;
|
|
|
|
// these should prevent autoswitch when out of ammo
|
|
override bool ReportHUDAmmo()
|
|
{
|
|
return false;
|
|
}
|
|
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void SetGesture( PlayerPawn mo, int which = 0 )
|
|
{
|
|
if ( (mo.player.ReadyWeapon is 'SWWMGesture') || (mo.player.PendingWeapon is 'SWWMGesture') ) return; // already gesturing
|
|
SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture"));
|
|
if ( !w )
|
|
{
|
|
w = SWWMGesture(Spawn("SWWMGesture"));
|
|
mo.AddInventory(w);
|
|
}
|
|
if ( mo.player.PendingWeapon != WP_NOCHANGE ) w.formerweapon = mo.player.PendingWeapon;
|
|
else w.formerweapon = mo.player.ReadyWeapon;
|
|
w.whichgesture = which;
|
|
mo.player.PendingWeapon = w;
|
|
}
|
|
|
|
action void A_CallPlayerGesture( statelabel st )
|
|
{
|
|
if ( invoker.Owner.FindState(st) && (invoker.Owner.Health > 0) )
|
|
invoker.Owner.SetStateLabel(st);
|
|
}
|
|
|
|
action void A_FinishGesture()
|
|
{
|
|
player.PendingWeapon = invoker.formerweapon;
|
|
player.SetPSprite(PSP_WEAPON,ResolveState("Deselect"));
|
|
}
|
|
|
|
Default
|
|
{
|
|
+WEAPON.CHEATNOTWEAPON;
|
|
+WEAPON.WIMPY_WEAPON;
|
|
+INVENTORY.UNDROPPABLE;
|
|
+INVENTORY.UNTOSSABLE;
|
|
+INVENTORY.UNCLEARABLE;
|
|
Weapon.SelectionOrder int.max;
|
|
}
|
|
States
|
|
{
|
|
Select:
|
|
TNT1 A 1
|
|
{
|
|
A_FullRaise();
|
|
if ( Health <= 0 ) A_FinishGesture();
|
|
}
|
|
Goto Ready;
|
|
Ready:
|
|
TNT1 A 1
|
|
{
|
|
if ( invoker.whichgesture == 1 ) return ResolveState("Approve");
|
|
if ( invoker.whichgesture == 2 ) return ResolveState("Victory");
|
|
return ResolveState("Wave");
|
|
}
|
|
Wait;
|
|
Fire:
|
|
TNT1 A 1;
|
|
Goto Ready;
|
|
Wave:
|
|
TNT1 A 3 A_CallPlayerGesture("Wave");
|
|
XZW1 AB 3;
|
|
XZW1 C 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW1 DEFGHIJ 3;
|
|
XZW1 K 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW1 LMNO 3;
|
|
TNT1 A -1 A_FinishGesture();
|
|
Stop;
|
|
Approve:
|
|
TNT1 A 3 A_CallPlayerGesture("Approve");
|
|
XZW1 PQ 3;
|
|
XZW1 R 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW1 STUVWX 3;
|
|
XZW1 Y 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW1 Z 3;
|
|
XZW2 ABCDEF 3;
|
|
TNT1 A -1 A_FinishGesture();
|
|
Stop;
|
|
Victory:
|
|
TNT1 A 3 A_CallPlayerGesture("Victory");
|
|
XZW2 GH 3;
|
|
XZW2 I 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW2 JKLMNOPQ 3;
|
|
XZW2 R 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW2 STUVWXY 3;
|
|
TNT1 A -1 A_FinishGesture();
|
|
Stop;
|
|
Deselect:
|
|
TNT1 A -1 A_FullLower();
|
|
Stop;
|
|
}
|
|
}
|