3705 lines
99 KiB
Text
3705 lines
99 KiB
Text
// constants for extra sound stuff, all starting at 0x4360 (436 was the last official UT version)
|
|
const CHAN_ANNOUNCER = 0x4360; // announcer voices
|
|
const CHAN_FOOTSTEP = 0x4361; // footsteps and splashes (should have OVERLAP flag)
|
|
const CHAN_LEFTWEAPON = 0x4362; // for dual wielded weapons
|
|
const CHAN_WEAPONMISC = 0x4363; // extra sounds (e.g.: idle loops)
|
|
const CHAN_LEFTWEAPONMISC = 0x4364; // ... for dual wielded weapons
|
|
const CHAN_POWERUP = 0x4365; // powerup/item use sounds
|
|
const CHAN_POWERUP2 = 0x4366; // auxiliary powerup sounds
|
|
const CHAN_POWERUP3 = 0x4367; // even more powerup sounds (used by jump boots, mainly)
|
|
const CHAN_POWERUP4 = 0x4368; // even more (used by shield belt / invuln hit sounds)
|
|
|
|
Class UTPlayer : DoomPlayer
|
|
{
|
|
bool doprintnoammo;
|
|
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;
|
|
Actor underwatersnd;
|
|
|
|
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
|
|
// in non-vanilla mode, also gives body/thigh armor
|
|
Class<Inventory> which[] =
|
|
{
|
|
"UTShieldBelt", "UTArmorBonus",
|
|
"UTBodyArmor", "UTThighpads"
|
|
};
|
|
Inventory inv;
|
|
int mx = flak_vanillaarmor?2:4;
|
|
for ( int i=0; i<mx; i++ )
|
|
{
|
|
inv = Inventory(Spawn(which[i]));
|
|
inv.ClearCounters();
|
|
inv.Amount = inv.MaxAmount;
|
|
if ( !inv.CallTryPickup(self) ) inv.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")))
|
|
|| ((gameinfo.gametype&GAME_HERETIC) && (type is 'ActUTFullAmmoBox')) )
|
|
{
|
|
// don't give activatables outside of Heretic/Hexen
|
|
// don't give full ammo cubes outside of Hexen
|
|
continue;
|
|
}
|
|
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);
|
|
}
|
|
|
|
// modified so it displays the "has no ammo" message
|
|
override Weapon PickWeapon( int slot, bool checkammo )
|
|
{
|
|
doprintnoammo = true;
|
|
let rslt = Super.PickWeapon(slot,checkammo);
|
|
doprintnoammo = false;
|
|
return rslt;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !flak_utmovement || !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) )
|
|
{
|
|
bNOFRICTION = false;
|
|
bNOFRICTIONBOUNCE = false;
|
|
}
|
|
else
|
|
{
|
|
bNOFRICTION = true;
|
|
bNOFRICTIONBOUNCE = true;
|
|
}
|
|
if ( InStateSequence(CurState,FindState("See",true)) )
|
|
SetStateLabel("See2");
|
|
if ( (waterlevel >= 2) && (lastwaterlevel < 2) )
|
|
PlaySplash(1.);
|
|
else if ( (waterlevel < 2) && (lastwaterlevel >= 2) )
|
|
PlaySurface();
|
|
if ( (waterlevel >= 3) && !underwatersnd )
|
|
{
|
|
underwatersnd = Spawn("UTUnderSound",pos);
|
|
underwatersnd.target = self;
|
|
}
|
|
else if ( (waterlevel < 3) && underwatersnd )
|
|
underwatersnd.Destroy();
|
|
lastwaterlevel = waterlevel;
|
|
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 ) PlaySplash(vol);
|
|
else A_StartSound("*uland",CHAN_FOOTSTEP,CHANF_OVERLAP,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 ) PlayWetFootstep(vol);
|
|
else PlayFootstep(vol);
|
|
}
|
|
lastground = player.onground;
|
|
lastvelz = prevvelz;
|
|
prevvelz = vel.z;
|
|
}
|
|
|
|
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) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) )
|
|
{
|
|
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
|
|
Vector3 floornormal = floorsector.floorplane.normal;
|
|
for ( int i=0; i<floorsector.Get3DFloorCount(); i++ )
|
|
{
|
|
F3DFloor ff = floorsector.Get3DFloor(i);
|
|
if ( ff.top.ZAtPoint(pos.xy) ~== floorz )
|
|
{
|
|
floornormal = -ff.top.normal;
|
|
break;
|
|
}
|
|
}
|
|
if ( floornormal 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 < 3) )
|
|
{
|
|
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_StartSound("*jump",CHAN_VOICE);
|
|
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 < 1) )
|
|
{
|
|
// 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;
|
|
// in UT you can't crouch unless you're on the ground
|
|
// however for the sake of compatibility with a whole lot of maps, crouch-jumping will be a thing here
|
|
if ( CanCrouch() && (player.health > 0) && level.IsCrouchingAllowed() )
|
|
{
|
|
if ( !totallyfrozen )
|
|
{
|
|
int crouchdir = player.crouching;
|
|
if ( !crouchdir ) crouchdir = (player.cmd.buttons&BT_CROUCH)?-1:1;
|
|
else if ( player.cmd.buttons&BT_CROUCH ) player.crouching = 0;
|
|
if ( (crouchdir == 1) && (player.crouchfactor < 1) && (pos.Z+height < ceilingz) )
|
|
CrouchMove(1);
|
|
else if ( (crouchdir == -1) && (player.crouchfactor > 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_StartSound("*jump",CHAN_VOICE);
|
|
}
|
|
last_jump_held = gametic;
|
|
}
|
|
if ( !player.onground || player.jumptics )
|
|
last_jump_held = gametic;
|
|
}
|
|
|
|
override void DeathThink()
|
|
{
|
|
Super.DeathThink();
|
|
if ( !flak_utmovement || !player || (player.mo != self) || (player.cheats&(CF_FROZEN|CF_TOTALLYFROZEN)) )
|
|
return;
|
|
// unreal physics while dead
|
|
double friction = FrictionToUnreal();
|
|
if ( !bNoGravity && player.onground && (waterlevel < 3) )
|
|
{
|
|
// 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 < 1) )
|
|
{
|
|
// 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_StartSound("ut/playerfootstep",CHAN_FOOTSTEP,CHANF_OVERLAP,vol);
|
|
}
|
|
|
|
virtual void PlaySplash( double vol )
|
|
{
|
|
TerrainDef t = GetFloorTerrain();
|
|
String snd = "ut/wetsplash";
|
|
switch ( t.TerrainName )
|
|
{
|
|
case 'UTLava':
|
|
case 'Lava':
|
|
snd = "ut/lavasplash";
|
|
break;
|
|
case 'UTSlime':
|
|
case 'UTNukage':
|
|
case 'Slime':
|
|
case 'Nukage':
|
|
case 'Sludge':
|
|
snd = "ut/slimesplash";
|
|
break;
|
|
}
|
|
A_StartSound(snd,CHAN_FOOTSTEP,CHANF_OVERLAP,vol);
|
|
}
|
|
|
|
virtual void PlaySurface()
|
|
{
|
|
TerrainDef t = GetFloorTerrain();
|
|
String snd = "ut/wetsurface";
|
|
switch ( t.TerrainName )
|
|
{
|
|
case 'UTLava':
|
|
case 'Lava':
|
|
snd = "ut/lavasurface";
|
|
break;
|
|
case 'UTSlime':
|
|
case 'UTNukage':
|
|
case 'Slime':
|
|
case 'Nukage':
|
|
case 'Sludge':
|
|
snd = "ut/slimesurface";
|
|
break;
|
|
}
|
|
A_StartSound(snd,CHAN_FOOTSTEP,CHANF_OVERLAP);
|
|
}
|
|
|
|
virtual void PlayWetFootstep( double vol )
|
|
{
|
|
TerrainDef t = GetFloorTerrain();
|
|
String snd = "ut/playerfootstepwet";
|
|
switch ( t.TerrainName )
|
|
{
|
|
case 'UTLava':
|
|
case 'Lava':
|
|
snd = "ut/playerfootsteplava";
|
|
break;
|
|
case 'UTSlime':
|
|
case 'UTNukage':
|
|
case 'Slime':
|
|
case 'Nukage':
|
|
case 'Sludge':
|
|
snd = "ut/playerfootstepslime";
|
|
break;
|
|
}
|
|
A_StartSound(snd,CHAN_FOOTSTEP,CHANF_OVERLAP,vol);
|
|
}
|
|
|
|
override void PlayIdle()
|
|
{
|
|
if ( player.Health <= 0 ) return;
|
|
if ( !bNoGravity && player.onground && (waterlevel < 3) )
|
|
{
|
|
// 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 ( player.crouchdir == -1 )
|
|
{
|
|
// Crouching
|
|
if ( !InStateSequence(CurState,FindState("SpawnCrouch")) )
|
|
SetStateLabel("SpawnCrouch");
|
|
}
|
|
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 < 1) )
|
|
{
|
|
// 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.Health <= 0 ) return;
|
|
if ( !bNoGravity && player.onground && (waterlevel < 3) )
|
|
{
|
|
// Ground
|
|
if ( player.crouchdir == -1 )
|
|
{
|
|
// Crouching
|
|
if ( !InStateSequence(CurState,FindState("SeeCrouch")) )
|
|
SetStateLabel("SeeCrouch");
|
|
}
|
|
else 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 < 1) && abs(pos.z-floorz) > maxstepheight )
|
|
{
|
|
// 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()
|
|
{
|
|
if ( player.Health <= 0 ) return;
|
|
// 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.Health <= 0 ) return;
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
SetStateLabel("Missile");
|
|
}
|
|
|
|
virtual void PlayReloading()
|
|
{
|
|
if ( player.Health <= 0 ) return;
|
|
if ( (player && (player.mo == self)) && (player.crouchdir == -1) ) return;
|
|
SetStateLabel("Reload");
|
|
}
|
|
|
|
virtual void PlayLanding()
|
|
{
|
|
if ( player.Health <= 0 ) return;
|
|
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();
|
|
}
|
|
|
|
// mostly a copy of A_FreezeDeathChunks but with some DT specific gib
|
|
// behaviour
|
|
void A_FreezeDeathChunksDT()
|
|
{
|
|
if ( (vel != (0,0,0)) && !bShattering )
|
|
{
|
|
tics = 3*TICRATE;
|
|
return;
|
|
}
|
|
vel = (0,0,0);
|
|
A_StartSound("misc/icebreak");
|
|
// [RH] In Hexen, this creates a random number of shards (range [24,56])
|
|
// with no relation to the size of the self shattering. I think it should
|
|
// base the number of shards on the size of the dead thing, so bigger
|
|
// things break up into more shards than smaller things.
|
|
// An actor with radius 20 and height 64 creates ~40 chunks.
|
|
int numChunks = max(4,int(radius*Height)/32);
|
|
int i = Random[FreezeDeathChunks](0,numChunks/4-1);
|
|
for ( i=max(24,numChunks+i); i>=0; i-- )
|
|
{
|
|
double xo = (random[FreezeDeathChunks]()-128)*radius/128;
|
|
double yo = (random[FreezeDeathChunks]()-128)*radius/128;
|
|
double zo = (random[FreezeDeathChunks]()*Height/255);
|
|
Actor mo = Spawn("IceChunk",Vec3Offset(xo,yo,zo),ALLOW_REPLACE);
|
|
if ( !mo ) continue;
|
|
mo.SetState(mo.SpawnState+random[FreezeDeathChunks](0,2));
|
|
mo.Vel.X = random2[FreezeDeathChunks]()/128.;
|
|
mo.Vel.Y = random2[FreezeDeathChunks]()/128.;
|
|
mo.Vel.Z = (mo.pos.Z-pos.Z)/Height*4;
|
|
}
|
|
// Gib the player, the camera will attach to the head at this point
|
|
A_GibMe();
|
|
A_NoBlocking();
|
|
SetStateLabel("IceBurst");
|
|
}
|
|
|
|
void A_PainDT()
|
|
{
|
|
if ( waterlevel > 2 ) A_StartSound("*death-drowning",CHAN_VOICE);
|
|
else A_Pain();
|
|
}
|
|
|
|
void A_PlayerScreamDT()
|
|
{
|
|
if ( waterlevel > 2 ) A_StartSound("*death-drowning",CHAN_VOICE);
|
|
else A_PlayerScream();
|
|
}
|
|
|
|
void A_XScreamDT()
|
|
{
|
|
if ( waterlevel > 2 ) A_StartSound("*death-drowning",CHAN_VOICE);
|
|
else A_XScream();
|
|
}
|
|
|
|
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_PainDT();
|
|
Goto Spawn;
|
|
#### # 2;
|
|
PLAY Y 3 A_PainDT();
|
|
Goto Spawn;
|
|
#### # 2;
|
|
PLAY Z 3 A_PainDT();
|
|
Goto Spawn;
|
|
Pain.Decapitated:
|
|
#### # 2;
|
|
PLAY X 3 A_PainDT();
|
|
Goto Spawn;
|
|
Death.Decapitated:
|
|
#### # 0 A_HeadPop();
|
|
PLD4 A 3 A_PlayerScreamDT();
|
|
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_PlayerScreamDT();
|
|
PD11 B 3 A_NoBlocking();
|
|
PD11 CDEFGHIJKLMNOPQ 3;
|
|
PD11 R 1 A_DMFade();
|
|
Wait;
|
|
Death.Zapped:
|
|
#### # 3;
|
|
PLD9 A 3 A_PlayerScreamDT();
|
|
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_PlayerScreamDT();
|
|
PLD8 B 3 A_NoBlocking();
|
|
PLD8 CDEFGHIJKLMNOPQ 3;
|
|
PLD8 R 1 A_DMFade();
|
|
Wait;
|
|
Death7:
|
|
#### # 3;
|
|
PLD7 A 3 A_PlayerScreamDT();
|
|
PLD7 B 3 A_NoBlocking();
|
|
PLD7 CDEFGHIJKLMNOPQRST 3;
|
|
PLD7 U 1 A_DMFade();
|
|
Wait;
|
|
Death3:
|
|
#### # 3;
|
|
PLD3 A 3 A_PlayerScreamDT();
|
|
PLD3 B 3 A_NoBlocking();
|
|
PLD3 CDEFGHIJKL 3;
|
|
PLD3 M 1 A_DMFade();
|
|
Wait;
|
|
Death2:
|
|
#### # 3;
|
|
PLD2 A 3 A_PlayerScreamDT();
|
|
PLD2 B 3 A_NoBlocking();
|
|
PLD2 CDEFGHIJKLMNO 3;
|
|
PLD2 P 1 A_DMFade();
|
|
Wait;
|
|
Death1:
|
|
#### # 3;
|
|
PLD1 A 3 A_PlayerScreamDT();
|
|
PLD1 B 3 A_NoBlocking();
|
|
PLD1 CDEFGHIJKL 3;
|
|
PLD1 M 1 A_DMFade();
|
|
Wait;
|
|
XDeath:
|
|
TNT1 A 350
|
|
{
|
|
A_XScreamDT();
|
|
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;
|
|
Ice:
|
|
PICE A 5 A_FreezeDeath();
|
|
PICE A 1 A_FreezeDeathChunksDT();
|
|
Wait;
|
|
IceBurst:
|
|
TNT1 A 35;
|
|
TNT1 A 1 A_CheckPlayerDone();
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class UTUnderSound : Actor
|
|
{
|
|
enum EFluidType
|
|
{
|
|
FLUID_WATER,
|
|
FLUID_SLIME,
|
|
FLUID_LAVA,
|
|
FLUID_NITRO
|
|
};
|
|
int curfluid, lastfluid;
|
|
String fluidsounds[4];
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
}
|
|
int GetFluid()
|
|
{
|
|
int thisfluid = FLUID_WATER;
|
|
// try to guess where we are
|
|
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
F3DFloor ff = CurSector.Get3DFloor(i);
|
|
switch ( ff.model.damagetype )
|
|
{
|
|
case 'Slime':
|
|
thisfluid = FLUID_SLIME;
|
|
break;
|
|
case 'Fire':
|
|
thisfluid = FLUID_LAVA;
|
|
break;
|
|
case 'Ice':
|
|
thisfluid = FLUID_NITRO;
|
|
break;
|
|
}
|
|
}
|
|
return thisfluid;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetOrigin(target.pos,true);
|
|
curfluid = lastfluid = GetFluid();
|
|
fluidsounds[0] = 'ut/underwater';
|
|
fluidsounds[1] = 'ut/underslime';
|
|
fluidsounds[2] = 'ut/underlava';
|
|
fluidsounds[3] = 'ut/undernitro';
|
|
A_StartSound(fluidsounds[curfluid],CHAN_VOICE,CHANF_LOOPING|CHANF_LISTENERZ,1.,0.);
|
|
A_SoundVolume(CHAN_VOICE,(players[consoleplayer].camera==target)?1.:0.);
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
A_StopSound(CHAN_VOICE);
|
|
Super.OnDestroy();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target || (target.waterlevel < 3) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos,true);
|
|
curfluid = GetFluid();
|
|
if ( curfluid != lastfluid )
|
|
A_StartSound(fluidsounds[curfluid],CHAN_VOICE,CHANF_LOOPING|CHANF_LISTENERZ,1.,0.);
|
|
lastfluid = curfluid;
|
|
A_SoundVolume(CHAN_VOICE,(players[consoleplayer].camera==target)?1.:0.);
|
|
}
|
|
}
|
|
|
|
// terrain splashes
|
|
Class UTTerrainFX : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SPSH ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_FadeOut(1./26.);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTWaterSplish : UTTerrainFX
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
let r = Spawn("WaterRing",pos);
|
|
r.scale *= 0.3;
|
|
}
|
|
}
|
|
|
|
Class UTWaterSplash : UTTerrainFX
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Spawn("WaterRing",pos);
|
|
}
|
|
}
|
|
|
|
Class WaterRing : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 0.15;
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
alpha -= 1./20.;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
RNGX ABCDEF 5 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTBloodSplish : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
Class UTBloodSplash : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
Class UTSlimeSplish : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
Class UTSlimeSplash : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
Class UTNukageSplish : UTTerrainFX
|
|
{
|
|
Default
|
|
{
|
|
+BRIGHT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
int numpt = Random[Terrain](3,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](2,5);
|
|
let s = Spawn("BioSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Terrain](1,2);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](.6,1.2);
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = pvel;
|
|
s.scale *= 0.7;
|
|
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
|
|
if ( Random[Terrain](0,1) ) s.SetShade("40FF60");
|
|
else s.SetShade("60FF40");
|
|
}
|
|
}
|
|
}
|
|
|
|
Class UTNukageSplash : UTTerrainFX
|
|
{
|
|
Default
|
|
{
|
|
+BRIGHT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
int numpt = Random[Terrain](8,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](3,12);
|
|
let s = Spawn("BioSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Terrain](3,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Terrain](-1,1),FRandom[Terrain](-1,1),FRandom[Terrain](-1,1)).unit()*FRandom[Terrain](1.2,2.4);
|
|
let s = Spawn("UTSmoke",pos);
|
|
s.vel = pvel;
|
|
s.scale *= 2;
|
|
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
|
|
if ( Random[Terrain](0,1) ) s.SetShade("40FF60");
|
|
else s.SetShade("60FF40");
|
|
}
|
|
}
|
|
}
|
|
|
|
Class UTLavaSplish : UTTerrainFX
|
|
{
|
|
Default
|
|
{
|
|
+BRIGHT;
|
|
}
|
|
}
|
|
|
|
Class UTLavaSplash : UTTerrainFX
|
|
{
|
|
Default
|
|
{
|
|
+BRIGHT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Spawn("FlameExplosion",pos);
|
|
}
|
|
}
|
|
|
|
Class FlameExplosion : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
Scale 0.8;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("ut/lavaex",CHAN_VOICE);
|
|
Spawn("SlugSmoke",pos);
|
|
Spawn("SlugLight",pos);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
FEXP ABCDEFGHIJ 2 BRIGHT;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTNitroSplish : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
Class UTNitroSplash : UTTerrainFX
|
|
{
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
States
|
|
{
|
|
See:
|
|
TMAL A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
Class UTPlayerTMale2 : UTPlayer
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tmale2";
|
|
Player.DisplayName "$N_TMALE2";
|
|
Player.Portrait "Brock";
|
|
UTPlayer.VoiceType VOICE_MaleTwo;
|
|
-NOMENU;
|
|
}
|
|
States
|
|
{
|
|
See:
|
|
TMAL B -1;
|
|
Stop;
|
|
}
|
|
}
|
|
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_PlayerScreamDT();
|
|
PLD6 B 3 A_NoBlocking();
|
|
PLD6 CDEFGHIJ 3;
|
|
PLD6 K 1 A_DMFade();
|
|
Wait;
|
|
Death.Zapped:
|
|
#### # 3;
|
|
PLD9 A 3 A_PlayerScreamDT();
|
|
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_PlayerScreamDT();
|
|
PLD7 B 3 A_NoBlocking();
|
|
PLD7 CDEFGHIJ 3;
|
|
PLD7 K 1 A_DMFade();
|
|
Wait;
|
|
Death5:
|
|
#### # 0 A_LegPop();
|
|
PLD5 A 3 A_PlayerScreamDT();
|
|
PLD5 B 3 A_NoBlocking();
|
|
PLD5 CDEFGHIJKL 3;
|
|
PLD5 M 1 A_DMFade();
|
|
Wait;
|
|
Death4:
|
|
#### # 3;
|
|
PLD4 A 3 A_PlayerScreamDT();
|
|
PLD4 B 3 A_NoBlocking();
|
|
PLD4 CDEFGHIJKL 3;
|
|
PLD4 M 1 A_DMFade();
|
|
Wait;
|
|
Death3:
|
|
#### # 3;
|
|
PLD3 A 3 A_PlayerScreamDT();
|
|
PLD3 B 3 A_NoBlocking();
|
|
PLD3 CDEFGHIJKLMNO 3;
|
|
PLD3 P 1 A_DMFade();
|
|
Wait;
|
|
Death2:
|
|
#### # 3;
|
|
PLD2 A 3 A_PlayerScreamDT();
|
|
PLD2 B 3 A_NoBlocking();
|
|
PLD2 CDEFGHIJKLMNOPQ 3;
|
|
PLD2 R 1 A_DMFade();
|
|
Wait;
|
|
Death1:
|
|
#### # 3;
|
|
PLD1 A 3 A_PlayerScreamDT();
|
|
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;
|
|
}
|
|
States
|
|
{
|
|
See:
|
|
TFEM A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
Class UTPlayerTFemale2 : UTPlayerTFemale
|
|
{
|
|
Default
|
|
{
|
|
Player.SoundClass "tfemale";
|
|
Player.DisplayName "$N_TFEMALE2";
|
|
Player.Portrait "Lauren";
|
|
UTPlayer.VoiceType VOICE_FemaleTwo;
|
|
-NOMENU;
|
|
}
|
|
States
|
|
{
|
|
See:
|
|
TFEM B -1;
|
|
Stop;
|
|
}
|
|
}
|
|
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_StartSound("ut/bossfootstep",CHAN_FOOTSTEP,CHANF_OVERLAP,vol);
|
|
else Super.PlayFootstep(vol);
|
|
}
|
|
States
|
|
{
|
|
See:
|
|
TBOS A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTWeapon : Weapon
|
|
{
|
|
int DropAmmo;
|
|
bool bExtraPickup;
|
|
transient int lastnoammotic;
|
|
|
|
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 DetachFromOwner()
|
|
{
|
|
Owner.A_StopSound(CHAN_WEAPON);
|
|
Owner.A_StopSound(CHAN_WEAPONMISC);
|
|
Owner.A_StopSound(CHAN_LEFTWEAPON);
|
|
Owner.A_StopSound(CHAN_LEFTWEAPONMISC);
|
|
Super.DetachFromOwner();
|
|
}
|
|
override void OwnerDied()
|
|
{
|
|
if ( Owner.player && (Owner.player.ReadyWeapon == self) )
|
|
{
|
|
Owner.A_StopSound(CHAN_WEAPON);
|
|
Owner.A_StopSound(CHAN_WEAPONMISC);
|
|
Owner.A_StopSound(CHAN_LEFTWEAPON);
|
|
Owner.A_StopSound(CHAN_LEFTWEAPONMISC);
|
|
}
|
|
A_ClearRefire();
|
|
Super.OwnerDied();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount )
|
|
{
|
|
bool rslt = Super.CheckAmmo(fireMode,autoSwitch,requireAmmo,ammocount);
|
|
if ( (gametic <= lastnoammotic) || rslt || !Owner.CheckLocalView() || !(Owner is 'UTPlayer') || !UTPlayer(Owner).doprintnoammo ) return rslt;
|
|
lastnoammotic = gametic;
|
|
Console.Printf(StringTable.Localize((Amount>1)?"$M_NOAMMO2":"$M_NOAMMO"),GetTag());
|
|
return rslt;
|
|
}
|
|
|
|
// For clips
|
|
virtual clearscope int, int, bool, bool GetClipAmount() const
|
|
{
|
|
return -1, -1, false, false;
|
|
}
|
|
|
|
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*(.5+.5*cos(GetAge()*30)));
|
|
args[LIGHT_GREEN] = int(160*alpha*(.5+.5*cos(GetAge()*30)));
|
|
args[LIGHT_BLUE] = int(255*alpha*(.5+.5*cos(GetAge()*30)));
|
|
args[LIGHT_INTENSITY] = Random[Tele](10,14)*8;
|
|
alpha -= 1./TICRATE;
|
|
}
|
|
}
|
|
|
|
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 -= 1.5/TICRATE;
|
|
}
|
|
}
|
|
|
|
Class UTTeleportFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOTELEPORT;
|
|
+NOGRAVITY;
|
|
RenderStyle "Add";
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Spawn("UTTeleportLight",Vec3Offset(0,0,16));
|
|
A_StartSound("misc/teleport");
|
|
Spawn("UTTeleportParticles",Vec3Offset(0,0,16));
|
|
}
|
|
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",Vec3Offset(0,0,16));
|
|
Spawn("UTRespawnParticles",Vec3Offset(0,0,12));
|
|
}
|
|
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 = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
|
|
SetOrigin(origin,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 = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
|
|
SetOrigin(origin,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);
|
|
if ( target.bIceCorpse ) bNOBLOOD = true; // ice statue
|
|
}
|
|
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_StartSound("misc/corpsefall",CHAN_BODY,CHANF_DEFAULT,clamp(-lastvel.z*.2,.1,1.));
|
|
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 chunks if frozen
|
|
if ( target.bIceCorpse )
|
|
{
|
|
target.bSHATTERING = true;
|
|
if ( target is 'UTPlayer' ) UTPlayer(target).A_FreezeDeathChunksDT();
|
|
else target.A_FreezeDeathChunks();
|
|
Destroy();
|
|
return 0;
|
|
}
|
|
// 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/LT_TexturePaletteLoop
|
|
Class PaletteLight : DynamicLight
|
|
{
|
|
Color pal[256];
|
|
bool IsLooping;
|
|
|
|
Default
|
|
{
|
|
Tag "Explosion";
|
|
DynamicLight.Type "Point";
|
|
Args 0,0,0,80;
|
|
ReactionTime 15;
|
|
}
|
|
private void UpdateLight()
|
|
{
|
|
int index = 255-((255*ReactionTime)/abs(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);
|
|
}
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
ReactionTime = -ReactionTime;
|
|
IsLooping = true;
|
|
}
|
|
UpdateLight();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
ReactionTime--;
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
if ( !IsLooping )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
else ReactionTime = abs(default.ReactionTime);
|
|
}
|
|
UpdateLight();
|
|
}
|
|
}
|
|
|
|
// A bParticles type actor, reads from the anivfile directly
|
|
// Tag: semicolon-separated, starting with the model, then the particle class
|
|
// of UTMeshParticle to use
|
|
Class UTParticleMesh : Actor
|
|
{
|
|
Array<double> px, py, pz;
|
|
Array<Actor> parts;
|
|
Class<UTMeshParticle> pclass;
|
|
int numframes, numverts;
|
|
double animframe, animrate;
|
|
|
|
Default
|
|
{
|
|
Tag "telepo;UTMeshParticle";
|
|
Args 28; // animation rate in fps
|
|
ReactionTime 35; // total lifespan
|
|
XScale 0.015; // scale of the model
|
|
YScale 0.03; // scale of the model
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Array<String> strs;
|
|
strs.Clear();
|
|
GetTag().Split(strs,";");
|
|
pclass = strs[1];
|
|
int lump = Wads.CheckNumForFullname(String.Format("models/%s_a.3d",strs[0]));
|
|
String anivfile = Wads.ReadLump(lump);
|
|
numframes = anivfile.ByteAt(0);
|
|
numframes |= anivfile.ByteAt(1)<<8;
|
|
int fsiz = anivfile.ByteAt(2);
|
|
fsiz |= anivfile.ByteAt(3)<<8;
|
|
numverts = fsiz/4;
|
|
px.Resize(numverts*numframes);
|
|
py.Resize(numverts*numframes);
|
|
pz.Resize(numverts*numframes);
|
|
parts.Resize(numverts);
|
|
int cursor = 4;
|
|
for ( int i=0; i<numverts*numframes; i++ )
|
|
{
|
|
int avert = anivfile.ByteAt(cursor++);
|
|
avert |= anivfile.ByteAt(cursor++)<<8;
|
|
avert |= anivfile.ByteAt(cursor++)<<16;
|
|
avert |= anivfile.ByteAt(cursor++)<<24;
|
|
int ax = ((avert&0x7ff)<<21),
|
|
ay = ((avert>>11)&0x7ff)<<21,
|
|
az = ((avert>>22)&0x3ff)<<22;
|
|
px[i] = ax/2097152.;
|
|
py[i] = ay/2097152.;
|
|
pz[i] = az/4194304.;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
for ( int i=0; i<numverts; i++ )
|
|
parts[i] = Spawn(pclass,level.Vec3Offset(pos,px[i]*scale.x*x+py[i]*scale.x*y+pz[i]*scale.y*z));
|
|
animframe = 0;
|
|
animrate = Args[0];
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
for ( int i=0; i<numverts; i++ )
|
|
{
|
|
if ( !parts[i] ) continue;
|
|
parts[i].Destroy();
|
|
}
|
|
Super.OnDestroy();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
animframe += animrate/TICRATE;
|
|
ReactionTime--;
|
|
if ( (ceil(animframe) >= numframes) || (ReactionTime <= 0) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
int framea = int(floor(animframe)), frameb = int(ceil(animframe));
|
|
double theta = animframe-framea;
|
|
Vector3 posa, posb, ipos;
|
|
Vector3 x, y, z;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
for ( int i=0; i<numverts; i++ )
|
|
{
|
|
posa = (px[i+numverts*framea],py[i+numverts*framea],pz[i+numverts*framea]);
|
|
posb = (px[i+numverts*frameb],py[i+numverts*frameb],pz[i+numverts*frameb]);
|
|
ipos = posa*(1.-theta)+posb*theta;
|
|
if ( !parts[i] ) parts[i] = Spawn(pclass,level.Vec3Offset(pos,ipos.x*scale.x*x+ipos.y*scale.x*y+ipos.z*scale.y*z));
|
|
else parts[i].SetOrigin(level.Vec3Offset(pos,ipos.x*scale.x*x+ipos.y*scale.x*y+ipos.z*scale.y*z),true);
|
|
parts[i].alpha = ReactionTime/double(default.ReactionTime);
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Individual particle
|
|
Class UTMeshParticle : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 0.25;
|
|
FloatBobPhase 0;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
DBEF A -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
Class UTRespawnParticles : UTParticleMesh
|
|
{
|
|
Default
|
|
{
|
|
Tag "telepo;UTRespawnParticle";
|
|
}
|
|
}
|
|
Class UTRespawnParticle : UTMeshParticle
|
|
{
|
|
Default
|
|
{
|
|
Scale 1.1;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
A_SetScale(0.03+alpha*0.37);
|
|
}
|
|
}
|
|
Class UTTeleportParticles : UTParticleMesh
|
|
{
|
|
Default
|
|
{
|
|
Tag "telepo;UTTeleParticle";
|
|
Args 21;
|
|
XScale 0.06;
|
|
YScale 0.16;
|
|
}
|
|
}
|
|
Class UTTeleParticle : UTMeshParticle
|
|
{
|
|
Default
|
|
{
|
|
Scale 0.2;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetState(FindState("Spawn")+Random[UTParticle](0,7));
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
UTFL ABCDEFGH -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class WaterHit
|
|
{
|
|
Sector sect;
|
|
Vector3 hitpos;
|
|
}
|
|
|
|
Class UTInvisibleSplasher : Actor
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 2;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Follows a single line trail, spawns bubbles if underwater
|
|
// In addition, also activates shoot-through lines, since LineTrace can't do it
|
|
// Also spawns splashes when crossing water sectors
|
|
Class UTBulletTrail : LineTracer
|
|
{
|
|
Array<WaterHit> WaterHitList;
|
|
Array<Line> ShootThroughList;
|
|
Actor ignoreme;
|
|
|
|
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, int dist, int bubblechance )
|
|
{
|
|
let t = new("UTBulletTrail");
|
|
t.ignoreme = target;
|
|
t.WaterHitList.Clear();
|
|
t.ShootThroughList.Clear();
|
|
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0);
|
|
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
|
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
|
{
|
|
let b = Actor.Spawn("UTInvisibleSplasher",t.WaterHitList[0].hitpos);
|
|
b.A_CheckTerrain();
|
|
}
|
|
for ( int i=5; i<t.Results.Distance; i+=10 )
|
|
{
|
|
if ( !Random[Boolet](0,bubblechance) ) continue;
|
|
let b = Actor.Spawn("UTBubble",level.Vec3Offset(pos,dir*i));
|
|
b.Scale *= FRandom[Boolet](0.4,0.6);
|
|
}
|
|
t.Destroy();
|
|
}
|
|
|
|
override ETraceStatus TraceCallback()
|
|
{
|
|
// liquid splashes
|
|
if ( Results.CrossedWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.CrossedWater;
|
|
hl.hitpos = Results.CrossedWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
else if ( Results.Crossed3DWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.Crossed3DWater;
|
|
hl.hitpos = Results.Crossed3DWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
if ( Results.HitType == TRACE_HitActor )
|
|
{
|
|
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
|
|
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
|
|
return TRACE_Skip;
|
|
}
|
|
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
|
|
{
|
|
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
|
|
return TRACE_Stop;
|
|
ShootThroughList.Push(Results.HitLine);
|
|
return TRACE_Skip;
|
|
}
|
|
return TRACE_Stop;
|
|
}
|
|
}
|
|
|
|
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[3];
|
|
|
|
ui void StartMenu()
|
|
{
|
|
tex[1] = TexMan.CheckForTexture("graphics/DTLogo.png",TexMan.Type_Any);
|
|
CVar protomenu = CVar.GetCVar('flak_protomenu',players[consoleplayer]);
|
|
if ( !protomenu ) return; // this can happen
|
|
int proto = protomenu.GetInt();
|
|
if ( proto )
|
|
{
|
|
tex[0] = TexMan.CheckForTexture("graphics/UTProtoBg.png",TexMan.Type_Any);
|
|
tex[2] = TexMan.CheckForTexture("graphics/protobg.png",TexMan.Type_Any);
|
|
if ( gamestate != GS_TITLELEVEL ) return;
|
|
if ( proto > 1 ) S_ChangeMusic("menu2");
|
|
else S_ChangeMusic("xyzdMenu");
|
|
}
|
|
else
|
|
{
|
|
tex[0] = TexMan.CheckForTexture("graphics/UTBg.png",TexMan.Type_Any);
|
|
tex[2] = TexMan.CheckForTexture("graphics/finalbg.png",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 RenderUnderlay( RenderEvent e )
|
|
{
|
|
if ( gamestate != GS_TITLELEVEL ) return;
|
|
double ar = Screen.GetAspectRatio();
|
|
Vector2 tsize = TexMan.GetScaledSize(tex[0]);
|
|
double sar = tsize.x/tsize.y;
|
|
Vector2 vsize;
|
|
if ( sar > ar ) vsize = (tsize.y*ar,tsize.y);
|
|
else if ( sar < ar ) vsize = (tsize.x,tsize.x/ar);
|
|
else vsize = tsize;
|
|
Screen.DrawTexture(tex[0],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
|
|
tsize = TexMan.GetScaledSize(tex[1]);
|
|
sar = tsize.x/tsize.y;
|
|
if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
|
|
else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
|
|
else vsize = tsize;
|
|
Screen.DrawTexture(tex[1],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
|
|
}
|
|
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
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 || (Menu.GetCurrentMenu() is 'ConversationMenu') ) return;
|
|
if ( !CVar.GetCVar('flak_showmenu',players[consoleplayer]).GetBool() ) return;
|
|
Screen.Dim("Black",1.,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
Screen.DrawTexture(tex[2],true,0,0,DTA_VirtualWidth,1024,DTA_VirtualHeight,768);
|
|
}
|
|
}
|
|
|
|
// nothing at all
|
|
Class UTNothing : Actor
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class UTMainHandler : EventHandler
|
|
{
|
|
Array<QueuedFlash> flashes;
|
|
TextureID gframes[166];
|
|
transient ui int gframe;
|
|
transient ui Font rfont;
|
|
bool isbd, ispb;
|
|
|
|
override void CheckReplacement( ReplaceEvent e )
|
|
{
|
|
if ( e.IsFinal ) return;
|
|
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.Replacee == 'Mace') ) 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 = 'UTInvulnerability';
|
|
else if ( e.Replacee == 'Berserk' ) e.Replacement = 'UDamage';
|
|
else if ( (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiEgg') || (e.Replacee == 'ArtiPork') ) 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.Replacee == 'EnchantedShield') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'UTShieldBelt';
|
|
else if ( (e.Replacee == 'ArtiInvulnerability') || (e.Replacee == 'ArtiInvulnerability2') ) e.Replacement = 'ActUTInvulnerability';
|
|
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 = 'ActUTInvisibility';
|
|
else if ( e.Replacee == 'Infrared' ) e.Replacement = 'UTNightVision';
|
|
else if ( e.Replacee == 'ArtiTorch' ) e.Replacement = 'ActUTNightVision';
|
|
else if ( e.Replacee == 'RadSuit' ) e.Replacement = 'UTJumpBoots';
|
|
else if ( (e.Replacee == 'ArtiFly') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'ActJumpBoots';
|
|
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') || (e.Replacee == 'ArtiHealingRadius') ) e.Replacement = 'UTBackpack';
|
|
else if ( (e.Replacee == 'ArmorBonus') || (e.Replacee == 'ArtiTimeBomb') || (e.Replacee == 'ArtiBlastRadius') ) e.Replacement = 'UTArmorBonus';
|
|
else if ( (e.Replacee == 'HealthBonus') || (e.Replacee == 'CrystalVial') ) e.Replacement = 'UTHealthBonus';
|
|
else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'AmuletOfWarding') || (e.Replacee == 'PlatinumHelm') ) e.Replacement = 'UTThighPads';
|
|
else if ( e.Replacee == 'Silvershield' )
|
|
{
|
|
if ( Random[Replacements](0,1) ) e.Replacement = 'UTThighPads';
|
|
else e.Replacement = 'UTBodyArmor';
|
|
}
|
|
else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'FalconShield') || (e.Replacee == 'MeshArmor') ) 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') || (e.Replacee == 'ArtiTeleportOther') || (e.Replacee == 'ArtiDarkServant') )
|
|
{
|
|
// I have no idea what to replace this with, so just have some random stuff
|
|
if ( (gameinfo.gametype&GAME_Hexen) && Random[Replacements](0,1) ) e.Replacement = 'UTBackpack';
|
|
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.Replacee == 'BloodSplatter') || (e.Replacee == 'AxeBlood')) ) 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';
|
|
else if ( e.Replacee.GetClassName() == 'KeyRed' )
|
|
{
|
|
// pretty sure that is a standardized name, so it should work for any heretic mod that adds it
|
|
e.Replacement = 'UTHereticRedKey';
|
|
}
|
|
// Hexen weapon replacements will attempt to follow some sort of progression
|
|
// (also additional support for Hexmas because of course)
|
|
else if ( (e.Replacee == 'FWeapFist') || (e.Replacee == 'CWeapMace') || (e.Replacee == 'MWeapWand') )
|
|
e.Replacement = 'ImpactHammer';
|
|
else if ( e.Replacee == 'FWeapAxe' ) e.Replacement = 'BioRifle';
|
|
else if ( e.Replacee == 'CWeapStaff' ) e.Replacement = 'ShockRifle';
|
|
else if ( e.Replacee == 'MWeapFrost' ) e.Replacement = 'PulseGun';
|
|
else if ( (e.Replacee == 'FWeaponPiece3') || (e.Replacee == 'FWeapQuietus') || (e.Replacee.GetClassName() == 'mkFullQuietus') ) e.Replacement = 'FlakCannon';
|
|
else if ( e.Replacee == 'CWeaponPiece3' ) e.Replacement = 'UTBackpack';
|
|
else if ( e.Replacee == 'MWeaponPiece3' ) e.Replacement = 'Enforcer';
|
|
else if ( e.Replacee == 'FWeapHammer' ) e.Replacement = 'Ripper2';
|
|
else if ( e.Replacee == 'CWeapFlame' ) e.Replacement = 'Minigun';
|
|
else if ( e.Replacee == 'MWeapLightning' ) e.Replacement = 'SniperRifle';
|
|
else if ( e.Replacee == 'FWeaponPiece2' ) e.Replacement = 'UTChainsaw';
|
|
else if ( (e.Replacee == 'CWeaponPiece2') || (e.Replacee == 'CWeapWraithverge') || (e.Replacee.GetClassName() == 'mkFullWraithverge') ) e.Replacement = 'UTRocketLauncher';
|
|
else if ( e.Replacee == 'MWeaponPiece2' ) e.Replacement = 'UTBackpack';
|
|
else if ( e.Replacee == 'FWeaponPiece1' ) e.Replacement = 'WarheadAmmo';
|
|
else if ( e.Replacee == 'CWeaponPiece1' ) e.Replacement = 'UTBackpack';
|
|
else if ( (e.Replacee == 'MWeaponPiece1') || (e.Replacee == 'MWeapBloodscourge') || (e.Replacee.GetClassName() == 'mkFullBloodscourge') ) e.Replacement = 'WarheadLauncher';
|
|
else if ( (e.Replacee == 'Mana1') || (e.Replacee is 'ArtiPoisonBag') ) e.Replacement = 'UTMinorAmmoBox';
|
|
else if ( e.Replacee == 'Mana2' ) e.Replacement = 'UTMediumAmmoBox';
|
|
else if ( e.Replacee == 'Mana3' ) e.Replacement = 'UTMajorAmmoBox';
|
|
else if ( e.Replacee == 'ArtiBoostMana' ) e.Replacement = 'ActUTFullAmmoBox';
|
|
// TODO Strife replacements
|
|
}
|
|
|
|
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_StartSound(snd,CHAN_BODY,CHANF_LOOPING,volume,attenuation);
|
|
return a;
|
|
}
|
|
|
|
override void WorldLoaded( WorldEvent e )
|
|
{
|
|
isbd = ispb = false;
|
|
for ( int i=0; i<AllActorClasses.size(); i++ )
|
|
{
|
|
if ( AllActorClasses[i].GetClassName() == "BrutalWeapon" )
|
|
isbd = true;
|
|
else if ( AllActorClasses[i].GetClassName() == "BrutalDoomer" )
|
|
ispb = true;
|
|
}
|
|
if ( isbd || ispb )
|
|
{
|
|
for ( int i=0; i<166; i++ )
|
|
gframes[i] = TexMan.CheckForTexture(String.Format("graphics/grunt/B_%03d.jpg",i+1),TexMan.Type_Any);
|
|
}
|
|
if ( level.levelname != "Modder Test Map" ) return;
|
|
// just replace the -noflat- with a better scaled version and change the sky
|
|
if ( !flak_doomtest )
|
|
{
|
|
Level.ReplaceTextures("-noflat-","DefTex",0);
|
|
TextureID skytx = TexMan.CheckForTexture("BlueSky",TexMan.Type_Any);
|
|
level.ChangeSky(skytx,skytx);
|
|
return;
|
|
}
|
|
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);
|
|
// prettify Kinsie's test map for a more Unreal feel
|
|
for ( int i=0; i<level.sectors.size(); i++ )
|
|
{
|
|
level.sectors[i].lightlevel = min(level.sectors[i].lightlevel,128);
|
|
if ( level.sectors[i].GetPlaneLight(0) ) level.sectors[i].SetPlaneLight(0,128);
|
|
if ( level.sectors[i].GetPlaneLight(1) ) level.sectors[i].SetPlaneLight(1,128);
|
|
// 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 )
|
|
{
|
|
if ( gameinfo.gametype&GAME_DOOM )
|
|
level.sectors[i].SetTexture(0,((i==47)||((i>=256)&&(i<=260)))?baseceil:baseflor);
|
|
else if ( gameinfo.gametype&GAME_HERETIC )
|
|
level.sectors[i].SetTexture(0,((i==47)||((i>=144)&&(i<=148)))?baseceil:baseflor);
|
|
else if ( gameinfo.gametype&GAME_HEXEN )
|
|
level.sectors[i].SetTexture(0,((i==47)||((i>=152)&&(i<=155)))?baseceil:baseflor);
|
|
level.sectors[i].SetXScale(0,2.);
|
|
level.sectors[i].SetYScale(0,2.);
|
|
}
|
|
if ( level.sectors[i].GetTexture(1) == deftex )
|
|
{
|
|
if ( gameinfo.gametype&GAME_DOOM )
|
|
level.sectors[i].SetTexture(1,((i==47)||((i>=256)&&(i<=260)))?baseflor:baseceil);
|
|
else if ( gameinfo.gametype&GAME_HERETIC )
|
|
level.sectors[i].SetTexture(1,((i==47)||((i>=144)&&(i<=148)))?baseflor:baseceil);
|
|
else if ( gameinfo.gametype&GAME_HEXEN )
|
|
level.sectors[i].SetTexture(1,((i==47)||((i>=152)&&(i<=155)))?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 ( ((gameinfo.gametype&GAME_DOOM) && ((i==529) || (i==530) || (i==533) || (i==534)))
|
|
|| ((gameinfo.gametype&GAME_RAVEN) && ((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
|
|
level.sectors[53].SetFade("00 00 20");
|
|
if ( gameinfo.gametype&GAME_DOOM )
|
|
{
|
|
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.sides[1844].SetTexture(1,glasstex);
|
|
level.lines[945].alpha = 0.5;
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HERETIC )
|
|
{
|
|
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.sides[216].SetTexture(1,glasstex);
|
|
level.lines[125].alpha = 0.5;
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HEXEN )
|
|
{
|
|
for ( int i=111; i<141; i++ )
|
|
{
|
|
if ( (i==114) || (i==117) || (i==123)
|
|
|| (i==126) || (i==128) || (i==134)
|
|
|| (i==139) ) continue;
|
|
level.sectors[i].SetSpecialColor(0,"00 00 00");
|
|
}
|
|
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);
|
|
if ( gameinfo.gametype&GAME_DOOM )
|
|
{
|
|
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);
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HERETIC )
|
|
{
|
|
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);
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HEXEN )
|
|
{
|
|
AddLight((1824,1760,64),"40 80 20",128);
|
|
AddLight((2080,1760,64),"FF 80 20",128);
|
|
AddLight((2336,1760,64),"20 20 80",128);
|
|
AddLight((2592,1760,64),"20 20 80",128);
|
|
AddLight((2944,960,64),"40 80 20",128);
|
|
AddLight((2944,736,64),"40 80 20",128);
|
|
AddLight((3264,960,64),"40 80 20",128);
|
|
AddLight((3264,736,64),"40 80 20",128);
|
|
AddLight((3264,512,64),"40 80 20",128);
|
|
AddLight((3264,288,64),"FF 80 20",128);
|
|
AddLight((3584,960,64),"40 80 20",128);
|
|
AddLight((3584,736,64),"FF 80 20",128);
|
|
AddLight((3584,512,64),"FF 80 20",128);
|
|
AddLight((3584,288,64),"FF 80 20",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);
|
|
if ( gameinfo.gametype&GAME_DOOM )
|
|
{
|
|
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);
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HERETIC )
|
|
{
|
|
AddAmbient((2336,1760,64),"testamb/water",0.4,2.4);
|
|
AddAmbient((2592,1760,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((3264,288,64),"testamb/lava",0.8,2.4);
|
|
}
|
|
else if ( gameinfo.gametype&GAME_HEXEN )
|
|
{
|
|
AddAmbient((2080,1760,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((3264,288,64),"testamb/lava",0.8,2.4);
|
|
AddAmbient((3584,736,64),"testamb/lava",0.4,2.4);
|
|
AddAmbient((3584,288,64),"testamb/lava",0.4,2.4);
|
|
}
|
|
AddAmbient((3584,512,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?e.Inflictor.FindInventory("UTChainsaw"):null);
|
|
if ( !saw ) saw = Weapon(e.DamageSource?e.DamageSource.FindInventory("UTChainsaw"):null);
|
|
bool current = false;
|
|
if ( e.Inflictor && e.Inflictor.player && (e.Inflictor.player.ReadyWeapon == saw) ) current = true;
|
|
if ( e.DamageSource && 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) || a.bCANNOTPUSH || (a.Mass >= 10000000) )
|
|
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 || !Victim.bSHOOTABLE || Victim.bCANNOTPUSH || (Victim.Mass >= 10000000) ) 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;
|
|
}
|
|
|
|
override void UiTick()
|
|
{
|
|
if ( !ispb && !isbd ) return;
|
|
if ( !(gametic%2) )
|
|
{
|
|
if ( !(gframe%11) && gframe < 160 )
|
|
{
|
|
S_StartSound("ut/malejump",CHAN_VOICE,CHANF_UI|CHANF_MAYBE_LOCAL,(gframe>100)?.5:1.);
|
|
S_StartSound("ut/malejump",CHAN_WEAPON,CHANF_UI|CHANF_MAYBE_LOCAL,(gframe>100)?.5:1.);
|
|
}
|
|
if ( ((gframe >= 37) && (gframe < 49) && !((gframe-37)%3))
|
|
|| ((gframe >= 89) && (gframe < 101) && !((gframe-89)%3))
|
|
|| ((gframe >= 141) && (gframe < 153) && !((gframe-141)%3)) )
|
|
{
|
|
S_StartSound("ut/land",CHAN_BODY,CHANF_UI|CHANF_MAYBE_LOCAL,(gframe>100)?.5:1.);
|
|
S_StartSound("ut/land",CHAN_ITEM,CHANF_UI|CHANF_MAYBE_LOCAL,(gframe>100)?.5:1.);
|
|
}
|
|
gframe++;
|
|
}
|
|
if ( gframe >= 166 ) gframe = 0;
|
|
}
|
|
|
|
// almost forgot about this
|
|
override void RenderUnderlay( RenderEvent e )
|
|
{
|
|
if ( !ispb && !isbd ) return;
|
|
double ar = Screen.GetAspectRatio();
|
|
Vector2 tsize = TexMan.GetScaledSize(gframes[gframe]);
|
|
double sar = tsize.x/tsize.y;
|
|
Vector2 vsize;
|
|
if ( sar > ar ) vsize = (tsize.y*ar,tsize.y);
|
|
else if ( sar < ar ) vsize = (tsize.x,tsize.x/ar);
|
|
else vsize = tsize;
|
|
Screen.DrawTexture(gframes[gframe],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add);
|
|
String txtme = "STOP LOADING BRUTAL DOOM WITH EVERYTHING";
|
|
if ( !rfont ) rfont = Font.GetFont('USmallFont');
|
|
Vector2 bcoord = ((CleanWidth-rfont.StringWidth(txtme))*.5,CleanHeight*0.8);
|
|
for ( int i=0; i<txtme.Length(); i++ )
|
|
{
|
|
String chr = txtme.Mid(i,1);
|
|
for ( int j=0; j<8; j++ )
|
|
{
|
|
Vector2 ccoord = bcoord + (cos((gametic+e.fractic+i-j)*8.),sin((gametic+e.fractic+i-j)*8.))*CleanXFac*3;
|
|
Screen.DrawText(rfont,Font.CR_DARKRED,ccoord.x,ccoord.y,chr,DTA_VirtualWidth,CleanWidth,DTA_VirtualHeight,CleanHeight,DTA_KeepRatio,true,DTA_Alpha,(8-j)/8.);
|
|
}
|
|
bcoord.x += rfont.StringWidth(chr);
|
|
}
|
|
bcoord = ((CleanWidth-rfont.StringWidth(txtme))*.5,CleanHeight*0.8);
|
|
for ( int i=0; i<txtme.Length(); i++ )
|
|
{
|
|
String chr = txtme.Mid(i,1);
|
|
Vector2 ccoord = bcoord + (cos((gametic+e.fractic+i)*8.),sin((gametic+e.fractic+i)*8.))*CleanXFac*3;
|
|
Screen.DrawText(rfont,Font.CR_FIRE,ccoord.x,ccoord.y,chr,DTA_VirtualWidth,CleanWidth,DTA_VirtualHeight,CleanHeight,DTA_KeepRatio,true);
|
|
bcoord.x += rfont.StringWidth(chr);
|
|
}
|
|
}
|
|
}
|