1295 lines
37 KiB
Text
1295 lines
37 KiB
Text
// The Demolitionist
|
|
Class Demolitionist : PlayerPawn
|
|
{
|
|
int last_jump_held;
|
|
Vector3 dashdir;
|
|
double dashfuel, dashboost;
|
|
int dashcooldown, boostcooldown, fuelcooldown;
|
|
bool dashsnd;
|
|
bool sendtoground;
|
|
bool key_reentrant;
|
|
|
|
int lastdamage;
|
|
bool lastground;
|
|
int lastgroundtic;
|
|
double lastvelz, prevvelz;
|
|
double ssup;
|
|
|
|
transient CVar myvoice, mute;
|
|
|
|
SWWMStats mystats;
|
|
int cairtime;
|
|
Vector3 oldpos;
|
|
|
|
int lastmpain;
|
|
|
|
double guideangle, guidepitch, guideroll;
|
|
|
|
Default
|
|
{
|
|
Speed 1;
|
|
Radius 16;
|
|
Height 56;
|
|
Mass 500;
|
|
PainChance 255;
|
|
Player.DisplayName "Demolitionist";
|
|
Player.StartItem "DeepImpact";
|
|
Player.StartItem "ExplodiumGun";
|
|
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;
|
|
}
|
|
// 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;
|
|
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;
|
|
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 == '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 eg = ExplodiumGun(FindInventory("ExplodiumGun"));
|
|
if ( !eg ) return;
|
|
eg.clipcount = 7;
|
|
eg.chambered = true;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
dashfuel = 100.;
|
|
mystats = SWWMStats.Find(player);
|
|
oldpos = pos;
|
|
}
|
|
void A_Dash()
|
|
{
|
|
vel += dashdir*dashboost;
|
|
player.vel = vel.xy;
|
|
if ( dashboost < 0.1 ) dashboost = 0.;
|
|
else
|
|
{
|
|
A_AlertMonsters(800);
|
|
dashboost *= .5;
|
|
}
|
|
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
|
|
dashfuel = max(0.,dashfuel-dashboost);
|
|
dashcooldown = 40;
|
|
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
|
|
{
|
|
A_AlertMonsters(800);
|
|
dashboost *= (player.cmd.buttons&BT_JUMP)?.9:.4;
|
|
}
|
|
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);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !player ) return;
|
|
double traveldist = level.Vec3Diff(oldpos,pos).length();
|
|
if ( waterlevel < 3 )
|
|
{
|
|
if ( !player.onground || bNoGravity )
|
|
{
|
|
cairtime++;
|
|
if ( cairtime > mystats.airtime ) mystats.airtime = cairtime;
|
|
mystats.airdist += traveldist;
|
|
}
|
|
else
|
|
{
|
|
cairtime = 0;
|
|
mystats.grounddist += traveldist;
|
|
}
|
|
}
|
|
if ( traveldist > mystats.topspeed ) mystats.topspeed = traveldist;
|
|
oldpos = pos;
|
|
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) )
|
|
{
|
|
if ( lastvelz < -30 )
|
|
{
|
|
let s = Spawn("DemolitionistShockwave",pos);
|
|
s.target = self;
|
|
s.special1 = int(-lastvelz);
|
|
ReactionTime = 17;
|
|
A_Stop();
|
|
A_AlertMonsters();
|
|
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);
|
|
A_Footstep(0,true,clamp(-lastvelz*0.05,0.01,1.0));
|
|
}
|
|
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(120.,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();
|
|
// look for things we could potentially bump into
|
|
let bi = BlockThingsIterator.Create(self,200);
|
|
bool bumped = false;
|
|
while ( (spd > 0) && bi.Next() )
|
|
{
|
|
a = bi.Thing;
|
|
if ( !a || (a == self) || !a.bSHOOTABLE || a.bTHRUACTORS ) continue;
|
|
Vector3 diff = level.Vec3Diff(level.Vec3Offset(pos,vel),level.Vec3Offset(a.pos,a.vel));
|
|
if ( (abs(diff.x) > (radius+a.radius+8)) || (abs(diff.y) > (radius+a.radius+8)) ) continue;
|
|
if ( (diff.z > (height+8)) || (diff.z < (8-a.height)) ) continue;
|
|
double moveang = atan2(dir.y,dir.x);
|
|
Vector3 sc = level.SphericalCoords(pos,a.pos,(moveang,0));
|
|
if ( abs(sc.x) > 60 ) continue;
|
|
// bosses and large monsters will stop the player
|
|
A_QuakeEx(1,1,1,3,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
|
|
a.A_StartSound("demolitionist/bump",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
if ( a.bDONTTHRUST || (a.Mass >= Mass*5) )
|
|
{
|
|
if ( bumped ) continue;
|
|
bumped = true;
|
|
A_QuakeEx(3,3,3,8,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
|
|
A_StartSound("demolitionist/bump",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
vel -= dir*(9+(spd*30/mass));
|
|
vel.z += 3+(spd*(20/mass));
|
|
}
|
|
Vector3 pushdir = Vec3To(a).unit()*.1+dir*.9;
|
|
if ( !a.bDONTTHRUST && (a.Mass < Mass*5) )
|
|
{
|
|
a.vel += pushdir*(15+(spd*150/max(50,a.mass)));
|
|
a.vel.z += 5+(spd*(50/max(50,a.mass)));
|
|
}
|
|
a.DamageMobj(self,self,int(15+spd*2.5),'Dash',DMG_THRUSTLESS);
|
|
}
|
|
}
|
|
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 ( Health < 25 ) damage /= 4;
|
|
if ( source == self ) damage /= 2;
|
|
int lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
|
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 ( player.centering )
|
|
{
|
|
guidepitch = clamp(deltaangle(pitch,0),-3.,3.);
|
|
guideroll = clamp(deltaangle(roll,0),-3.,3.);
|
|
}
|
|
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;
|
|
if ( (abs(roll) <= 0.) && (abs(pitch) <= 0.) ) player.centering = false;
|
|
double fs = TweakSpeeds(1.,0.);
|
|
fs *= max(abs(player.cmd.forwardmove/12800.),abs(player.cmd.sidemove/10240.));
|
|
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*player.cmd.upmove;
|
|
accel *= fs/128.;
|
|
vel = vel+accel/TICRATE;
|
|
if ( vel.length() > 50. )
|
|
vel = vel.unit()*50.;
|
|
vel *= .99;
|
|
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();
|
|
if ( abs(roll) > 0. ) roll += clamp(deltaangle(roll,0),-3.,3.);
|
|
}
|
|
guideangle *= .9;
|
|
guidepitch *= .9;
|
|
guideroll *= .9;
|
|
if ( player.onground ) lastgroundtic = gametic;
|
|
// anchor to ground when going down steps
|
|
if ( !player.onground && !bFly && !bFlyCheat && !(player.cheats&CF_NOCLIP2) && (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);
|
|
player.onground = true;
|
|
}
|
|
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.onground ) dodge.z = max(0,dodge.z);
|
|
if ( dodge == (0,0,0) ) dodge.xy = RotateVector((1,0),angle);
|
|
if ( (waterlevel < 2) && !(bFly || bFlyCheat || (player.cheats&CF_NOCLIP2)) )
|
|
dodge.z += .15;
|
|
if ( (dodge.length() > 0) && (dashcooldown <= 0) && (dashfuel > 20.) && player.cmd.buttons&BT_USER2 )
|
|
{
|
|
dashdir = dodge.unit();
|
|
dashcooldown = 40;
|
|
dashboost = 20.;
|
|
bOnMobj = false;
|
|
if ( player.cheats & CF_REVERTPLEASE )
|
|
{
|
|
player.cheats &= ~CF_REVERTPLEASE;
|
|
player.camera = player.mo;
|
|
}
|
|
vel *= 0;
|
|
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
|
|
bool walljump = LineTrace(angle-180,Radius*4,0,TRF_NOSKY|TRF_THRUHITSCAN,height*.2);
|
|
if ( player.cmd.buttons&BT_JUMP )
|
|
{
|
|
if ( player.crouchoffset ) player.crouching = 1;
|
|
else if ( waterlevel >= 2 ) vel.z = 4*Speed;
|
|
else if ( bFly || bFlyCheat || (player.cheats&CF_NOCLIP2) ) return;
|
|
else if ( level.IsJumpingAllowed()
|
|
&& (player.onground && (player.jumptics == 0))
|
|
|| (!player.onground && (level.maptime > last_jump_held) && (((dashfuel > 10.) && (boostcooldown <= 0)) || walljump)) )
|
|
{
|
|
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;
|
|
double pvelz = vel.z;
|
|
if ( !player.onground )
|
|
{
|
|
// check for wall kicks
|
|
if ( walljump )
|
|
{
|
|
vel.z = max(-abs(vel.z)*.1,vel.z);
|
|
vel.z += jumpvelz;
|
|
vel.xy = (cos(angle),sin(angle))*20*Speed;
|
|
}
|
|
}
|
|
bOnMobj = false;
|
|
player.jumpTics = -1;
|
|
if ( !(player.cheats&CF_PREDICTING) )
|
|
A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
if ( (dashfuel > 10.) && !player.onground && !walljump )
|
|
{
|
|
dashboost = 3.;
|
|
boostcooldown = 20;
|
|
vel.z += jumpvelz+clamp(-pvelz,0.,30.);
|
|
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
|
|
mystats.boostcount++;
|
|
}
|
|
else
|
|
{
|
|
dashboost = 0.;
|
|
vel.z += jumpvelz*1.25;
|
|
}
|
|
SetStateLabel("Jump");
|
|
}
|
|
last_jump_held = level.maptime+1;
|
|
}
|
|
}
|
|
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 ( player.cmd.buttons&BT_SPEED
|
|
&& (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()
|
|
{
|
|
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 A_DMFade()
|
|
{
|
|
if ( !deathmatch || player ) return;
|
|
A_FadeOut(0.03);
|
|
}
|
|
void A_DemoPain()
|
|
{
|
|
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
if ( lastdamage > 90 )
|
|
{
|
|
A_StartSound("*pain25",CHAN_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("*pain50",CHAN_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("*pain100",CHAN_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()
|
|
{
|
|
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
A_PlayerScream();
|
|
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;
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
int score = 500;
|
|
// last secret (this is called before counting it up, so have to subtract)
|
|
if ( level.found_secrets == level.total_secrets-1 )
|
|
{
|
|
score = 5000;
|
|
Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score);
|
|
}
|
|
else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score);
|
|
if ( CheckLocalView() && (mute.GetInt() < 2) ) SWWMHandler.AddOneliner("findsecret",40);
|
|
SWWMCredits.Give(player,score);
|
|
return true;
|
|
}
|
|
override void AddInventory( Inventory item )
|
|
{
|
|
Super.AddInventory(item);
|
|
if ( !player ) return;
|
|
// add lore if any
|
|
SWWMLoreLibrary.Add(player,item.GetClassName());
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
if ( (item is 'Weapon') && CheckLocalView() && (mute.GetInt() < 2) )
|
|
SWWMHandler.AddOneliner("getweapon");
|
|
if ( (item is 'Key') && !key_reentrant )
|
|
{
|
|
// score
|
|
int score = 250;
|
|
Console.Printf(StringTable.Localize("$SWWM_FINDKEY"),player.GetUserName(),item.GetTag(),score);
|
|
SWWMCredits.Give(player,score);
|
|
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 )
|
|
{
|
|
if ( !player ) return Super.UseInventory(item);
|
|
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
|
|
bool res = Super.UseInventory(item);
|
|
if ( CheckLocalView() )
|
|
{
|
|
if ( !res && !(item is 'Weapon') ) A_StartSound("menu/noinvuse",CHAN_ITEM);
|
|
if ( (item is 'PuzzleItem') && (mute.GetInt() < 2) )
|
|
{
|
|
if ( res ) SWWMHandler.AddOneliner("puzzsucc",10);
|
|
else SWWMHandler.AddOneliner("puzzfail",20);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
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;
|
|
CVar othermute = CVar.GetCVar('swwm_mutevoice',user.player);
|
|
if ( (user.player == players[consoleplayer]) && (othermute.GetInt() < 2) )
|
|
SWWMHandler.AddOneliner("greet");
|
|
return false;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
// normal idle
|
|
#### # 2;
|
|
XZW1 A 1 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(player&&(player.cmd.buttons&BT_SPEED),"SeeFast");
|
|
XZW1 E 0 A_Footstep(1);
|
|
XZW1 EFGHIJKL 2 A_JumpIf(player&&(player.cmd.buttons&BT_SPEED),"SeeFast");
|
|
XZW1 M 0 A_Footstep(-1);
|
|
XZW1 MNOPA 2 A_JumpIf(player&&(player.cmd.buttons&BT_SPEED),"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(player&&!(player.cmd.buttons&BT_SPEED),"SeeFastEnd");
|
|
XZW2 A 2 A_JumpIf(player&&!(player.cmd.buttons&BT_SPEED),"SeeFastEnd");
|
|
XZW2 B 0 A_Footstep(-1,true);
|
|
XZW2 BCDEFG 2 A_JumpIf(player&&!(player.cmd.buttons&BT_SPEED),"SeeFastEnd");
|
|
XZW1 T 2 A_JumpIf(player&&!(player.cmd.buttons&BT_SPEED),"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 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 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 350;
|
|
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;
|
|
Taunt:
|
|
#### # 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:
|
|
Melee:
|
|
// attacking
|
|
XZW1 A 2;
|
|
XZW6 XYZ 2;
|
|
XZW7 ABC 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;
|
|
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 350;
|
|
XZW8 L 1 A_DMFade();
|
|
Wait;
|
|
EndCrouch:
|
|
#### # 2 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.45);
|
|
XZW8 MNOPQRS 2;
|
|
Goto Spawn+1;
|
|
}
|
|
}
|
|
|
|
Class DashTrail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
Scale 0.3;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SDST A 1
|
|
{
|
|
A_FadeOut(0.08);
|
|
A_SetScale(scale.x,scale.y*0.98);
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class DemolitionistRadiusShockwave : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Translucent";
|
|
Speed 10;
|
|
DamageFunction int(20*alpha);
|
|
DamageType "GroundPound";
|
|
Radius 16;
|
|
Height 8;
|
|
Alpha 0.8;
|
|
Scale 1.2;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+MISSILE;
|
|
+STEPMISSILE;
|
|
+NOEXPLODEFLOOR;
|
|
+WALLSPRITE;
|
|
+NOTELEPORT;
|
|
+RIPPER;
|
|
}
|
|
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
|
|
{
|
|
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;
|
|
}
|
|
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(5,i));
|
|
r.target = target;
|
|
r.angle = i;
|
|
r.vel.xy = (cos(i),sin(i))*r.speed;
|
|
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;
|
|
}
|
|
}
|