2778 lines
76 KiB
Text
2778 lines
76 KiB
Text
Class UTPlayer : DoomPlayer
|
|
{
|
|
bool lastground;
|
|
int lastwaterlevel;
|
|
int lastgroundtic;
|
|
double lastvelz, prevvelz;
|
|
transient CVar footsteps;
|
|
Vector2 acceleration;
|
|
Vector3 acceleration3;
|
|
int last_fm, last_sm;
|
|
int last_fm_tap, last_sm_tap;
|
|
int last_tap_fm, last_tap_sm;
|
|
int last_jump_held;
|
|
|
|
int tempslide;
|
|
double ssup;
|
|
int corpsetime;
|
|
bool headless, leglessR, leglessL, armlessR, armlessL, torsoless;
|
|
// these are gibber hints to disable spawning some parts
|
|
// headless: missing head
|
|
// leglessR: missing right leg
|
|
// leglessL: mising left leg
|
|
// armlessR: missing right arm
|
|
// armlessL: mising left arm
|
|
// torsoless: missing torso
|
|
|
|
int dolltype, voicetype;
|
|
|
|
Property DollType : dolltype;
|
|
Property VoiceType : voicetype;
|
|
|
|
enum EDollType
|
|
{
|
|
DOLL_Male,
|
|
DOLL_Female,
|
|
DOLL_Boss
|
|
};
|
|
|
|
enum EVoiceType
|
|
{
|
|
VOICE_None,
|
|
VOICE_MaleOne,
|
|
VOICE_MaleTwo,
|
|
VOICE_FemaleOne,
|
|
VOICE_FemaleTwo,
|
|
VOICE_Boss
|
|
};
|
|
|
|
const groundspeed = 400.;
|
|
const swimspeed = 200.;
|
|
const baseaccelrate = 2048.;
|
|
const walkfactor = 0.3;
|
|
const utaircontrol = 0.35;
|
|
const swimspeed_doomish = 400.;
|
|
const groundspeed_doomish = 600.;
|
|
const dodgez = 210.;
|
|
const utjumpz = 325.;
|
|
const groundfriction = 8.;
|
|
const fluidfriction = 1.2;
|
|
const terminalvelocity = 2500.;
|
|
const slantnormal = 0.7; // slope sliding will have to be handled eventually, but currently due to how much is hardcoded this is impossible
|
|
|
|
Default
|
|
{
|
|
Player.StartItem "Enforcer";
|
|
Player.StartItem "ImpactHammer";
|
|
Player.StartItem "MiniAmmo", 30;
|
|
Player.DamageScreenColor "FF 00 00";
|
|
Player.ViewHeight 46;
|
|
Player.GruntSpeed 20;
|
|
+NOMENU;
|
|
+DONTTRANSLATE;
|
|
UTPlayer.DollType DOLL_Male;
|
|
UTPlayer.VoiceType VOICE_None;
|
|
}
|
|
|
|
// Have to modify the give cheat to handle UT armor
|
|
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 ( name ~== "health" )
|
|
{
|
|
if ( amount > 0 )
|
|
{
|
|
health += amount;
|
|
player.health = health;
|
|
}
|
|
else player.health = health = GetMaxHealth(true);
|
|
}
|
|
if ( giveall || (name ~== "backpack") )
|
|
{
|
|
// Select the correct type of backpack based on the game
|
|
let type = (class<Inventory>)(gameinfo.backpacktype);
|
|
if ( type ) GiveInventory(type,1,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 ( (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") )
|
|
{
|
|
// Doom Tournament just gives the player a shield belt and maximum bonuses
|
|
let belt = Inventory(Spawn("UTShieldBelt"));
|
|
belt.ClearCounters();
|
|
if ( !belt.CallTryPickup(self) ) belt.Destroy();
|
|
let bonus = Inventory(Spawn("UTArmorBonus"));
|
|
bonus.ClearCounters();
|
|
bonus.Amount = bonus.MaxAmount;
|
|
if ( !bonus.CallTryPickup(self) ) bonus.Destroy();
|
|
if ( !giveall ) return;
|
|
}
|
|
if ( giveall || (name ~== "keys") )
|
|
{
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
if ( !(AllActorClasses[i] is "Key") ) continue;
|
|
let keyitem = GetDefaultByType(AllActorClasses[i]);
|
|
if ( keyitem.special1 )
|
|
{
|
|
let item = Inventory(Spawn(AllActorClasses[i]));
|
|
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 ( !(gameinfo.gametype&GAME_Raven)
|
|
&& ((type is "UTActivatable") || (type is "UTActivatableHealth")) )
|
|
continue; // don't give these outside of Heretic/Hexen
|
|
if ( def.Icon.isValid() && (def.MaxAmount > 1) &&
|
|
!(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor"))
|
|
{
|
|
// 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 void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !player || (player.mo != self) ) return;
|
|
if ( flak_utmovement )
|
|
{
|
|
bNOFRICTION = true;
|
|
bNOFRICTIONBOUNCE = true;
|
|
}
|
|
else
|
|
{
|
|
bNOFRICTION = false;
|
|
bNOFRICTIONBOUNCE = false;
|
|
}
|
|
if ( !footsteps ) footsteps = CVar.GetCVar('flak_footsteps',players[consoleplayer]);
|
|
if ( !footsteps.GetBool() || (Health <= 0) ) return;
|
|
double ang = level.time/(20*TICRATE/35.)*360.;
|
|
bool forcefootstep = false;
|
|
if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) )
|
|
{
|
|
player.jumptics = 0;
|
|
if ( lastvelz < -8 )
|
|
{
|
|
double vol = clamp((-lastvelz-8)*0.05,0.01,1.0);
|
|
if ( ((waterlevel > 0) || GetFloorTerrain().IsLiquid) && !bOnMobj ) A_PlaySound("ut/wetsplash",CHAN_AUTO,vol);
|
|
else A_PlaySound("*uland",CHAN_AUTO,vol);
|
|
PlayLanding();
|
|
}
|
|
else forcefootstep = true;
|
|
}
|
|
if ( tempslide )
|
|
{
|
|
tempslide = max(0,tempslide-1);
|
|
if ( !tempslide ) forcefootstep = true;
|
|
}
|
|
if ( forcefootstep || ((abs(sin(ang)) >= 1.0) && player.onground && lastground && (player.jumptics == 0) && (player.cmd.forwardmove || player.cmd.sidemove) && (waterlevel < 2)) )
|
|
{
|
|
double vol = abs(vel.xy.length())*0.03;
|
|
if ( forcefootstep ) vol = clamp(-lastvelz*0.05,0.01,1.0);
|
|
if ( (waterlevel > 0) || GetFloorTerrain().IsLiquid && !bOnMobj ) A_PlaySound("ut/playerfootstepwet",CHAN_5,vol);
|
|
else PlayFootstep(vol);
|
|
}
|
|
if ( (waterlevel >= 2) && (lastwaterlevel < 2) )
|
|
A_PlaySound("ut/wetsplash",CHAN_AUTO);
|
|
else if ( (waterlevel < 2) && (lastwaterlevel >= 2) )
|
|
A_PlaySound("ut/wetsurface",CHAN_AUTO);
|
|
lastground = player.onground;
|
|
lastvelz = prevvelz;
|
|
prevvelz = vel.z;
|
|
lastwaterlevel = waterlevel;
|
|
}
|
|
|
|
double FrictionToUnreal()
|
|
{
|
|
double fin = GetFriction();
|
|
if ( fin >= 1.0 ) return 0.0;
|
|
return 734.2969*fin*fin-1485.0868*fin+750.7899;
|
|
}
|
|
|
|
override void CalcHeight()
|
|
{
|
|
if ( !flak_utmovement || !player || (player.mo != self) )
|
|
{
|
|
Super.CalcHeight();
|
|
return;
|
|
}
|
|
double angle, bob;
|
|
bool still = false;
|
|
// no bobbing while:
|
|
// - using noclip2 (equivalent to unreal's ghost cheat)
|
|
// - flying
|
|
// - swimming
|
|
// - falling
|
|
if ( !bNoGravity && player.onground && (waterlevel < 2) )
|
|
{
|
|
player.bob = player.Vel dot player.Vel;
|
|
if ( player.bob == 0 ) still = true;
|
|
else
|
|
{
|
|
player.bob *= player.GetMoveBob();
|
|
if ( player.bob > MAXBOB ) player.bob = MAXBOB;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this still doesn't help because fly bob is hardcoded
|
|
player.bob = 0;
|
|
}
|
|
double defaultviewheight = ViewHeight+player.crouchviewdelta;
|
|
if ( player.cheats&CF_NOVELOCITY )
|
|
{
|
|
player.viewz = pos.z+defaultviewheight;
|
|
if ( player.viewz > ceilingz-4 ) player.viewz = ceilingz-4;
|
|
return;
|
|
}
|
|
if ( still )
|
|
{
|
|
if ( player.health > 0 )
|
|
{
|
|
angle = Level.maptime/(120*TICRATE/35.)*360.;
|
|
bob = player.GetStillBob()*sin(angle);
|
|
}
|
|
else bob = 0;
|
|
}
|
|
else
|
|
{
|
|
angle = Level.maptime/(20*TICRATE/35.)*360.;
|
|
bob = player.bob*sin(angle)*((waterlevel>1)?0.25:0.5);
|
|
}
|
|
// move viewheight
|
|
if ( player.playerstate == PST_LIVE )
|
|
{
|
|
player.viewheight += player.deltaviewheight;
|
|
if ( player.viewheight > defaultviewheight )
|
|
{
|
|
player.viewheight = defaultviewheight;
|
|
player.deltaviewheight = 0;
|
|
}
|
|
else if ( player.viewheight < (defaultviewheight/2) )
|
|
{
|
|
player.viewheight = defaultviewheight/2;
|
|
if ( player.deltaviewheight <= 0 )
|
|
player.deltaviewheight = 1/65536.;
|
|
}
|
|
if ( player.deltaviewheight )
|
|
{
|
|
player.deltaviewheight += 0.25;
|
|
if ( !player.deltaviewheight )
|
|
player.deltaviewheight = 1/65536.;
|
|
}
|
|
}
|
|
if ( player.morphTics ) bob = 0;
|
|
player.viewz = pos.z+player.viewheight+(bob*clamp(ViewBob,0.,1.5)); // [SP] Allow DECORATE changes to view bobbing speed.
|
|
// handle smooth step down (hacky but looks ok)
|
|
player.viewz += ssup;
|
|
ssup = max(0,(ssup*0.7)-0.25);
|
|
if ( floorclip && (player.playerstate != PST_DEAD) && (pos.z <= floorz) )
|
|
player.viewz -= Floorclip;
|
|
if ( player.viewz > ceilingz-4 ) player.viewz = ceilingz-4;
|
|
if ( player.viewz < floorz+4 ) player.viewz = floorz+4;
|
|
}
|
|
|
|
override void MovePlayer()
|
|
{
|
|
if ( !flak_utmovement || !player || (player.mo != self) )
|
|
{
|
|
Super.MovePlayer();
|
|
return;
|
|
}
|
|
bNODROPOFF = false;
|
|
UserCmd cmd = player.cmd;
|
|
if ( player.turnticks )
|
|
{
|
|
player.turnticks--;
|
|
Angle += (180./TURN180_TICKS);
|
|
}
|
|
else Angle += cmd.yaw*(360./65536.);
|
|
player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
|
|
// slant (aka steep slope) detection
|
|
// TODO make it 3d floor aware when the PR makes it in
|
|
if ( floorsector.floorplane.normal dot (0,0,1) < (46342./65536.) )
|
|
player.onground = false;
|
|
if ( player.onground ) lastgroundtic = gametic;
|
|
if ( !player.onground && !bNoGravity && (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;
|
|
}
|
|
double friction = FrictionToUnreal();
|
|
double fs = TweakSpeeds(1.0,0.0);
|
|
if ( !flak_doomspeed )
|
|
{
|
|
if ( cmd.buttons&BT_SPEED ) fs *= walkfactor;
|
|
}
|
|
else fs *= max(abs(cmd.forwardmove/12800.),abs(cmd.sidemove/10240.));
|
|
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
|
|
acceleration = rotatevector((cmd.forwardmove,-cmd.sidemove),angle);
|
|
double accelrate = baseaccelrate*fs;
|
|
Vector2 dodge = (0,0);
|
|
int fm = cmd.forwardmove;
|
|
int sm = cmd.sidemove;
|
|
if ( fm )
|
|
{
|
|
int clk = abs(gametic-last_fm_tap);
|
|
if ( (clk < flak_taptics) && (last_fm*fm == 0) && (last_tap_fm*fm>0) )
|
|
dodge += RotateVector((fm,0),angle).unit();
|
|
if ( !last_fm && (last_jump_held < gametic-1) )
|
|
{
|
|
last_fm_tap = gametic;
|
|
last_tap_fm = fm;
|
|
}
|
|
}
|
|
last_fm = fm;
|
|
if ( sm )
|
|
{
|
|
int clk = abs(gametic-last_sm_tap);
|
|
if ( (clk < flak_taptics) && (last_sm*sm == 0) && (last_tap_sm*sm>0) )
|
|
dodge += RotateVector((0,-sm),angle).unit();
|
|
if ( !last_sm && (last_jump_held < gametic-1) )
|
|
{
|
|
last_sm_tap = gametic;
|
|
last_tap_sm = sm;
|
|
}
|
|
}
|
|
last_sm = sm;
|
|
if ( !bNoGravity && player.onground && (waterlevel < 2) )
|
|
{
|
|
if ( flak_tapdodge && (dodge.length() > 0) && !tempslide )
|
|
{
|
|
if ( level.IsJumpingAllowed() )
|
|
{
|
|
if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*1.5)/TICRATE;
|
|
else vel += dodge.unit()*(groundspeed*1.5)/TICRATE;
|
|
vel.z += dodgez/TICRATE;
|
|
}
|
|
else
|
|
{
|
|
if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*2.0)/TICRATE;
|
|
else vel += dodge.unit()*(groundspeed*2.0)/TICRATE;
|
|
tempslide = 8;
|
|
}
|
|
bOnMobj = false;
|
|
if ( !(player.cheats&CF_PREDICTING) )
|
|
A_PlaySound("*jump",CHAN_BODY);
|
|
if ( player.cheats & CF_REVERTPLEASE )
|
|
{
|
|
player.cheats &= ~CF_REVERTPLEASE;
|
|
player.camera = player.mo;
|
|
}
|
|
player.vel *= 0;
|
|
player.jumptics = -2;
|
|
}
|
|
else
|
|
{
|
|
if ( flak_nowalkdrop && !tempslide )
|
|
bNODROPOFF = ((acceleration.length() > double.epsilon) && (cmd.buttons&BT_SPEED));
|
|
// Hook in Unreal physics
|
|
Vector2 dir = (0,0);
|
|
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
|
|
double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0);
|
|
if ( tempslide ) friction *= 0;
|
|
if ( acceleration.length() <= double.epsilon )
|
|
{
|
|
Vector2 oldvel = vel.xy;
|
|
vel.xy = vel.xy - (2 * dir) * vel.xy.length() * friction/TICRATE;
|
|
if ( oldvel dot vel.xy < 0.0 ) vel.xy *= 0;
|
|
}
|
|
else
|
|
{
|
|
Vector2 acceldir = acceleration.unit();
|
|
acceleration = acceldir * Min(acceleration.length(), accelrate/TICRATE);
|
|
vel.xy = vel.xy - (dir - acceldir) * vel.xy.length() * friction/TICRATE;
|
|
}
|
|
vel.xy = vel.xy + acceleration/TICRATE;
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
|
|
else maxvel = groundspeed/TICRATE;
|
|
maxvel *= fs*doomfriction;
|
|
// TODO attempt to replicate walk on ice velocity increase glitch
|
|
if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel;
|
|
if ( !(player.cheats & CF_PREDICTING) )
|
|
{
|
|
if ( acceleration.length() <= double.epsilon ) PlayIdle();
|
|
else PlayRunning();
|
|
}
|
|
if ( tempslide ) player.vel *= 0;
|
|
else player.vel = vel.xy;
|
|
}
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 2) )
|
|
{
|
|
// air acceleration when falling
|
|
float maxaccel = accelrate/TICRATE;
|
|
if ( vel.xy.length() < (40./TICRATE) )
|
|
maxaccel += (40.-vel.xy.length())/TICRATE;
|
|
if ( acceleration.length() > maxaccel )
|
|
acceleration = acceleration.unit()*maxaccel;
|
|
Vector2 dir = (0,0);
|
|
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
|
|
if ( acceleration.length() > double.epsilon )
|
|
{
|
|
Vector2 acceldir = acceleration.unit();
|
|
acceleration = acceldir * Min(acceleration.length(), accelrate/TICRATE);
|
|
}
|
|
acceleration *= flak_doomaircontrol?level.aircontrol:utaircontrol;
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = (groundspeed_doomish*fs)/TICRATE;
|
|
else maxvel = (groundspeed*fs)/TICRATE;
|
|
// if new velocity is higher than ground speed, steer but don't increase it
|
|
if ( (vel.xy+acceleration/TICRATE).length() > maxvel )
|
|
{
|
|
double vsiz = vel.xy.length();
|
|
vel.xy = (vel.xy+acceleration/TICRATE).unit()*vsiz;
|
|
}
|
|
else vel.xy = vel.xy+acceleration/TICRATE;
|
|
if ( vel.length() > terminalvelocity/TICRATE ) vel = vel.unit()*(terminalvelocity/TICRATE);
|
|
player.vel *= 0;
|
|
player.jumptics = -2;
|
|
if ( !(player.cheats & CF_PREDICTING) ) PlayIdle();
|
|
}
|
|
else if ( (bFly && bFlyCheat) || (player.cheats&CF_NOCLIP2) )
|
|
{
|
|
// fly cheat has infinite friction (player stops moving when movement keys are released)
|
|
Vector3 dir = (0,0,0);
|
|
if ( vel.length() > double.epsilon ) dir = vel.unit();
|
|
Vector3 x, y;
|
|
[x, y] = dt_CoordUtil.GetAxes(pitch,angle,0);
|
|
acceleration3 = x*player.cmd.forwardmove+y*player.cmd.sidemove;
|
|
if ( player.cmd.buttons&BT_JUMP ) acceleration3.z = 0x500;
|
|
else if ( player.cmd.buttons&BT_CROUCH ) acceleration3.z = -0x500;
|
|
if ( acceleration3.length() <= double.epsilon ) vel *= 0;
|
|
else
|
|
{
|
|
Vector3 acceldir = acceleration3.unit();
|
|
acceleration3 = acceldir*Min(acceleration3.length(),accelrate/TICRATE);
|
|
vel = vel-(dir-acceldir)*vel.length();
|
|
}
|
|
vel = vel+acceleration3/TICRATE;
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
|
|
else maxvel = groundspeed/TICRATE;
|
|
maxvel *= fs;
|
|
if ( vel.length() > maxvel ) vel = vel.unit()*maxvel;
|
|
player.vel *= 0;
|
|
player.jumptics = -2;
|
|
if ( !(player.cheats & CF_PREDICTING) ) PlayIdle();
|
|
}
|
|
else
|
|
{
|
|
// swimming is pretty much like ground movement, but with much reduced friction and lower speed
|
|
friction *= fluidfriction/groundfriction;
|
|
Vector3 dir = (0,0,0);
|
|
if ( vel.length() > double.epsilon ) dir = vel.unit();
|
|
double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0);
|
|
Vector3 x, y;
|
|
[x, y] = dt_CoordUtil.GetAxes(pitch,angle,0);
|
|
acceleration3 = x*player.cmd.forwardmove+y*player.cmd.sidemove;
|
|
if ( player.cmd.buttons&BT_JUMP ) acceleration3.z = 0x500;
|
|
else if ( player.cmd.buttons&BT_CROUCH ) acceleration3.z = -0x500;
|
|
if ( acceleration3.length() <= double.epsilon )
|
|
{
|
|
Vector3 oldvel = vel;
|
|
vel = vel-(2*dir)*vel.length()*friction/TICRATE;
|
|
if ( oldvel dot vel < 0.0 ) vel *= 0;
|
|
}
|
|
else
|
|
{
|
|
Vector3 acceldir = acceleration3.unit();
|
|
acceleration3 = acceldir*Min(acceleration3.length(),accelrate/TICRATE);
|
|
vel = vel-(dir-acceldir)*vel.length()*friction/TICRATE;
|
|
}
|
|
vel = vel+acceleration3/TICRATE;
|
|
double maxvel;
|
|
if ( waterlevel < 2 ) // flying uses ground speed
|
|
{
|
|
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
|
|
else maxvel = groundspeed/TICRATE;
|
|
}
|
|
else
|
|
{
|
|
if ( flak_doomspeed ) maxvel = swimspeed_doomish/TICRATE;
|
|
else maxvel = swimspeed/TICRATE;
|
|
}
|
|
maxvel *= fs*doomfriction;
|
|
if ( vel.length() > maxvel ) vel = vel.unit()*maxvel;
|
|
player.vel = vel.xy;
|
|
player.jumptics = -2;
|
|
if ( !(player.cheats & CF_PREDICTING) )
|
|
{
|
|
if ( acceleration3.length() <= double.epsilon ) PlayIdle();
|
|
else PlayRunning();
|
|
}
|
|
}
|
|
if ( player.cheats & CF_REVERTPLEASE )
|
|
{
|
|
player.cheats &= ~CF_REVERTPLEASE;
|
|
player.camera = player.mo;
|
|
}
|
|
}
|
|
override void CheckCrouch( bool totallyfrozen )
|
|
{
|
|
if ( !flak_utmovement || !player || (player.mo != self) )
|
|
{
|
|
Super.CheckCrouch(totallyfrozen);
|
|
return;
|
|
}
|
|
if ( player.cmd.buttons&BT_JUMP ) player.cmd.buttons &= ~BT_CROUCH;
|
|
if ( CanCrouch() && (player.health > 0) && level.IsCrouchingAllowed() && player.onground ) // in UT you can't crouch unless you're on the ground
|
|
{
|
|
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 > 0.5 ))
|
|
CrouchMove(-1);
|
|
}
|
|
}
|
|
else player.Uncrouch();
|
|
player.crouchoffset = -(ViewHeight)*(1-player.crouchfactor);
|
|
}
|
|
override void CheckJump()
|
|
{
|
|
if ( !flak_utmovement || !player || (player.mo != self) )
|
|
{
|
|
Super.CheckJump();
|
|
return;
|
|
}
|
|
if ( player.cmd.buttons&BT_JUMP )
|
|
{
|
|
if ( player.crouchoffset ) player.crouching = 1;
|
|
else if ( level.IsJumpingAllowed() && player.onground && (player.jumpTics == 0) && (last_jump_held < gametic-1) && !bNoGravity && (waterlevel < 2) && !(player.cheats&(CF_FLY|CF_NOCLIP2)) )
|
|
{
|
|
double jumpvelz;
|
|
if ( flak_doomspeed ) jumpvelz = jumpz;
|
|
else jumpvelz = utjumpz/TICRATE;
|
|
double jumpfac = 0;
|
|
for ( let p = Inv; p != null; p = p.Inv )
|
|
{
|
|
let pp = PowerHighJump(p);
|
|
if ( !pp ) continue;
|
|
double f = pp.Strength;
|
|
if ( f > jumpfac ) jumpfac = f;
|
|
}
|
|
if ( jumpfac > 0 ) jumpvelz *= jumpfac;
|
|
Vel.z += jumpvelz;
|
|
bOnMobj = false;
|
|
player.jumpTics = -1;
|
|
if ( !(player.cheats&CF_PREDICTING) )
|
|
A_PlaySound("*jump",CHAN_BODY);
|
|
}
|
|
last_jump_held = gametic;
|
|
}
|
|
if ( !player.onground || player.jumptics )
|
|
last_jump_held = gametic;
|
|
}
|
|
|
|
override void DeathThink()
|
|
{
|
|
Super.DeathThink();
|
|
if ( !flak_utmovement ) return;
|
|
// unreal physics while dead
|
|
double friction = FrictionToUnreal();
|
|
if ( !bNoGravity && player.onground && (waterlevel < 2) )
|
|
{
|
|
// Hook in Unreal physics
|
|
Vector2 dir = (0,0);
|
|
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
|
|
double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0);
|
|
Vector2 oldvel = vel.xy;
|
|
vel.xy = vel.xy - (2 * dir) * vel.xy.length() * friction/TICRATE;
|
|
if ( oldvel dot vel.xy < 0.0 ) vel.xy *= 0;
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
|
|
else maxvel = groundspeed/TICRATE;
|
|
maxvel *= doomfriction;
|
|
if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel;
|
|
player.vel *= 0;
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 2) )
|
|
{
|
|
// air acceleration when falling
|
|
Vector2 dir = (0,0);
|
|
if ( vel.xy.length() > double.epsilon ) dir = vel.xy.unit();
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
|
|
else maxvel = groundspeed/TICRATE;
|
|
// if new velocity is higher than ground speed, steer but don't increase it
|
|
if ( vel.xy.length() > maxvel )
|
|
{
|
|
double vsiz = vel.xy.length();
|
|
vel.xy = vel.xy.unit()*vsiz;
|
|
}
|
|
if ( vel.length() > terminalvelocity/TICRATE ) vel = vel.unit()*(terminalvelocity/TICRATE);
|
|
player.vel *= 0;
|
|
}
|
|
else
|
|
{
|
|
// swimming is pretty much like ground movement, but with much reduced friction and lower speed
|
|
friction *= fluidfriction/groundfriction;
|
|
Vector3 dir = (0,0,0);
|
|
if ( vel.length() > double.epsilon ) dir = vel.unit();
|
|
double doomfriction = clamp(GetFriction()/ORIG_FRICTION,0.0,1.0);
|
|
Vector3 oldvel = vel;
|
|
vel = vel-(2*dir)*vel.length()*friction/TICRATE;
|
|
if ( oldvel dot vel < 0.0 ) vel *= 0;
|
|
double maxvel;
|
|
if ( flak_doomspeed ) maxvel = swimspeed_doomish/TICRATE;
|
|
else maxvel = swimspeed/TICRATE;
|
|
maxvel *= doomfriction;
|
|
if ( vel.length() > maxvel ) vel = vel.unit()*maxvel;
|
|
player.vel *= 0;
|
|
}
|
|
}
|
|
|
|
virtual void PlayFootstep( double vol )
|
|
{
|
|
A_PlaySound("ut/playerfootstep",CHAN_5,vol);
|
|
}
|
|
|
|
override void PlayIdle()
|
|
{
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) )
|
|
{
|
|
// Crouching
|
|
if ( !InStateSequence(CurState,FindState("SpawnCrouch")) )
|
|
SetStateLabel("SpawnCrouch");
|
|
}
|
|
else if ( !bNoGravity && player.onground && (waterlevel < 2) )
|
|
{
|
|
// Ground
|
|
if ( (player && (player.mo == self)) && player.cmd.yaw )
|
|
{
|
|
if ( InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("Spawn")+1) )
|
|
SetStateLabel("Turn");
|
|
}
|
|
else
|
|
{
|
|
if ( InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch")) )
|
|
SetStateLabel("Spawn");
|
|
}
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 2) )
|
|
{
|
|
// Falling
|
|
if ( InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
SetStateLabel("SpawnAir");
|
|
}
|
|
else
|
|
{
|
|
// Swimming
|
|
if ( InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
SetStateLabel("SpawnSwim");
|
|
}
|
|
}
|
|
|
|
override void PlayRunning()
|
|
{
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) )
|
|
{
|
|
// Crouching
|
|
if ( !InStateSequence(CurState,FindState("SeeCrouch")) )
|
|
SetStateLabel("SeeCrouch");
|
|
}
|
|
else if ( !bNoGravity && player.onground && (waterlevel < 2) )
|
|
{
|
|
// Ground
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
SetStateLabel("See2");
|
|
}
|
|
else if ( !bNoGravity && (waterlevel < 2) )
|
|
{
|
|
// Falling
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeSwim"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
SetStateLabel("SeeAir");
|
|
}
|
|
else
|
|
{
|
|
// Swimming
|
|
if ( InStateSequence(CurState,FindState("Spawn"))
|
|
|| InStateSequence(CurState,FindState("SpawnAir"))
|
|
|| InStateSequence(CurState,FindState("SpawnSwim"))
|
|
|| InStateSequence(CurState,FindState("SpawnCrouch"))
|
|
|| InStateSequence(CurState,FindState("See2"))
|
|
|| InStateSequence(CurState,FindState("SeeAir"))
|
|
|| InStateSequence(CurState,FindState("SeeCrouch"))
|
|
|| InStateSequence(CurState,FindState("Turn")) )
|
|
SetStateLabel("SeeSwim");
|
|
}
|
|
}
|
|
|
|
override void PlayAttacking()
|
|
{
|
|
// no animation if crouched
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
// check weapon type
|
|
if ( (player.ReadyWeapon is 'SniperRifle') && (player.buttons&BT_ALTATTACK) )
|
|
return;
|
|
if ( ((player.ReadyWeapon is 'BioRifle') && (player.buttons&BT_ALTATTACK))
|
|
|| (player.ReadyWeapon is 'UTRocketLauncher') )
|
|
{
|
|
if ( !InStateSequence(CurState,FindState("MissileRepStill")) )
|
|
SetStateLabel("MissileRepStill");
|
|
}
|
|
else if ( ((player.ReadyWeapon is 'ImpactHammer') && (player.buttons&BT_ATTACK))
|
|
|| ((player.ReadyWeapon is 'UTChainsaw') && (player.buttons&BT_ATTACK))
|
|
|| (player.ReadyWeapon is 'PulseGun')
|
|
|| (player.ReadyWeapon is 'Minigun') )
|
|
{
|
|
if ( !InStateSequence(CurState,FindState("MissileRep")) )
|
|
SetStateLabel("MissileRep");
|
|
}
|
|
else SetStateLabel("Missile");
|
|
}
|
|
|
|
override void PlayAttacking2()
|
|
{
|
|
PlayAttacking();
|
|
}
|
|
|
|
virtual void PlayAttacking3()
|
|
{
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
SetStateLabel("Missile");
|
|
}
|
|
|
|
virtual void PlayReloading()
|
|
{
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
SetStateLabel("Reload");
|
|
}
|
|
|
|
virtual void PlayLanding()
|
|
{
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
SetStateLabel("Landing");
|
|
}
|
|
|
|
void A_HeadPop()
|
|
{
|
|
headless = true;
|
|
Class<UTHead> hclass = "UTHeadMale";
|
|
if ( DollType == DOLL_Boss ) hclass = "UTHeadBoss";
|
|
else if ( DollType == DOLL_Female ) hclass = "UTHeadFemale";
|
|
let h = UTHead(Spawn(hclass,Vec3Offset(0,0,viewheight)));
|
|
if ( player )
|
|
{
|
|
h.headowner = player;
|
|
player.camera = h;
|
|
}
|
|
double ang = FRandom[Blod](0,360);
|
|
double pt = FRandom[Blod](-90,-30);
|
|
Vector3 dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
h.vel = vel*0.6+dir*FRandom[Blod](8.0,12.0);
|
|
}
|
|
|
|
void A_GibMe()
|
|
{
|
|
let a = Actor.Spawn("UTPlayerGibber",pos);
|
|
a.vel = vel;
|
|
a.Scale = Scale;
|
|
a.A_SetSize(radius,height);
|
|
UTGibber(a).Gibbed = self;
|
|
}
|
|
|
|
void A_DMFade()
|
|
{
|
|
if ( !deathmatch || player ) return;
|
|
corpsetime++;
|
|
if ( corpsetime > 350 ) A_FadeOut(0.03);
|
|
}
|
|
|
|
override double GetDeathHeight()
|
|
{
|
|
// no height reduction while still being zapped
|
|
if ( DamageType == 'Zapped' ) return height;
|
|
return Super.GetDeathHeight();
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
#### # 5;
|
|
PLAY ABCDEFG 8;
|
|
Goto Spawn+1;
|
|
Turn:
|
|
#### # 6;
|
|
PLYT A 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn");
|
|
PLYT A 6;
|
|
PLYT A 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn");
|
|
PLYT A 6;
|
|
PLYT B 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn");
|
|
PLYT B 6;
|
|
PLYT B 0 A_JumpIf(!player||!player.cmd.yaw,"Spawn");
|
|
PLYT B 6;
|
|
Goto Turn+1;
|
|
SpawnAir:
|
|
#### # 5;
|
|
PLYA A 50;
|
|
PLYL A 50;
|
|
Goto SpawnAir+1;
|
|
SpawnSwim:
|
|
#### # 5;
|
|
PLYS ABCDEFG 6;
|
|
Goto SpawnSwim+1;
|
|
SpawnCrouch:
|
|
#### # 5;
|
|
PLYC A -1;
|
|
Wait;
|
|
// See state needs to be defined on each subclass for the selection menu
|
|
See2:
|
|
#### # 5;
|
|
PLAY HIJKL 4;
|
|
Goto See2+1;
|
|
SeeAir:
|
|
#### # 5;
|
|
PLYA A 50;
|
|
PLYL A 50;
|
|
Goto SeeAir+1;
|
|
SeeSwim:
|
|
#### # 5;
|
|
PLYS HIJKL 4;
|
|
Goto SeeSwim+1;
|
|
SeeCrouch:
|
|
#### # 5;
|
|
PLYC ABCDE 4;
|
|
Goto SeeCrouch+1;
|
|
Missile:
|
|
#### # 2;
|
|
PLAY MNOPQA 3;
|
|
Goto Spawn;
|
|
MissileRep:
|
|
#### # 2;
|
|
PLAY R 2;
|
|
PLAY S 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
PLAY S 2;
|
|
PLAY T 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
PLAY T 2;
|
|
PLAY U 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
PLAY U 2;
|
|
PLAY V 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
PLAY V 2;
|
|
PLAY R 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
Goto MissileRep+1;
|
|
MissileRepStill:
|
|
#### # 2;
|
|
PLAY R 2;
|
|
PLAY R 0 A_JumpIf(!player||!(player.buttons&(BT_ATTACK|BT_ALTATTACK)),"Spawn");
|
|
Goto MissileRepStill+1;
|
|
Reload:
|
|
#### # 3;
|
|
PLYR ABCDEFGH 4;
|
|
Goto Spawn;
|
|
Landing:
|
|
#### # 4;
|
|
PLYL A 3;
|
|
Goto Spawn;
|
|
Pain:
|
|
#### # 0 A_Jump(256,1,3,5,7);
|
|
#### # 2;
|
|
PLAY W 3 A_Pain();
|
|
Goto Spawn;
|
|
#### # 2;
|
|
PLAY Y 3 A_Pain();
|
|
Goto Spawn;
|
|
#### # 2;
|
|
PLAY Z 3 A_Pain();
|
|
Goto Spawn;
|
|
Pain.Decapitated:
|
|
#### # 2;
|
|
PLAY X 3 A_Pain();
|
|
Goto Spawn;
|
|
Death.Decapitated:
|
|
#### # 0 A_HeadPop();
|
|
PLD4 A 3 A_PlayerScream();
|
|
PLD4 B 3 A_NoBlocking();
|
|
PLD4 CDEFGHIJKLMNO 3;
|
|
PLD4 P 1 A_DMFade();
|
|
Wait;
|
|
Death:
|
|
#### # 0 A_JumpIf(vel.length()>20,"Death11");
|
|
#### # 0 A_JumpIf(vel.length()>10,"Death1");
|
|
#### # 0 A_Jump(256,"Death2","Death3","Death7","Death8");
|
|
Death11:
|
|
#### # 3;
|
|
PD11 A 3 A_PlayerScream();
|
|
PD11 B 3 A_NoBlocking();
|
|
PD11 CDEFGHIJKLMNOPQ 3;
|
|
PD11 R 1 A_DMFade();
|
|
Wait;
|
|
Death.Zapped:
|
|
#### # 3;
|
|
PLD9 A 3 A_PlayerScream();
|
|
PLD9 B 3 A_NoBlocking();
|
|
PLD9 CDEFGHIJKLMNOPQRST 2;
|
|
PD9B A 2 A_SetSize(-1,height*0.25); // reduce now
|
|
PD9B BCDEFGHI 2;
|
|
PD9B J 1 A_DMFade();
|
|
Wait;
|
|
Death8:
|
|
#### # 3;
|
|
PLD8 A 3 A_PlayerScream();
|
|
PLD8 B 3 A_NoBlocking();
|
|
PLD8 CDEFGHIJKLMNOPQ 3;
|
|
PLD8 R 1 A_DMFade();
|
|
Wait;
|
|
Death7:
|
|
#### # 3;
|
|
PLD7 A 3 A_PlayerScream();
|
|
PLD7 B 3 A_NoBlocking();
|
|
PLD7 CDEFGHIJKLMNOPQRST 3;
|
|
PLD7 U 1 A_DMFade();
|
|
Wait;
|
|
Death3:
|
|
#### # 3;
|
|
PLD3 A 3 A_PlayerScream();
|
|
PLD3 B 3 A_NoBlocking();
|
|
PLD3 CDEFGHIJKL 3;
|
|
PLD3 M 1 A_DMFade();
|
|
Wait;
|
|
Death2:
|
|
#### # 3;
|
|
PLD2 A 3 A_PlayerScream();
|
|
PLD2 B 3 A_NoBlocking();
|
|
PLD2 CDEFGHIJKLMNO 3;
|
|
PLD2 P 1 A_DMFade();
|
|
Wait;
|
|
Death1:
|
|
#### # 3;
|
|
PLD1 A 3 A_PlayerScream();
|
|
PLD1 B 3 A_NoBlocking();
|
|
PLD1 CDEFGHIJKL 3;
|
|
PLD1 M 1 A_DMFade();
|
|
Wait;
|
|
XDeath:
|
|
TNT1 A 350
|
|
{
|
|
A_XScream();
|
|
A_NoBlocking();
|
|
A_GibMe();
|
|
}
|
|
TNT1 A 1 A_CheckPlayerDone();
|
|
Wait;
|
|
Taunt1:
|
|
#### # 5;
|
|
PLT1 ABCDEFGHIJKLMNOPQRST 3;
|
|
Goto Spawn;
|
|
Taunt2:
|
|
#### # 5;
|
|
PLT2 ABCDEFGHIJKLMNOPQR 4;
|
|
PLT2 R 8;
|
|
PLAY A 3;
|
|
Goto Spawn;
|
|
Taunt3:
|
|
#### # 5;
|
|
PLT3 ABCDEFGHIJKLMNO 3;
|
|
Goto Spawn;
|
|
Taunt4:
|
|
#### # 5;
|
|
PLT4 ABCDEFGHIJKLMNO 3;
|
|
Goto Spawn;
|
|
}
|
|
}
|
|
|
|
// these only exist for sound
|
|
// female classes have identical sounds, so they use the same soundclass here
|
|
Class UTPlayerTMale1 : UTPlayer
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tmale1";
|
|
Player.DisplayName "$N_TMALE1";
|
|
Player.Portrait "Blake";
|
|
UTPlayer.VoiceType VOICE_MaleOne;
|
|
-NOMENU;
|
|
}
|
|
}
|
|
Class UTPlayerTMale2 : UTPlayer
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tmale2";
|
|
Player.DisplayName "$N_TMALE2";
|
|
Player.Portrait "Brock";
|
|
UTPlayer.VoiceType VOICE_MaleTwo;
|
|
-NOMENU;
|
|
}
|
|
}
|
|
Class UTPlayerTFemale : UTPlayer
|
|
{
|
|
Default
|
|
{
|
|
UTPlayer.DollType DOLL_Female;
|
|
}
|
|
|
|
void A_LegPop()
|
|
{
|
|
leglessR = true;
|
|
let a = Actor.Spawn("UTFemaleLegGibber",pos);
|
|
a.vel = vel;
|
|
a.Scale = Scale;
|
|
a.A_SetSize(radius,height);
|
|
UTGibber(a).Gibbed = self;
|
|
}
|
|
|
|
States
|
|
{
|
|
Reload:
|
|
#### # 3;
|
|
PLYR ABCDEF 4;
|
|
Goto Spawn;
|
|
Taunt1:
|
|
#### # 5;
|
|
PLT1 ABCDEFGHIJKLMNO 3;
|
|
Goto Spawn;
|
|
Taunt2:
|
|
#### # 5;
|
|
PLT2 ABCDEFGHIJ 6;
|
|
PLAY A 3;
|
|
Goto Spawn;
|
|
Death.Decapitated:
|
|
#### # 0 A_HeadPop();
|
|
PLD6 A 3 A_PlayerScream();
|
|
PLD6 B 3 A_NoBlocking();
|
|
PLD6 CDEFGHIJ 3;
|
|
PLD6 K 1 A_DMFade();
|
|
Wait;
|
|
Death.Zapped:
|
|
#### # 3;
|
|
PLD9 A 3 A_PlayerScream();
|
|
PLD9 B 3 A_NoBlocking();
|
|
PLD9 CDEFGHIJKLMNOPQRST 2;
|
|
PD9B A 2 A_SetSize(-1,height*0.25); // reduce now
|
|
PD9B BCDEFGHIJ 2;
|
|
PD9B K 1 A_DMFade();
|
|
Wait;
|
|
Death:
|
|
#### # 0 A_JumpIf(vel.length()>20,"Death5");
|
|
#### # 0 A_JumpIf(vel.length()>10,"Death2");
|
|
#### # 0 A_Jump(256,"Death1","Death3","Death4","Death7");
|
|
Death7:
|
|
#### # 3;
|
|
PLD7 A 3 A_PlayerScream();
|
|
PLD7 B 3 A_NoBlocking();
|
|
PLD7 CDEFGHIJ 3;
|
|
PLD7 K 1 A_DMFade();
|
|
Wait;
|
|
Death5:
|
|
#### # 0 A_LegPop();
|
|
PLD5 A 3 A_PlayerScream();
|
|
PLD5 B 3 A_NoBlocking();
|
|
PLD5 CDEFGHIJKL 3;
|
|
PLD5 M 1 A_DMFade();
|
|
Wait;
|
|
Death4:
|
|
#### # 3;
|
|
PLD4 A 3 A_PlayerScream();
|
|
PLD4 B 3 A_NoBlocking();
|
|
PLD4 CDEFGHIJKL 3;
|
|
PLD4 M 1 A_DMFade();
|
|
Wait;
|
|
Death3:
|
|
#### # 3;
|
|
PLD3 A 3 A_PlayerScream();
|
|
PLD3 B 3 A_NoBlocking();
|
|
PLD3 CDEFGHIJKLMNO 3;
|
|
PLD3 P 1 A_DMFade();
|
|
Wait;
|
|
Death2:
|
|
#### # 3;
|
|
PLD2 A 3 A_PlayerScream();
|
|
PLD2 B 3 A_NoBlocking();
|
|
PLD2 CDEFGHIJKLMNOPQ 3;
|
|
PLD2 R 1 A_DMFade();
|
|
Wait;
|
|
Death1:
|
|
#### # 3;
|
|
PLD1 A 3 A_PlayerScream();
|
|
PLD1 B 3 A_NoBlocking();
|
|
PLD1 CDEFGHIJKLMNOPQRSTUV 3;
|
|
PLD1 W 1 A_DMFade();
|
|
Wait;
|
|
}
|
|
}
|
|
Class UTPlayerTFemale1 : UTPlayerTFemale
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tfemale";
|
|
Player.DisplayName "$N_TFEMALE1";
|
|
Player.Portrait "Ivana";
|
|
UTPlayer.VoiceType VOICE_FemaleOne;
|
|
-NOMENU;
|
|
}
|
|
}
|
|
Class UTPlayerTFemale2 : UTPlayerTFemale
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tfemale";
|
|
Player.DisplayName "$N_TFEMALE2";
|
|
Player.Portrait "Lauren";
|
|
UTPlayer.VoiceType VOICE_FemaleTwo;
|
|
-NOMENU;
|
|
}
|
|
}
|
|
Class UTPlayerTBoss : UTPlayer
|
|
{
|
|
transient CVar bossfootsteps;
|
|
Default
|
|
{
|
|
Player.SoundClass "tboss";
|
|
Player.DisplayName "$N_TBOSS";
|
|
Player.Portrait "Xan";
|
|
UTPlayer.DollType DOLL_Boss;
|
|
UTPlayer.VoiceType VOICE_Boss;
|
|
// should have NOBLOOD, but Xan did bleed in vanilla UT so...
|
|
// (this is what gave birth to the theory that Xan was actually Jerl Liandri himself)
|
|
-NOMENU;
|
|
}
|
|
override void PlayFootstep( double vol )
|
|
{
|
|
if ( !bossfootsteps ) bossfootsteps = CVar.GetCVar('flak_bossfootsteps',players[consoleplayer]);
|
|
if ( bossfootsteps.GetBool() ) A_PlaySound("ut/bossfootstep",CHAN_5,vol);
|
|
else Super.PlayFootstep(vol);
|
|
}
|
|
}
|
|
|
|
Class UTWeapon : Weapon
|
|
{
|
|
int DropAmmo;
|
|
bool bExtraPickup;
|
|
|
|
Property DropAmmo: DropAmmo;
|
|
|
|
// Drawstuffs under HUD
|
|
virtual ui void PreRender( double lbottom ) {}
|
|
// Drawstuffs over HUD
|
|
virtual ui void PostRender( double lbottom ) {}
|
|
// Future preparations for scripted textures
|
|
virtual ui void RenderOverlay( RenderEvent e ) {}
|
|
|
|
override Inventory CreateTossable( int amt )
|
|
{
|
|
if ( Ammo1 && (Ammo1.Amount <= 0) ) return null;
|
|
Inventory d = Super.CreateTossable(amt);
|
|
if ( d && (d.GetClass() == GetClass()) )
|
|
{
|
|
d.SetState(d.ResolveState("Spawn")+1);
|
|
d.bALWAYSPICKUP = true;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
override bool SpecialDropAction( Actor dropper )
|
|
{
|
|
SetState(ResolveState("Spawn")+1);
|
|
bALWAYSPICKUP = true;
|
|
return false;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
// don't slide on floor when dropped
|
|
if ( CurState == ResolveState("Spawn")+1 )
|
|
{
|
|
if ( pos.z <= floorz )
|
|
vel.xy *= 0;
|
|
}
|
|
if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) return;
|
|
Owner.player.WeaponState |= WF_WEAPONBOBBING; // UT weapons always bob
|
|
}
|
|
|
|
virtual void FireEffect()
|
|
{
|
|
let amp = DamageAmplifier(Owner.FindInventory("DamageAmplifier",true));
|
|
if ( amp ) amp.FireEffect();
|
|
}
|
|
|
|
override void OwnerDied()
|
|
{
|
|
Super.OwnerDied();
|
|
A_ClearRefire();
|
|
}
|
|
|
|
override bool HandlePickup( Inventory item )
|
|
{
|
|
if (item.GetClass() == GetClass())
|
|
{
|
|
if ( Weapon(item).PickupForAmmo(self) )
|
|
item.bPickupGood = true;
|
|
if ( (MaxAmount > 1) || bALWAYSPICKUP )
|
|
return Inventory.HandlePickup(item);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
override bool ShouldStay()
|
|
{
|
|
if ( ((multiplayer && (!deathmatch && !alwaysapplydmflags)) || sv_weaponstay) && !bDropped )
|
|
return (!bExtraPickup && !bALWAYSPICKUP);
|
|
return false;
|
|
}
|
|
override bool TryPickup( in out Actor toucher )
|
|
{
|
|
if ( !bExtraPickup ) bExtraPickup = ((MaxAmount > 1) && (toucher.CountInv(GetClass()) < MaxAmount));
|
|
return Super.TryPickup(toucher);
|
|
}
|
|
|
|
// Whole chain of function rewrites because of some stupid hardcoded deathmatch ammo multiplier
|
|
override bool TryPickupRestricted( in out Actor toucher )
|
|
{
|
|
if ( ShouldStay() ) return false;
|
|
bExtraPickup = false;
|
|
bool gaveSome = !!(NonIdioticAddAmmo(toucher,AmmoType1,AmmoGive1));
|
|
gaveSome |= !!(NonIdioticAddAmmo(toucher,AmmoType2,AmmoGive2));
|
|
if ( gaveSome ) GoAwayAndDie();
|
|
return gaveSome;
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
bExtraPickup = false;
|
|
Inventory.AttachToOwner(other);
|
|
Ammo1 = NonIdioticAddAmmo(Owner,AmmoType1,AmmoGive1);
|
|
Ammo2 = NonIdioticAddAmmo(Owner,AmmoType2,AmmoGive2);
|
|
SisterWeapon = AddWeapon(SisterWeaponType);
|
|
if ( Owner.player )
|
|
{
|
|
if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch )
|
|
Owner.player.PendingWeapon = self;
|
|
if ( Owner.player.mo == players[consoleplayer].camera )
|
|
StatusBar.ReceivedWeapon(self);
|
|
}
|
|
GivenAsMorphWeapon = false;
|
|
}
|
|
|
|
// rewrite of AddAmmo without stupid hardcoded 2.5x ammo multiplier
|
|
protected Ammo NonIdioticAddAmmo( Actor other, Class<Ammo> ammotype, int amount )
|
|
{
|
|
if ( !ammotype ) return null;
|
|
Ammo ammoitem;
|
|
if ( !bIgnoreSkill ) amount = int(amount*G_SkillPropertyFloat(SKILLP_AmmoFactor));
|
|
ammoitem = Ammo(other.FindInventory(ammotype));
|
|
if ( !ammoitem )
|
|
{
|
|
ammoitem = Ammo(Spawn(ammotype));
|
|
ammoitem.Amount = min(amount,ammoitem.MaxAmount);
|
|
ammoitem.AttachToOwner(other);
|
|
}
|
|
else if ( ammoitem.Amount < ammoitem.MaxAmount )
|
|
{
|
|
ammoitem.Amount += amount;
|
|
if ( ammoitem.Amount > ammoitem.MaxAmount )
|
|
ammoitem.Amount = ammoitem.MaxAmount;
|
|
}
|
|
return ammoitem;
|
|
}
|
|
|
|
override void OnDrop( Actor dropper )
|
|
{
|
|
Vector2 hofs = RotateVector((dropper.radius,0),dropper.angle);
|
|
SetOrigin(dropper.Vec3Offset(hofs.x,hofs.y,dropper.height*0.5),false);
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(dropper.pitch,dropper.angle,dropper.roll);
|
|
vel = x*12.0;
|
|
vel.z += 4.0;
|
|
angle = dropper.angle;
|
|
pitch = 0;
|
|
roll = 0;
|
|
}
|
|
|
|
Default
|
|
{
|
|
Weapon.BobStyle "Smooth";
|
|
Weapon.BobSpeed 1.5;
|
|
Weapon.BobRangeX 0.2;
|
|
Weapon.BobRangeY 0.4;
|
|
Weapon.YAdjust 0;
|
|
+WEAPON.NOALERT;
|
|
}
|
|
}
|
|
|
|
Class UTTeleportLight : DynamicLight
|
|
{
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 128,160,255,80;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( alpha <= 0 )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
args[LIGHT_RED] = int(128*alpha);
|
|
args[LIGHT_GREEN] = int(160*alpha);
|
|
args[LIGHT_BLUE] = int(255*alpha);
|
|
args[LIGHT_INTENSITY] = Random[Tele](10,14)*8;
|
|
alpha -= 1./35;
|
|
}
|
|
}
|
|
|
|
Class UTItemLight : DynamicLight
|
|
{
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 255,224,160,48;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( alpha <= 0 )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
args[LIGHT_RED] = int(255*alpha);
|
|
args[LIGHT_GREEN] = int(224*alpha);
|
|
args[LIGHT_BLUE] = int(160*alpha);
|
|
args[LIGHT_INTENSITY] = Random[Tele](6,8)*8;
|
|
alpha -= 3./35;
|
|
}
|
|
}
|
|
|
|
Class UTTeleportFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOTELEPORT;
|
|
+NOGRAVITY;
|
|
RenderStyle "Add";
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Spawn("UTTeleportLight",pos+(0,0,16));
|
|
A_PlaySound ("misc/teleport");
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TELE ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 Bright A_FadeOut(1./35);
|
|
TEL2 ABCDEFGHI 1 Bright A_FadeOut(1./35);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTItemFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOTELEPORT;
|
|
+NOGRAVITY;
|
|
RenderStyle "Add";
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Spawn("UTItemLight",pos+(0,0,16));
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTSpark : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+FORCEXYBILLBOARD;
|
|
+MISSILE;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
BounceType "Doom";
|
|
BounceFactor 0.4;
|
|
Gravity 0.2;
|
|
Scale 0.05;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("UTBubble",pos);
|
|
b.vel = vel;
|
|
b.scale *= 0.3;
|
|
Destroy();
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SPRK A 1 Bright A_FadeOut(0.01);
|
|
Wait;
|
|
Death:
|
|
SPRK A 1 Bright A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class UTViewSpark : UTSpark
|
|
{
|
|
Default
|
|
{
|
|
+NOCLIP;
|
|
-MISSILE;
|
|
BounceType "None";
|
|
}
|
|
Vector3 ofs, vvel;
|
|
|
|
override void Tick()
|
|
{
|
|
Actor.Tick();
|
|
if ( !target || !target.player )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
Vector3 origin = x*ofs.x+y*ofs.y+z*ofs.z+(0,0,target.player.viewz);
|
|
SetOrigin(target.Vec2OffsetZ(origin.x,origin.y,origin.z),true);
|
|
bInvisible = (players[consoleplayer].camera != target);
|
|
if ( isFrozen() ) return;
|
|
ofs += vvel;
|
|
vvel.z -= 0.1;
|
|
scale *= 0.8;
|
|
if ( (scale.x <= 0.01) || (waterlevel > 0) ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class UTChip : Actor
|
|
{
|
|
int deadtimer;
|
|
double rollvel, anglevel, pitchvel;
|
|
|
|
Default
|
|
{
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+MISSILE;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
BounceType "Doom";
|
|
BounceFactor 0.3;
|
|
Gravity 0.35;
|
|
Scale 0.2;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
deadtimer = 0;
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
frame = Random[Junk](0,3);
|
|
scale *= Frandom[Junk](0.8,1.2);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( InStateSequence(CurState,ResolveState("Death")) )
|
|
{
|
|
deadtimer++;
|
|
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
CHIP # 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
roll += rollvel;
|
|
}
|
|
Loop;
|
|
Bounce:
|
|
CHIP # 0
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
CHIP # -1;
|
|
Stop;
|
|
Dummy:
|
|
CHIP ABCD -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTBubble : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+NOTELEPORT;
|
|
Scale 0.05;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
|
if ( waterlevel <= 0 ) Destroy();
|
|
SetState(ResolveState("Spawn")+Random[Puff](0,2));
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.05;
|
|
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BUBL ABC -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "FFFFFF";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+CANBOUNCEWATER;
|
|
-BOUNCEAUTOOFF;
|
|
BounceType "Hexen";
|
|
BounceFactor 1.0;
|
|
WallBounceFactor 1.0;
|
|
Scale 0.5;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.01;
|
|
A_FadeOut(1/32.);
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("UTBubble",pos);
|
|
b.vel = vel;
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay A_Jump(255,"US1","US2","US3","US4","US5","US6","US7","US8","US9","US10");
|
|
Stop;
|
|
US1:
|
|
US1_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US2:
|
|
US2_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US3:
|
|
US3_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US4:
|
|
US4_ ABCDEFGHIJKLMNO 2;
|
|
Stop;
|
|
US5:
|
|
US5_ ABCDEFGHIJKLMNO 2;
|
|
Stop;
|
|
US6:
|
|
US6_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US7:
|
|
US7_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US8:
|
|
US8_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US9:
|
|
US9_ ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
US10:
|
|
US10 ABCDEFGHIJKLMNOP 2;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTSmallSmoke : UTSmoke
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
Actor.PostBeginPlay();
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
}
|
|
}
|
|
|
|
Class UTViewSmoke : UTSmoke
|
|
{
|
|
Vector3 ofs, vvel;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Actor.PostBeginPlay();
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vvel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Actor.Tick();
|
|
if ( !target || !target.player )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
Vector3 origin = x*ofs.x+y*ofs.y+z*ofs.z+(0,0,target.player.viewz);
|
|
SetOrigin(target.Vec2OffsetZ(origin.x,origin.y,origin.z),true);
|
|
bInvisible = (players[consoleplayer].camera != target);
|
|
if ( isFrozen() ) return;
|
|
ofs += vvel;
|
|
vvel *= 0.96;
|
|
vvel.z += 0.01;
|
|
A_FadeOut(1/32.);
|
|
if ( waterlevel > 0 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class UTStaticViewSmoke : UTViewSmoke
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
Actor.PostBeginPlay();
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
}
|
|
}
|
|
|
|
Class UTRedSkull : RedSkull
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_REDSKULL";
|
|
Species "RedSkull";
|
|
Inventory.PickupMessage "$I_REDSKULL";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
USKL A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTGoldSkull : YellowSkull
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_GOLDSKULL";
|
|
Species "YellowSkull";
|
|
Inventory.PickupMessage "$I_GOLDSKULL";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
USKL B -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTBlueSkull : BlueSkull
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_BLUESKULL";
|
|
Species "BlueSkull";
|
|
Inventory.PickupMessage "$I_BLUESKULL";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
USKL C -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTRedKey : RedCard
|
|
{
|
|
Default
|
|
{
|
|
Tag "%T_REDKEY";
|
|
Species "RedCard";
|
|
Inventory.PickupMessage "$I_REDKEY";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
UKEY A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTGoldKey : YellowCard
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_GOLDKEY";
|
|
Species "YellowCard";
|
|
Inventory.PickupMessage "$I_GOLDKEY";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
UKEY B -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTBlueKey : BlueCard
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_BLUEKEY";
|
|
Species "BlueCard";
|
|
Inventory.PickupMessage "$I_BLUEKEY";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
UKEY C -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class ShredCorpseHitbox : Actor
|
|
{
|
|
int accdamage;
|
|
Vector3 lastvel;
|
|
bool wasonair;
|
|
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
-SOLID;
|
|
+DONTSPLASH;
|
|
+SHOOTABLE;
|
|
+NOTELEPORT;
|
|
Health int.max;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
accdamage = target.Health;
|
|
A_SetSize(target.radius,target.height);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( (!(target is 'UTPlayer') && !flak_corpsedamage) || !target || (target.Health > 0) || target.InStateSequence(target.CurState,target.FindState("XDeath",true)) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos,true);
|
|
A_SetSize(target.radius,target.height);
|
|
if ( target.pos.z > target.floorz ) wasonair = true;
|
|
else
|
|
{
|
|
if ( wasonair )
|
|
{
|
|
A_PlaySound("misc/corpsefall",CHAN_BODY,clamp(-lastvel.z*0.2,0.1,1.0));
|
|
if ( lastvel.z < -20 ) DamageMobj(null,null,int.max,'Falling');
|
|
}
|
|
wasonair = false;
|
|
}
|
|
lastvel = target.vel;
|
|
}
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
// somehow target can spontaneously stop existing while this is happening
|
|
if ( !target ) return 0;
|
|
if ( (target is 'UTPlayer') && (mod == 'Zapped') && (target.sprite == target.GetSpriteIndex('PLD9')) )
|
|
{
|
|
// keep the zapping action on
|
|
target.SetState(target.FindState("Death.Zapped")+Random[ZapMe](2,8));
|
|
damage /= 4;
|
|
// push the corpse
|
|
target.vel.xy += RotateVector((1,0),angle)*damage*0.1;
|
|
// keep it afloat
|
|
target.vel.z = max(0.1,target.vel.z+0.1);
|
|
}
|
|
accdamage -= damage;
|
|
int gibhealth = (target.GibHealth==int.min)?-target.SpawnHealth():target.GibHealth;
|
|
if ( accdamage < gibhealth )
|
|
{
|
|
// force gib (cheap ATM)
|
|
State gib = target.FindState("XDeath",true);
|
|
if ( !gib ) gib = target.FindState("Death.Extreme",true);
|
|
if ( gib ) target.SetState(gib);
|
|
Destroy();
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// imitates UE1 light type LT_TexturePaletteOnce
|
|
Class PaletteLight : DynamicLight
|
|
{
|
|
Color pal[256];
|
|
|
|
Default
|
|
{
|
|
Tag "Explosion";
|
|
DynamicLight.Type "Point";
|
|
Args 0,0,0,80;
|
|
ReactionTime 15;
|
|
}
|
|
private void UpdateLight()
|
|
{
|
|
int index = 255-((255*ReactionTime)/default.ReactionTime);
|
|
args[LIGHT_RED] = pal[index].r;
|
|
args[LIGHT_GREEN] = pal[index].g;
|
|
args[LIGHT_BLUE] = pal[index].b;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",GetTag()));
|
|
String paldat = Wads.ReadLump(lump);
|
|
for ( int i=0; i<256; i++ )
|
|
{
|
|
pal[i].r = paldat.ByteAt(i*3);
|
|
pal[i].g = paldat.ByteAt(i*3+1);
|
|
pal[i].b = paldat.ByteAt(i*3+2);
|
|
}
|
|
UpdateLight();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
A_CountDown();
|
|
UpdateLight();
|
|
}
|
|
}
|
|
|
|
Enum ESwingMode
|
|
{
|
|
SWING_Straight, // constant increment
|
|
SWING_Spring, // bounces back after a delay
|
|
};
|
|
|
|
Class Swinger : Thinker
|
|
{
|
|
Actor target;
|
|
Vector2 dir;
|
|
double inc, rmul;
|
|
int steps, mode, delay;
|
|
double str, tstr;
|
|
int cnt, cstate;
|
|
|
|
Enum ESwingerState
|
|
{
|
|
STATE_Initial,
|
|
STATE_Wait,
|
|
STATE_Return,
|
|
};
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !target ) cstate = -1;
|
|
switch ( cstate )
|
|
{
|
|
case STATE_Initial:
|
|
target.A_SetAngle(target.angle+dir.x*str*flak_swingerstrength,SPF_INTERPOLATE);
|
|
target.A_SetPitch(target.pitch+dir.y*str*flak_swingerstrength,SPF_INTERPOLATE);
|
|
str += inc;
|
|
if ( ++cnt >= steps )
|
|
{
|
|
cnt = 0;
|
|
str = tstr/steps;
|
|
cstate = (mode==SWING_Straight)?(-1):(delay>0)?STATE_Wait:STATE_Return;
|
|
}
|
|
else tstr += str;
|
|
break;
|
|
case STATE_Wait:
|
|
if ( ++cnt >= delay )
|
|
{
|
|
cnt = 0;
|
|
cstate = STATE_Return;
|
|
}
|
|
break;
|
|
case STATE_Return:
|
|
target.A_SetAngle(target.angle-dir.x*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
|
|
target.A_SetPitch(target.pitch-dir.y*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
|
|
if ( ++cnt >= steps*rmul )
|
|
{
|
|
cnt = 0;
|
|
cstate = -1;
|
|
}
|
|
break;
|
|
default:
|
|
Destroy();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Class GenericFlash : HUDMessageBase
|
|
{
|
|
Color col;
|
|
int duration;
|
|
double alpha;
|
|
Actor cam;
|
|
transient CVar str;
|
|
GenericFlash Setup( Actor camera, Color c, int d )
|
|
{
|
|
alpha = 1.0;
|
|
col = c;
|
|
duration = d;
|
|
cam = camera;
|
|
return self;
|
|
}
|
|
override bool Tick()
|
|
{
|
|
if ( duration > 0 ) alpha -= 1./duration;
|
|
return (alpha<=0)||(!cam);
|
|
}
|
|
override void Draw( int bottom, int visibility )
|
|
{
|
|
if ( automapactive || (visibility != BaseStatusBar.HUDMSGLayer_UnderHUD) ) return;
|
|
if ( cam && (players[consoleplayer].camera != cam) ) return;
|
|
if ( !str ) str = CVar.GetCVar('flak_flashstrength',players[consoleplayer]);
|
|
Screen.Dim(col,(col.a/255.)*alpha*str.GetFloat(),0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
}
|
|
}
|
|
|
|
Class QueuedFlash
|
|
{
|
|
Color c;
|
|
int duration;
|
|
int tic;
|
|
Actor cam;
|
|
}
|
|
|
|
Class UTStaticHandler : StaticEventHandler
|
|
{
|
|
ui TextureID tex;
|
|
|
|
ui void StartMenu()
|
|
{
|
|
CVar protomenu = CVar.GetCVar('flak_protomenu',players[consoleplayer]);
|
|
if ( !protomenu ) return; // this can happen
|
|
int proto = protomenu.GetInt();
|
|
if ( proto )
|
|
{
|
|
tex = TexMan.CheckForTexture("protobg",TexMan.Type_Any);
|
|
if ( gamestate != GS_TITLELEVEL ) return;
|
|
if ( proto > 1 ) S_ChangeMusic("menu2");
|
|
else S_ChangeMusic("xyzdMenu");
|
|
}
|
|
else
|
|
{
|
|
tex = TexMan.CheckForTexture("finalbg",TexMan.Type_Any);
|
|
if ( gamestate != GS_TITLELEVEL ) return;
|
|
S_ChangeMusic("utmenu23");
|
|
}
|
|
}
|
|
|
|
override void ConsoleProcess( ConsoleEvent e )
|
|
{
|
|
if ( e.Name ~== "refreshmenu" ) StartMenu();
|
|
}
|
|
|
|
override void PostUiTick()
|
|
{
|
|
if ( gametic <= 0 ) StartMenu();
|
|
}
|
|
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
// well this if sure is a long one
|
|
if ( players[consoleplayer].camera.player && players[consoleplayer].camera.player.ReadyWeapon && (players[consoleplayer].camera.player.ReadyWeapon is 'UTWeapon') )
|
|
UTWeapon(players[consoleplayer].camera.player.ReadyWeapon).RenderOverlay(e);
|
|
if ( !menuactive ) return;
|
|
if ( tex.IsNull() || !tex.IsValid() ) return;
|
|
if ( !CVar.GetCVar('flak_showmenu',players[consoleplayer]).GetBool() ) return;
|
|
Screen.Dim("Black",1.0,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
Screen.DrawTexture(tex,true,0,0,DTA_VirtualWidth,1024,DTA_VirtualHeight,768);
|
|
}
|
|
}
|
|
|
|
Class UTMainHandler : EventHandler
|
|
{
|
|
Array<QueuedFlash> flashes;
|
|
|
|
override void CheckReplacement( ReplaceEvent e )
|
|
{
|
|
if ( (e.Replacee == 'Chainsaw') || (e.Replacee == 'Gauntlets') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'UTChainsaw';
|
|
else e.Replacement = 'Enforcer';
|
|
}
|
|
else if ( (e.Replacee == 'Fist') || (e.Replacee == 'Staff') ) e.Replacement = 'ImpactHammer';
|
|
else if ( (e.Replacee == 'Pistol') || (e.Replacee == 'GoldWand') ) e.Replacement = 'Enforcer';
|
|
else if ( (e.Replacee == 'Shotgun') || (e.Replacee == 'SuperShotgun') || (e.Replacee == 'Crossbow') )
|
|
{
|
|
if ( !Random[Replacements](0,3) ) e.Replacement = 'Enforcer';
|
|
else if ( Random[Replacements](0,1) ) e.Replacement = 'BioRifle';
|
|
else e.Replacement = 'ShockRifle';
|
|
}
|
|
else if ( (e.Replacee == 'Chaingun') || (e.Replacee == 'Blaster') )
|
|
{
|
|
if ( Random[Replacements](0,2) ) e.Replacement = 'PulseGun';
|
|
else e.Replacement = 'Ripper2';
|
|
}
|
|
else if ( (e.Replacee == 'RocketLauncher') || (e.Replacee == 'PhoenixRod') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'FlakCannon';
|
|
else e.Replacement = 'UTRocketLauncher';
|
|
}
|
|
else if ( (e.Replacee == 'PlasmaRifle') || (e.Replacee == 'SkullRod') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'Minigun';
|
|
else e.Replacement = 'SniperRifle';
|
|
}
|
|
else if ( e.Replacee == 'BFG9000' ) e.Replacement = 'WarheadLauncher';
|
|
else if ( e.Replacee == 'Mace' )
|
|
{
|
|
if ( (!deathmatch || flak_dmsshock) && !Random[Replacements](0,6) ) e.Replacement = 'EnhancedShockRifle';
|
|
else e.Replacement = 'WarheadLauncher';
|
|
}
|
|
else if ( (e.Replacee == 'Clip') || (e.Replacee == 'GoldWandAmmo') || (e.Replacee == 'GoldWandHefty') ) e.Replacement = 'EClip';
|
|
else if ( (e.Replacee == 'ClipBox') )
|
|
{
|
|
if ( !Random[Replacements](0,2) ) e.Replacement = 'EClip';
|
|
else if ( Random[Replacements](0,2) ) e.Replacement = 'PulseAmmo';
|
|
else e.Replacement = 'RipperAmmo';
|
|
}
|
|
else if ( (e.Replacee == 'BlasterAmmo') || (e.Replacee == 'BlasterHefty') )
|
|
{
|
|
if ( Random[Replacements](0,2) ) e.Replacement = 'PulseAmmo';
|
|
else e.Replacement = 'RipperAmmo';
|
|
}
|
|
else if ( (e.Replacee == 'Shell') || (e.Replacee == 'CrossbowAmmo') )
|
|
{
|
|
if ( !Random[Replacements](0,2) ) e.Replacement = 'EClip';
|
|
else if ( Random[Replacements](0,1) ) e.Replacement = 'BioAmmo2';
|
|
else e.Replacement = 'ShockAmmo2';
|
|
}
|
|
else if ( (e.Replacee == 'ShellBox') || (e.Replacee == 'CrossbowHefty') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'BioAmmo';
|
|
else e.Replacement = 'ShockAmmo';
|
|
}
|
|
else if ( (e.Replacee == 'RocketAmmo') || (e.Replacee == 'PhoenixRodAmmo') || (e.Replacee == 'MaceAmmo') )
|
|
{
|
|
if ( Random[Replacements](0,1) )
|
|
{
|
|
if ( !Random[Replacements](0,3) ) e.Replacement = 'FlakAmmo';
|
|
else e.Replacement = 'FlakAmmo2';
|
|
}
|
|
else
|
|
{
|
|
if ( !Random[Replacements](0,3) ) e.Replacement = 'UTRocketAmmo';
|
|
else e.Replacement = 'UTRocketAmmo2';
|
|
}
|
|
}
|
|
else if ( (e.Replacee == 'RocketBox') || (e.Replacee == 'PhoenixRodHefty') || (e.Replacee == 'MaceHefty') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'FlakAmmo';
|
|
else e.Replacement = 'UTRocketAmmo';
|
|
}
|
|
else if ( (e.Replacee == 'Cell') || (e.Replacee == 'SkullRodAmmo') )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'EClip';
|
|
else if ( !Random[Replacements](0,3) ) e.Replacement = 'RifleAmmo';
|
|
else e.Replacement = 'RifleAmmo2';
|
|
}
|
|
else if ( (e.Replacee == 'CellPack') || (e.Replacee == 'SkullRodHefty') )
|
|
{
|
|
if ( !Random[Replacements](0,6) ) e.Replacement = 'WarheadAmmo';
|
|
else if ( Random[Replacements](0,1) ) e.Replacement = 'MiniAmmo';
|
|
else e.Replacement = 'RifleAmmo';
|
|
}
|
|
else if ( e.Replacee == 'InvulnerabilitySphere' ) e.Replacement = (!deathmatch||flak_dmsshock)?'EnhancedShockRifle':'UDamage';
|
|
else if ( e.Replacee == 'Berserk' ) e.Replacement = 'UDamage';
|
|
else if ( (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiEgg') ) e.Replacement = 'ActUDamage';
|
|
else if ( e.Replacee == 'Soulsphere' ) e.Replacement = 'UTHealthPack';
|
|
else if ( e.Replacee == 'ArtiSuperHealth' ) e.Replacement = 'ActHealthPack';
|
|
else if ( e.Replacee == 'Megasphere' ) e.Replacement = 'UTShieldBelt';
|
|
else if ( e.Replacee == 'ArtiInvulnerability' ) e.Replacement = 'ActShieldBelt';
|
|
else if ( (e.Replacee == 'Allmap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'UTMapRevealer';
|
|
else if ( e.Replacee == 'BlurSphere' ) e.Replacement = 'UTInvisibility';
|
|
else if ( e.Replacee == 'ArtiInvisibility' ) e.Replacement = 'ActInvisibility';
|
|
else if ( e.Replacee == 'Infrared' ) e.Replacement = 'Searchlight';
|
|
else if ( e.Replacee == 'ArtiTorch' ) e.Replacement = 'ActSearchlight';
|
|
else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UTJumpBoots';
|
|
else if ( e.Replacee == 'ArtiFly' ) e.Replacement = 'ActJumpBoots';
|
|
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') ) e.Replacement = 'UTBackpack';
|
|
else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') ) e.Replacement = 'UTArmorBonus';
|
|
else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'UTHealthBonus';
|
|
else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'Silvershield') ) e.Replacement = 'UTThighPads';
|
|
else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'UTBodyArmor';
|
|
else if ( e.Replacee == 'Stimpack' ) e.Replacement = 'UTMedBox';
|
|
else if ( e.Replacee == 'Medikit' ) e.Replacement = 'UTHealthBox';
|
|
else if ( e.Replacee == 'ArtiHealth' )
|
|
{
|
|
if ( !Random[Replacements](0,3) ) e.Replacement = 'ActHealthBox';
|
|
else e.Replacement = 'ActMedBox';
|
|
}
|
|
else if ( e.Replacee == 'ArtiTeleport' )
|
|
{
|
|
// I have no idea what to replace this with, so just have some random stuff
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'UTBackpack';
|
|
else if ( Random[Replacements](0,1) ) e.Replacement = 'ActShieldBelt';
|
|
else if ( Random[Replacements](0,1) ) e.Replacement = 'ActHealthPack';
|
|
else e.Replacement = 'ActUDamage';
|
|
}
|
|
else if ( e.Replacee == 'RedCard' ) e.Replacement = 'UTRedKey';
|
|
else if ( e.Replacee == 'BlueCard' ) e.Replacement = 'UTBlueKey';
|
|
else if ( e.Replacee == 'YellowCard' ) e.Replacement = 'UTGoldKey';
|
|
else if ( e.Replacee == 'RedSkull' ) e.Replacement = 'UTRedSkull';
|
|
else if ( e.Replacee == 'BlueSkull' ) e.Replacement = 'UTBlueSkull';
|
|
else if ( e.Replacee == 'YellowSkull' ) e.Replacement = 'UTGoldSkull';
|
|
else if ( e.Replacee == 'TeleportFog' ) e.Replacement = 'UTTeleportFog';
|
|
else if ( e.Replacee == 'ItemFog' ) e.Replacement = 'UTItemFog';
|
|
else if ( flak_blood && (e.Replacee == 'Blood') ) e.Replacement = 'UTBlood';
|
|
else if ( e.Replacee == 'KeyYellow' ) e.Replacement = 'UTHereticYellowKey';
|
|
else if ( e.Replacee == 'KeyGreen' ) e.Replacement = 'UTHereticGreenKey';
|
|
else if ( e.Replacee == 'KeyBlue' ) e.Replacement = 'UTHereticBlueKey';
|
|
}
|
|
|
|
private Actor AddLight( Vector3 pos, Color col, int radius )
|
|
{
|
|
Actor l = Actor.Spawn("PointLightAttenuated",pos);
|
|
if ( !l ) return null;
|
|
l.args[0] = col.r;
|
|
l.args[1] = col.g;
|
|
l.args[2] = col.b;
|
|
l.args[3] = radius;
|
|
return l;
|
|
}
|
|
|
|
private Actor AddAmbient( Vector3 pos, String snd, double volume = 1., double attenuation = ATTN_NORM )
|
|
{
|
|
Actor a = Actor.Spawn("MapSpot",pos);
|
|
if ( !a ) return null;
|
|
a.A_PlaySound(snd,CHAN_BODY,volume,true,attenuation);
|
|
return a;
|
|
}
|
|
|
|
override void WorldLoaded( WorldEvent e )
|
|
{
|
|
// just replace the -noflat- with a better scaled version and change the sky
|
|
if ( !flak_doomtest )
|
|
{
|
|
if ( (level.GetChecksum() ~== "959A613006CC3AA912C4A22908B7566A") || (level.GetChecksum() ~== "0EADB2F82732A968B8513E4DC6138439") )
|
|
{
|
|
Level.ReplaceTextures("-noflat-","DefTex",0);
|
|
TextureID skytx = TexMan.CheckForTexture("BlueSky",TexMan.Type_Any);
|
|
level.ChangeSky(skytx,skytx);
|
|
}
|
|
return;
|
|
}
|
|
// prettify Kinsie's test map for a more Unreal feel
|
|
if ( level.GetChecksum() ~== "959A613006CC3AA912C4A22908B7566A" )
|
|
{
|
|
S_ChangeMusic("Course");
|
|
TextureID deftex = TexMan.CheckForTexture("-noflat-",TexMan.Type_Any);
|
|
TextureID skytx = TexMan.CheckForTexture("KGDaySky",TexMan.Type_Any);
|
|
TextureID baseflor = TexMan.CheckForTexture("rClfFlr0",TexMan.Type_Any);
|
|
TextureID baseceil = TexMan.CheckForTexture("rClfBas0",TexMan.Type_Any);
|
|
TextureID basewall = TexMan.CheckForTexture("uAlnWl2b",TexMan.Type_Any);
|
|
TextureID xbasewall = TexMan.CheckForTexture("xAlnWl2b",TexMan.Type_Any);
|
|
TextureID glasstex = TexMan.CheckForTexture("Glassg",TexMan.Type_Any);
|
|
level.ChangeSky(skytx,skytx);
|
|
for ( int i=0; i<level.sectors.size(); i++ )
|
|
{
|
|
level.sectors[i].lightlevel = min(level.sectors[i].lightlevel,96);
|
|
if ( level.sectors[i].GetPlaneLight(0) ) level.sectors[i].SetPlaneLight(0,96);
|
|
if ( level.sectors[i].GetPlaneLight(1) ) level.sectors[i].SetPlaneLight(1,96);
|
|
// open some ceilings
|
|
if ( level.sectors[i].ceilingplane.ZAtPoint(level.sectors[i].centerspot) == 1280 )
|
|
level.sectors[i].SetTexture(1,skyflatnum);
|
|
if ( level.sectors[i].GetTexture(0) == deftex )
|
|
{
|
|
level.sectors[i].SetTexture(0,((i==47)||((i>=256)&&(i<=260)))?baseceil:baseflor);
|
|
level.sectors[i].SetXScale(0,2.);
|
|
level.sectors[i].SetYScale(0,2.);
|
|
}
|
|
if ( level.sectors[i].GetTexture(1) == deftex )
|
|
{
|
|
level.sectors[i].SetTexture(1,((i==47)||((i>=256)&&(i<=260)))?baseflor:baseceil);
|
|
level.sectors[i].SetXScale(1,2.);
|
|
level.sectors[i].SetYScale(1,2.);
|
|
}
|
|
}
|
|
for ( int i=0; i<level.sides.size(); i++ )
|
|
{
|
|
level.sides[i].light = 0;
|
|
level.sides[i].flags &= ~Side.WALLF_ABSLIGHTING;
|
|
for ( int j=0; j<3; j++ )
|
|
{
|
|
if ( level.sides[i].GetTexture(j) != deftex ) continue;
|
|
if ( (i==529) || (i==530) || (i==533) || (i==534) )
|
|
{
|
|
level.sides[i].SetTexture(j,xbasewall);
|
|
level.sides[i].SetTextureYOffset(j,-2304);
|
|
}
|
|
else level.sides[i].SetTexture(j,basewall);
|
|
level.sides[i].SetTextureXScale(j,2.);
|
|
level.sides[i].SetTextureYScale(j,2.);
|
|
}
|
|
}
|
|
// fixup
|
|
for ( int i=215; i<246; i++ )
|
|
{
|
|
if ( (i==218) || (i==221) || (i==227)
|
|
|| (i==230) || (i==232) || (i==234)
|
|
|| (i==238) || (i==243) ) continue;
|
|
level.sectors[i].SetSpecialColor(0,"00 00 00");
|
|
}
|
|
level.sectors[53].SetFade("00 00 20");
|
|
level.sides[1844].SetTexture(1,glasstex);
|
|
level.lines[945].alpha = 0.5;
|
|
AddLight((0,-288,128),"E0 E0 FF",256);
|
|
AddLight((-2560,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((0,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((2560,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((-384,-160,64),"FF FF FF",128);
|
|
AddLight((-384,-288,64),"FF FF FF",128);
|
|
AddLight((-384,-416,64),"FF FF FF",128);
|
|
AddLight((0,2816,96),"FF FF FF",512);
|
|
AddLight((2904,1344,128),"80 80 FF",256);
|
|
AddLight((3408,1344,128),"80 80 FF",256);
|
|
AddLight((1568,1760,64),"20 20 80",128);
|
|
AddLight((1824,1760,64),"80 20 20",128);
|
|
AddLight((2080,1760,64),"FF 80 20",128);
|
|
AddLight((2336,1760,64),"20 FF 20",128);
|
|
AddLight((2592,1760,64),"80 80 20",128);
|
|
AddLight((2848,1760,64),"A0 A0 30",128);
|
|
AddLight((2944,960,64),"20 FF 20",128);
|
|
AddLight((2944,736,64),"20 FF 20",128);
|
|
AddLight((3264,960,64),"FF 20 20",128);
|
|
AddLight((3264,736,64),"A0 A0 30",128);
|
|
AddLight((3264,512,64),"80 80 20",128);
|
|
AddLight((3584,960,64),"80 80 20",128);
|
|
AddLight((3584,736,64),"20 FF 20",128);
|
|
AddLight((3584,512,64),"FF 80 20",128);
|
|
AddLight((3584,288,64),"FF 80 FF",128);
|
|
AddAmbient((0,-288,192),"testamb/wind1",0.5,1.6);
|
|
AddAmbient((-2560,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((0,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((2560,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((768,1600,1280),"testamb/wind2",0.8,1.6);
|
|
AddAmbient((3174,1344,128),"testamb/water",0.4,1.6);
|
|
AddAmbient((1568,1760,64),"testamb/water",0.4,2.4);
|
|
AddAmbient((2080,1760,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((2336,1760,64),"testamb/slime",0.4,2.4);
|
|
AddAmbient((2944,960,64),"testamb/slime",0.4,2.4);
|
|
AddAmbient((2944,736,64),"testamb/slime",0.4,2.4);
|
|
AddAmbient((3584,736,64),"testamb/slime",0.4,2.4);
|
|
AddAmbient((3584,512,64),"testamb/lava",0.8,2.4);
|
|
}
|
|
else if ( level.GetChecksum() ~== "0EADB2F82732A968B8513E4DC6138439" )
|
|
{
|
|
S_ChangeMusic("Course");
|
|
TextureID deftex = TexMan.CheckForTexture("-noflat-",TexMan.Type_Any);
|
|
TextureID skytx = TexMan.CheckForTexture("KGDaySky",TexMan.Type_Any);
|
|
TextureID baseflor = TexMan.CheckForTexture("rClfFlr0",TexMan.Type_Any);
|
|
TextureID baseceil = TexMan.CheckForTexture("rClfBas0",TexMan.Type_Any);
|
|
TextureID basewall = TexMan.CheckForTexture("uAlnWl2b",TexMan.Type_Any);
|
|
TextureID xbasewall = TexMan.CheckForTexture("xAlnWl2b",TexMan.Type_Any);
|
|
TextureID glasstex = TexMan.CheckForTexture("Glassg",TexMan.Type_Any);
|
|
level.ChangeSky(skytx,skytx);
|
|
for ( int i=0; i<level.sectors.size(); i++ )
|
|
{
|
|
level.sectors[i].lightlevel = min(level.sectors[i].lightlevel,96);
|
|
if ( level.sectors[i].GetPlaneLight(0) ) level.sectors[i].SetPlaneLight(0,96);
|
|
if ( level.sectors[i].GetPlaneLight(1) ) level.sectors[i].SetPlaneLight(1,96);
|
|
// open some ceilings
|
|
if ( level.sectors[i].ceilingplane.ZAtPoint(level.sectors[i].centerspot) == 1280 )
|
|
level.sectors[i].SetTexture(1,skyflatnum);
|
|
if ( level.sectors[i].GetTexture(0) == deftex )
|
|
{
|
|
level.sectors[i].SetTexture(0,((i==47)||((i>=144)&&(i<=148)))?baseceil:baseflor);
|
|
level.sectors[i].SetXScale(0,2.);
|
|
level.sectors[i].SetYScale(0,2.);
|
|
}
|
|
if ( level.sectors[i].GetTexture(1) == deftex )
|
|
{
|
|
level.sectors[i].SetTexture(1,((i==47)||((i>=144)&&(i<=148)))?baseflor:baseceil);
|
|
level.sectors[i].SetXScale(1,2.);
|
|
level.sectors[i].SetYScale(1,2.);
|
|
}
|
|
}
|
|
for ( int i=0; i<level.sides.size(); i++ )
|
|
{
|
|
level.sides[i].light = 0;
|
|
level.sides[i].flags &= ~Side.WALLF_ABSLIGHTING;
|
|
for ( int j=0; j<3; j++ )
|
|
{
|
|
if ( level.sides[i].GetTexture(j) != deftex ) continue;
|
|
if ( (i==295) || (i==296) || (i==309) || (i==310) )
|
|
{
|
|
level.sides[i].SetTexture(j,xbasewall);
|
|
level.sides[i].SetTextureYOffset(j,-2304);
|
|
}
|
|
else level.sides[i].SetTexture(j,basewall);
|
|
level.sides[i].SetTextureXScale(j,2.);
|
|
level.sides[i].SetTextureYScale(j,2.);
|
|
}
|
|
}
|
|
// fixup
|
|
for ( int i=104; i<134; i++ )
|
|
{
|
|
if ( (i==107) || (i==110) || (i==116)
|
|
|| (i==119) || (i==121) || (i==127)
|
|
|| (i==132) ) continue;
|
|
level.sectors[i].SetSpecialColor(0,"00 00 00");
|
|
}
|
|
level.sectors[53].SetFade("00 00 20");
|
|
level.sides[216].SetTexture(1,glasstex);
|
|
level.lines[125].alpha = 0.5;
|
|
AddLight((0,-288,128),"E0 E0 FF",256);
|
|
AddLight((-2560,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((0,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((2560,1024,1280),"E0 E0 FF",1024);
|
|
AddLight((-384,-160,64),"FF FF FF",128);
|
|
AddLight((-384,-288,64),"FF FF FF",128);
|
|
AddLight((-384,-416,64),"FF FF FF",128);
|
|
AddLight((0,2816,96),"FF FF FF",512);
|
|
AddLight((2904,1344,128),"80 80 FF",256);
|
|
AddLight((3408,1344,128),"80 80 FF",256);
|
|
AddLight((1568,1760,64),"20 20 80",128);
|
|
AddLight((1824,1760,64),"A0 A0 30",128);
|
|
AddLight((2080,1760,64),"80 80 20",128);
|
|
AddLight((2336,1760,64),"20 20 80",128);
|
|
AddLight((2592,1760,64),"FF 80 20",128);
|
|
AddLight((2944,960,64),"A0 A0 30",128);
|
|
AddLight((2944,736,64),"A0 A0 30",128);
|
|
AddLight((3264,960,64),"A0 A0 30",128);
|
|
AddLight((3264,736,64),"A0 A0 30",128);
|
|
AddLight((3264,512,64),"A0 A0 30",128);
|
|
AddLight((3264,288,64),"FF 80 20",128);
|
|
AddLight((3584,960,64),"A0 A0 30",128);
|
|
AddLight((3584,736,64),"80 80 20",128);
|
|
AddLight((3584,512,64),"FF 80 20",128);
|
|
AddLight((3584,288,64),"A0 A0 30",128);
|
|
AddAmbient((0,-288,192),"testamb/wind1",0.5,1.6);
|
|
AddAmbient((-2560,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((0,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((2560,1024,768),"testamb/wind1",0.4,0.8);
|
|
AddAmbient((768,1600,1280),"testamb/wind2",0.8,1.6);
|
|
AddAmbient((3174,1344,128),"testamb/water",0.4,1.6);
|
|
AddAmbient((1568,1760,64),"testamb/water",0.4,2.4);
|
|
AddAmbient((2336,1760,64),"testamb/water",0.4,2.4);
|
|
AddAmbient((2592,1760,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((3584,512,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((3264,288,64),"testamb/lava",0.8,2.4);
|
|
}
|
|
}
|
|
|
|
override void WorldThingSpawned( WorldEvent e )
|
|
{
|
|
if ( flak_nobosstelefrag && e.Thing.bBOSS ) e.Thing.bNOTELEFRAG = true;
|
|
if ( deathmatch && flak_instagib )
|
|
{
|
|
if ( (e.Thing is 'EnhancedShockRifle') || (e.Thing is 'EnhancedShockAmmo') ) return;
|
|
if ( e.Thing is 'Inventory' ) e.Thing.Destroy();
|
|
}
|
|
}
|
|
|
|
override void PlayerEntered( PlayerEvent e )
|
|
{
|
|
if ( deathmatch && flak_instagib )
|
|
{
|
|
players[e.playernumber].mo.GiveInventory("EnhancedShockRifle",1);
|
|
return;
|
|
}
|
|
if ( flak_translocator )
|
|
players[e.playernumber].mo.GiveInventory("Translocator",1);
|
|
}
|
|
override void PlayerRespawned( PlayerEvent e )
|
|
{
|
|
if ( deathmatch && flak_instagib )
|
|
{
|
|
players[e.playernumber].mo.GiveInventory("EnhancedShockRifle",1);
|
|
return;
|
|
}
|
|
if ( flak_translocator )
|
|
players[e.playernumber].mo.GiveInventory("Translocator",1);
|
|
}
|
|
|
|
override void NetworkProcess( ConsoleEvent e )
|
|
{
|
|
if ( e.Name ~== "refreshtrans" )
|
|
{
|
|
if ( flak_translocator )
|
|
{
|
|
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].mo.GiveInventory("Translocator",1);
|
|
}
|
|
else
|
|
{
|
|
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].mo.TakeInventory("Translocator",1);
|
|
}
|
|
}
|
|
else if ( e.Name ~== "uttaunt" )
|
|
{
|
|
if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return;
|
|
let mo = players[e.player].mo;
|
|
if ( (mo.Health <= 0) || !(mo is 'UTPlayer') ) return;
|
|
switch ( e.Args[0] )
|
|
{
|
|
case 2:
|
|
if ( mo.FindState("Taunt2") ) mo.SetStateLabel("Taunt2");
|
|
break;
|
|
case 3:
|
|
if ( mo.FindState("Taunt3") ) mo.SetStateLabel("Taunt3");
|
|
break;
|
|
case 4:
|
|
if ( mo.FindState("Taunt4") ) mo.SetStateLabel("Taunt4");
|
|
break;
|
|
default:
|
|
if ( mo.FindState("Taunt1") ) mo.SetStateLabel("Taunt1");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldTick()
|
|
{
|
|
for ( int i=0; i<flashes.size(); i++ )
|
|
{
|
|
if ( flashes[i].tic >= gametic ) continue;
|
|
flashes.Delete(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
override void PostUiTick()
|
|
{
|
|
for ( int i=0; i<flashes.size(); i++ )
|
|
{
|
|
if ( flashes[i].tic < gametic ) continue;
|
|
GenericFlash gf = new("GenericFlash").Setup(flashes[i].cam,flashes[i].c,flashes[i].duration);
|
|
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
|
|
}
|
|
}
|
|
|
|
override void WorldThingDamaged( WorldEvent e )
|
|
{
|
|
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
|
|
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill ) return;
|
|
if ( (e.Inflictor && e.Inflictor.player && (e.Inflictor != e.Thing)) || (e.DamageSource && e.DamageSource.player && (e.DamageSource != e.Thing)) )
|
|
{
|
|
Weapon saw = Weapon(e.Inflictor.FindInventory("UTChainsaw"));
|
|
if ( !saw ) saw = Weapon(e.DamageSource.FindInventory("UTChainsaw"));
|
|
bool current = false;
|
|
if ( e.Inflictor.player && e.Inflictor.player.ReadyWeapon == saw ) current = true;
|
|
if ( e.DamageSource.player && (e.DamageSource.player.ReadyWeapon == saw) ) current = true;
|
|
if ( flak_sawammo && saw && (saw.Ammo1.Amount < 40) && !current && !Random[SawDrop](0,9) )
|
|
{
|
|
let a = Actor.Spawn("ChainsawAmmo",e.Thing.Vec3Offset(0,0,e.Thing.Height*.5));
|
|
a.TossItem();
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldThingDied( WorldEvent e )
|
|
{
|
|
if ( e.Thing.bDONTGIB ) return;
|
|
// gibbers
|
|
if ( flak_gibs && !e.Thing.bNOBLOOD && (e.Thing.FindState("XDeath",true) || e.Thing.FindState("Death.Extreme",true)) && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.Thing.Health < e.Thing.GetGibHealth())) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) )
|
|
{
|
|
// players use their own gibber
|
|
if ( e.Thing is 'UTPlayer' ) return;
|
|
// generic gibbing
|
|
let a = Actor.Spawn("UTGibber",e.Thing.pos);
|
|
a.vel = e.Thing.vel;
|
|
a.Scale = e.Thing.Scale;
|
|
a.Translation = e.Thing.BloodTranslation;
|
|
if ( e.Thing.BloodColor ) a.SetShade(e.Thing.BloodColor);
|
|
else a.SetShade(gameinfo.defaultbloodcolor);
|
|
a.A_SetSize(e.Thing.radius,e.Thing.height);
|
|
e.Thing.A_XScream();
|
|
e.Thing.A_NoBlocking();
|
|
e.Thing.A_BossDeath();
|
|
e.Thing.SetStateLabel("Null"); // instead of destroying, should prevent random VM aborts
|
|
return;
|
|
}
|
|
// attach damage accumulator for corpses
|
|
if ( !(e.Thing is 'UTPlayer') && !flak_corpsedamage ) return;
|
|
let a = Actor.Spawn("ShredCorpseHitbox",e.Thing.pos);
|
|
a.target = e.Thing;
|
|
}
|
|
|
|
static void DoFlash( Actor camera, Color c, int duration )
|
|
{
|
|
QueuedFlash qf = new("QueuedFlash");
|
|
qf.duration = duration;
|
|
qf.c = c;
|
|
qf.tic = gametic;
|
|
qf.cam = camera;
|
|
let hnd = UTMainHandler(EventHandler.Find("UTMainHandler"));
|
|
if ( !hnd ) return; // not supposed to happen
|
|
hnd.flashes.push(qf);
|
|
}
|
|
|
|
// Doom's explosions aren't fully 3D
|
|
static void DoBlast( Actor Source, double ExplosionRadius, double MomentumTransfer )
|
|
{
|
|
BlockThingsIterator bi = BlockThingsIterator.Create(Source,ExplosionRadius);
|
|
while ( bi.Next() )
|
|
{
|
|
Actor a = bi.Thing;
|
|
if ( !a || !a.bSHOOTABLE || !Source.CheckSight(a,0xf) || (a == Source) || (Source.Distance3D(a) > ExplosionRadius) )
|
|
continue;
|
|
Vector3 midpoint = a.Vec3Offset(0,0,a.height*0.5);
|
|
Vector3 dir = Level.Vec3Diff(Source.pos,midpoint);
|
|
double dist = max(1,dir.length());
|
|
double damagescale = 1-max(0,(dist-a.radius)/ExplosionRadius);
|
|
dir = dir/dist;
|
|
if ( (a is 'ShredCorpseHitbox') && a.target )
|
|
a.target.vel += dir*damagescale*(MomentumTransfer/(Thinker.TICRATE*a.target.mass));
|
|
else a.vel += dir*damagescale*(MomentumTransfer/(Thinker.TICRATE*a.mass));
|
|
}
|
|
}
|
|
|
|
// Same for this
|
|
static void DoKnockback( Actor Victim, Vector3 HitDirection, double MomentumTransfer )
|
|
{
|
|
if ( !Victim ) return;
|
|
if ( (Victim is 'ShredCorpseHitbox') && Victim.target )
|
|
Victim.target.vel += HitDirection*(MomentumTransfer/(Thinker.TICRATE*Victim.target.Mass));
|
|
else Victim.vel += HitDirection*(MomentumTransfer/(Thinker.TICRATE*Victim.Mass));
|
|
}
|
|
|
|
static void DoSwing( Actor target, Vector2 dir, double initial, double inc, int steps, int mode = 0, int delay = 0, double rmul = 1.0 )
|
|
{
|
|
if ( !flak_swingers ) return;
|
|
let s = new("Swinger");
|
|
s.ChangeStatNum(Thinker.STAT_USER);
|
|
s.target = target;
|
|
s.dir = dir;
|
|
s.inc = inc;
|
|
s.rmul = rmul;
|
|
s.steps = steps;
|
|
s.mode = mode;
|
|
s.delay = delay;
|
|
s.cnt = 0;
|
|
s.cstate = 0;
|
|
s.str = initial;
|
|
s.tstr = initial;
|
|
}
|
|
}
|