Everything in here is highly unstable and may not work. Current commit contains various new features for the HUD, some cleanup, and additional changes for compatibility with Doomreal as it is developed. The diff is kinda fucky on the font restructure due to flaky rename detection.
2775 lines
76 KiB
Text
2775 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;
|
|
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"));
|
|
if ( !belt.CallTryPickup(self) ) belt.Destroy();
|
|
let bonus = Inventory(Spawn("UTArmorBonus"));
|
|
bonus.Amount = bonus.MaxAmount;
|
|
if ( !bonus.CallTryPickup(self) ) bonus.Destroy();
|
|
level.total_items -= 2; // spawning them in raises item count
|
|
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("See"))
|
|
|| 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("See"))
|
|
|| 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("See"))
|
|
|| 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("See"))
|
|
|| 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("See");
|
|
}
|
|
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("See"))
|
|
|| 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("See"))
|
|
|| 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:
|
|
#### # 5;
|
|
PLAY HIJKL 4;
|
|
Goto See+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.Destroy();
|
|
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;
|
|
}
|
|
}
|