Files
quakemapping/mod_xj18/my_progs/client.qc
2020-01-07 11:54:38 +01:00

1269 lines
43 KiB
Plaintext

/*======================================================================
CLIENT FUNCTIONS
======================================================================*/
// Vanilla Quake Inventory reset combined flag
// IT_SUPERHEALTH, IT_KEY1, IT_KEY2
// IT_INVISIBILITY, IT_INVULNERABILITY, IT_SUIT, IT_QUAD
float IT_ITEMRESET = 8323072; // 65536++
float IT_MODRESET = 8380416; // 8192++
float ALL_WEAPONS = 255;
// client_camera.qc
void() SetupIntermissionCamera;
void() CycleIntermissionCamera;
// client_debuff.qc AND client_power.qc
void() ClientDeBuff;
void() ClientPowerups;
// player.qc
void(entity inflictor, entity attacker, float damage) player_pain;
void() player_death;
void() player_stand1;
// triggers.qc
void() trigger_changelevel_finish;
void() trigger_cdtrack_change;
void(entity targ) trigger_skybox_change;
/*======================================================================
USED : Load (map command) NEW MAP or NEW GAME
This is a function called via the engine for new games
Resets all parm data back to default values, followed by Decode
======================================================================*/
void() SetNewParms =
{
dprint("\b[CLIENT]\b SetNewParms\n");
// Brand new game/map, reset start position settings
update_configflag(SVR_SPAWN_BIT1, FALSE);
update_configflag(SVR_SPAWN_BIT2, FALSE);
update_configflag(SVR_SPAWN_BIT3, FALSE);
fog_active = FALSE;
parm1 = IT_SHOTGUN | IT_AXE; // Default ID weapon start
parm2 = 100; // Starting Health
parm3 = 0; parm9 = 0; // Armourtype and value
parm4 = 25; parm5 = 0; // Ammo qty (shell/nail/rocket/cells)
parm6 = 0; parm7 = 0;
parm8 = 1; // Current selected weapon
parm10 = 0; // New MOD items
// Update new player inventory based on config flag
if (query_configflag(SVR_UPDAXE)) parm10 = parm10 | IT_UPGRADE_AXE;
if (query_configflag(SVR_UPDSSG)) parm10 = parm10 | IT_UPGRADE_SSG;
if (query_configflag(SVR_UPDLG)) parm10 = parm10 | IT_UPGRADE_LG;
// Are map variables live?
if (mapvar_cvar) dprint("\b[CLIENT]\b LIVE Map Variables Detected\n");
else {
dprint("\b[CLIENT]\b RESETTING Map Variables\n");
// Reset map variables ready for use
parm11 = parm12 = parm13 = parm14 = parm15 = parm16 = 0;
mapvar_reset();
}
};
/*======================================================================
// USED : trigger CHANGE LEVEL
// Stores a copy of the parm data ready for death/level reset
======================================================================*/
void() SetChangeParms =
{
dprint("\n\b[CLIENT]\b SetChangeParms\n");
if (self.health < 1) { SetNewParms (); return; }
// remove temporary items (keys and powerups)
self.items = self.items - (self.items & IT_ITEMRESET);
parm1 = self.items;
self.moditems = self.moditems - (self.moditems & IT_MODRESET);
parm10 = self.moditems;
dprint("\b[CLIENT]\b MOD weapons ("); dprint(ftos(parm10)); dprint(")\n");
// cap super health
if (self.health > HEAL_PLAYMAX) self.health = HEAL_PLAYMAX;
if (self.health < 50) self.health = 50;
parm2 = self.health;
parm3 = self.armorvalue;
parm9 = self.armortype * 100;
// Always make sure the player has 25 shells
if (self.ammo_shells < DEF_SHELLS) parm4 = DEF_SHELLS;
else parm4 = self.ammo_shells;
parm5 = self.ammo_nails;
parm6 = self.ammo_rockets;
parm7 = self.ammo_cells;
parm8 = self.weapon;
dprint("\b[CLIENT]\b SAVING Map Variables\n");
// Save map variables (22 bits per parm, 132 total)
parm11 = mapvar[0]; parm12 = mapvar[1]; parm13 = mapvar[2];
parm14 = mapvar[3]; parm15 = mapvar[4]; parm16 = mapvar[5];
};
/*======================================================================
USED : Load NEW MAP or trigger CHANGE LEVEL
Not used when loading a saved file or quickload
======================================================================*/
void() DecodeWorldParms =
{
local float parm_items;
parm_items = parm1;
dprint("\b[CLIENT]\b Decode Worldspawn Inv Parms\n");
//----------------------------------------------------------------------
// Show developer feedback on player inventory update
if (world.give_weapons) {
dprint("\b[CLIENT]\b Weapon Give ( ");
if (world.give_weapons & IT_SHOTGUN) dprint("SG ");
if (world.give_weapons & IT_SUPER_SHOTGUN) dprint("SSG ");
if (world.give_weapons & IT_NAILGUN) dprint("NG ");
if (world.give_weapons & IT_SUPER_NAILGUN) dprint("SNG ");
if (world.give_weapons & IT_GRENADE_LAUNCHER) dprint("GL ");
if (world.give_weapons & IT_ROCKET_LAUNCHER) dprint("RL ");
if (world.give_weapons & IT_LIGHTNING) dprint("LG ");
dprint(")\n");
}
if (world.take_weapons) {
dprint("\b[CLIENT]\b Weapon Take ( ");
if (world.take_weapons & IT_SHOTGUN) dprint("SG ");
if (world.take_weapons & IT_SUPER_SHOTGUN) dprint("SSG ");
if (world.take_weapons & IT_NAILGUN) dprint("NG ");
if (world.take_weapons & IT_SUPER_NAILGUN) dprint("SNG ");
if (world.take_weapons & IT_GRENADE_LAUNCHER) dprint("GL ");
if (world.take_weapons & IT_ROCKET_LAUNCHER) dprint("RL ");
if (world.take_weapons & IT_LIGHTNING) dprint("LG ");
dprint(")\n");
}
//----------------------------------------------------------------------
// Adding weapons to existing inventory
if (world.give_weapons) {
parm_items = parm_items | (world.give_weapons & ALL_WEAPONS);
}
//----------------------------------------------------------------------
// Player always has the axe, it cannot be removed!
if (world.take_weapons > 0) {
if (world.take_weapons & IT_SHOTGUN && parm_items & IT_SHOTGUN)
parm_items = parm_items - IT_SHOTGUN;
if (world.take_weapons & IT_SUPER_SHOTGUN && parm_items & IT_SUPER_SHOTGUN) {
parm_items = parm_items - IT_SUPER_SHOTGUN;
// Player cannot have upgrades without base item
parm10 = parm10 - (parm10 & IT_UPGRADE_SSG);
}
if (world.take_weapons & IT_NAILGUN && parm_items & IT_NAILGUN)
parm_items = parm_items - IT_NAILGUN;
if (world.take_weapons & IT_SUPER_NAILGUN && parm_items & IT_SUPER_NAILGUN)
parm_items = parm_items - IT_SUPER_NAILGUN;
if (world.take_weapons & IT_GRENADE_LAUNCHER && parm_items & IT_GRENADE_LAUNCHER)
parm_items = parm_items - IT_GRENADE_LAUNCHER;
if (world.take_weapons & IT_ROCKET_LAUNCHER && parm_items & IT_ROCKET_LAUNCHER)
parm_items = parm_items - IT_ROCKET_LAUNCHER;
if (world.take_weapons & IT_LIGHTNING && parm_items & IT_LIGHTNING) {
parm_items = parm_items - IT_LIGHTNING;
// Player cannot have upgrades without base item
parm10 = parm10 - (parm10 & IT_UPGRADE_LG);
}
}
// Store result back to global variable
parm1 = parm_items;
//----------------------------------------------------------------------
// Update player health (reset or minimum value)
if (world.reset_health > 0 && world.reset_health <= HEAL_PLAYMAX) {
dprint("\b[CLIENT]\b Health ("); dprint(ftos(parm2)); dprint(") To (");
parm2 = world.reset_health;
dprint(ftos(parm2)); dprint(")\n");
}
else if (world.max_health && (parm2 < world.max_health) ) {
dprint("\b[CLIENT]\b Health ("); dprint(ftos(parm2)); dprint(") To (");
if (parm2 < world.max_health) parm2 = world.max_health;
dprint(ftos(parm2)); dprint(")\n");
}
//----------------------------------------------------------------------
// Update the inventory ammo quantities
// currentammo = 1 to reset the inventory to worldspawn values
// currentammo = 0 to use the worldspawn values as a minimum quantity
if (world.ammo_shells || (world.currentammo && parm4 > 0)) {
dprint("\b[CLIENT]\b Ammo Shells ("); dprint(ftos(parm4)); dprint(") To (");
if (world.currentammo) parm4 = world.ammo_shells;
else if (parm4 < world.ammo_shells) parm4 = world.ammo_shells;
if (parm4 > AMMO_MAXSHELLS) parm4 = AMMO_MAXSHELLS;
else if (parm4 < 0) parm4 = 0;
dprint(ftos(parm4)); dprint(")\n");
}
if (world.ammo_nails || (world.currentammo && parm5 > 0)) {
dprint("\b[CLIENT]\b Ammo Spikes ("); dprint(ftos(parm5)); dprint(") To (");
if (world.currentammo) parm5 = world.ammo_nails;
else if (parm5 < world.ammo_nails) parm5 = world.ammo_nails;
if (parm5 > AMMO_MAXNAILS) parm5 = AMMO_MAXNAILS;
else if (parm5 < 0) parm5 = 0;
dprint(ftos(parm5)); dprint(")\n");
}
if (world.ammo_rockets || (world.currentammo && parm6 > 0)) {
dprint("\b[CLIENT]\b Ammo Rockets ("); dprint(ftos(parm6)); dprint(") To (");
if (world.currentammo) parm6 = world.ammo_rockets;
else if (parm6 < world.ammo_rockets) parm6 = world.ammo_rockets;
if (parm6 > AMMO_MAXROCKETS) parm6 = AMMO_MAXROCKETS;
else if (parm6 < 0) parm6 = 0;
dprint(ftos(parm6)); dprint(")\n");
}
if (world.ammo_cells || (world.currentammo && parm7 > 0)) {
dprint("\b[CLIENT]\b Ammo Cells ("); dprint(ftos(parm7)); dprint(") To (");
if (world.currentammo) parm7 = world.ammo_cells;
else if (parm7 < world.ammo_cells) parm7 = world.ammo_cells;
if (parm7 > AMMO_MAXROCKETS) parm7 = AMMO_MAXROCKETS;
else if (parm7 < 0) parm7 = 0;
dprint(ftos(parm7)); dprint(")\n");
}
//----------------------------------------------------------------------
// Update player armour type and quantity
if (world.armortype > 0) {
// Remove any previous armour types from the HUD
parm1 = parm1 - (parm1 & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3));
// Setup any armour quantity first, limit check with armour type
if (world.armorvalue > 0) parm3 = world.armorvalue;
// Work through the different armour types, resetting armour quantity
if (world.armortype == 1) {
parm1 = parm1 | IT_ARMOR1;
parm9 = ARMOR_GRN_TYPE * 100;
if (!world.armorvalue || parm3 > ARMOR_GRN_VALUE) parm3 = ARMOR_GRN_VALUE;
dprint("\b[CLIENT]\b Green Armor ("); dprint(ftos(parm3)); dprint(")\n");
}
else if (world.armortype == 2) {
parm1 = parm1 | IT_ARMOR2;
parm9 = ARMOR_YEL_TYPE * 100;
if (!world.armorvalue || parm3 > ARMOR_YEL_VALUE) parm3 = ARMOR_YEL_VALUE;
dprint("\b[CLIENT]\b Yellow Armor ("); dprint(ftos(parm3)); dprint(")\n");
}
else if (world.armortype == 3) {
parm1 = parm1 | IT_ARMOR3;
parm9 = ARMOR_RED_TYPE * 100;
if (!world.armorvalue || parm3 > ARMOR_RED_VALUE) parm3 = ARMOR_RED_VALUE;
dprint("\b[CLIENT]\b Red Armor ("); dprint(ftos(parm3)); dprint(")\n");
}
// Default is no armour
else parm3 = 0;
}
};
/*======================================================================
USED : Load NEW MAP or trigger CHANGE LEVEL
Double check the player inventory matches the server/config flags
Check worldspawn for add/removal of upgrade items
======================================================================*/
void() SyncModInventory =
{
//----------------------------------------------------------------------
// Shadow Axe
//----------------------------------------------------------------------
// Check worldspawn for upgrade_axe first, add/deny inventory
if (world.upgrade_axe < 0) {
dprint("\b[MODINV]\b Worldspawn removing Axe Upgrade\n");
parm10 = parm10 - (parm10 & IT_UPGRADE_AXE);
update_configflag(SVR_UPDAXE, FALSE);
}
else if (world.upgrade_axe > 0) {
dprint("\b[MODINV]\b Worldspawn adding Axe Upgrade\n");
parm10 = parm10 | IT_UPGRADE_AXE;
parm1 = parm1 | IT_AXE;
update_configflag(SVR_UPDAXE, TRUE);
}
// Finally syncing the player inventory to serverflags
else if (parm10 & IT_UPGRADE_AXE) {
dprint("\b[MODINV]\b Inventory adding Axe Upgrade\n");
update_configflag(SVR_UPDAXE, TRUE);
}
//----------------------------------------------------------------------
// Widowmaker Shotgun
//----------------------------------------------------------------------
// Check worldspawn for upgrade_ssg first, add/deny inventory
if (world.upgrade_ssg < 0) {
dprint("\b[MODINV]\b Worldspawn removing SSG Upgrade\n");
parm10 = parm10 - (parm10 & IT_UPGRADE_SSG);
update_configflag(SVR_UPDSSG, FALSE);
}
else if (world.upgrade_ssg > 0) {
dprint("\b[MODINV]\b Worldspawn adding SSG Upgrade\n");
parm10 = parm10 | IT_UPGRADE_SSG;
parm1 = parm1 | IT_SUPER_SHOTGUN;
update_configflag(SVR_UPDSSG, TRUE);
}
// Syncing the player inventory to serverflags
else if (parm10 & IT_UPGRADE_SSG) {
dprint("\b[MODINV]\b Inventory adding SSG Upgrade\n");
update_configflag(SVR_UPDSSG, TRUE);
}
//----------------------------------------------------------------------
// Projectile Shotgun
//----------------------------------------------------------------------
// Check for worldspawn feature to turn off projectiles + casing
if (world.no_sgprojectile > 0)
update_configflag(SVR_SHOTGPROJ, TRUE);
if (world.no_sgcasing > 0)
update_configflag(SVR_SHOTGCASE, TRUE);
//----------------------------------------------------------------------
// Plasma Gun
//----------------------------------------------------------------------
// Check worldspawn for upgrade_lg first, add/deny inventory
if (world.upgrade_lg < 0) {
dprint("\b[MODINV]\b Worldspawn removing LG Upgrade\n");
parm10 = parm10 - (parm10 & IT_UPGRADE_LG);
update_configflag(SVR_UPDLG, FALSE);
}
else if (world.upgrade_lg > 0) {
dprint("\b[MODINV]\b Worldspawn adding LG Upgrade\n");
parm10 = parm10 | IT_UPGRADE_LG;
parm1 = parm1 | IT_LIGHTNING;
update_configflag(SVR_UPDLG, TRUE);
}
// Finally syncing the player inventory to serverflags
else if (parm10 & IT_UPGRADE_LG) {
dprint("\b[MODINV]\b Inventory adding LG Upgrade\n");
update_configflag(SVR_UPDLG, TRUE);
}
};
/*======================================================================
USED : Load NEW MAP or trigger CHANGE LEVEL
Not used when loading a saved file or quickload
======================================================================*/
void() DecodeLevelParms =
{
dprint("\b[CLIENT]\b Decode (Level) Parms\n");
// Map Variables are active (do not reset)
mapvar_cvar = TRUE;
// Found any runes? going back to start map? reset inventory
if (serverflags & SVR_RUNE_ALL) {
// take away all stuff on starting new episode
if (world.model == "maps/start.bsp") SetNewParms ();
}
DecodeWorldParms(); // Decode worldspawn client inv updates
sync_serverflags(); // update/sync serverflags to worldspawn
SyncModInventory(); // update/sync mod inventory items
// Read all current params into client variables
self.items = parm1;
self.moditems = parm10;
self.health = parm2;
self.ammo_shells = parm4;
self.ammo_nails = parm5;
self.ammo_rockets = parm6;
self.ammo_cells = parm7;
self.weapon = parm8;
self.armortype = parm9 * 0.01;
self.armorvalue = parm3;
dprint("\b[CLIENT]\b Reading Map Variables\n");
// Read/Setup map variables (22 bits per parm, 132 total)
mapvar[0] = parm11; mapvar[1] = parm12; mapvar[2] = parm13;
mapvar[3] = parm14; mapvar[4] = parm15; mapvar[5] = parm16;
// Check for any worldspawn map variable settings
if (CheckZeroVector(world.mapvar_update) == FALSE)
mapvar_range(world.mapvar_update);
};
//----------------------------------------------------------------------
void() GotoNextMap =
{
// if samelevel is set, stay on same level
if (cvar("samelevel")) changelevel (mapname);
else changelevel (nextmap);
};
/*======================================================================
NextLevel (DM ONLY)
- A special function designed for MP only, will cycle around
the first map of each episode until someone quits
gives the player runes as well to tell which to load
======================================================================*/
void() NextLevel =
{
local entity o;
if (mapname == "start") {
if (!cvar("registered")) mapname = "e1m1";
else if (query_configflag(SVR_RUNE_KEY1) == FALSE) {
mapname = "e1m1";
update_configflag(SVR_RUNE_KEY1, TRUE);
}
else if (query_configflag(SVR_RUNE_KEY2) == FALSE) {
mapname = "e2m1";
update_configflag(SVR_RUNE_KEY1, TRUE);
}
else if (query_configflag(SVR_RUNE_KEY3) == FALSE) {
mapname = "e3m1";
update_configflag(SVR_RUNE_KEY1, TRUE);
}
else if (query_configflag(SVR_RUNE_KEY4) == FALSE) {
mapname = "e4m1";
update_configflag(SVR_RUNE_KEY1, FALSE);
update_configflag(SVR_RUNE_KEY2, FALSE);
update_configflag(SVR_RUNE_KEY3, FALSE);
}
o = spawn();
o.map = mapname;
}
else {
// find a trigger changelevel
o = find(world, classname, "trigger_changelevel");
// go back to start if no trigger_changelevel
if (!o) {
mapname = "start";
o = spawn();
o.map = mapname;
}
}
nextmap = o.map;
gameover = TRUE;
if (o.nextthink < time) {
o.think = trigger_changelevel_finish;
o.nextthink = time + 0.1;
}
};
/*======================================================================
CheckRules (DM ONLY)
- Check death timers (time/frag limits)
and decide if to load a new map or not
======================================================================*/
void() CheckRules =
{
local float timelimit, fraglimit;
if (deathmatch == 0) return; // Playing SP?
if (gameover) return; // someone else quit the game already
timelimit = cvar("timelimit") * 60;
fraglimit = cvar("fraglimit");
if (timelimit && time >= timelimit) NextLevel();
else if (fraglimit && self.frags >= fraglimit) NextLevel();
};
//----------------------------------------------------------------------
void() SetupSpawnCoopLocations =
{
local entity cooploc, prevloc;
// The first spawn for coop is always the SP start location
cooploc = find (world, classname, "info_player_coop");
prevloc = world;
// Any coop spawns exist?
if (cooploc) {
// Create a list
while(cooploc) {
// first spawn in chain? setup first and previous spawns
if (!prevloc) {
coop_ent = cooploc;
coop_ent.count = 0;
}
else prevloc.owner = cooploc;
// Save current spawn location to previous ready for loop
prevloc = cooploc;
coop_ent.count = coop_ent.count + 1;
cooploc = find(cooploc, classname, "info_player_coop");
}
// Close the chain
prevloc.owner = coop_ent;
dprint("\b[COOP]\b Spawn locations found (");
dprint(ftos(coop_ent.count)); dprint(")\n");
}
else {
// No coop spawns, use start location
dprint("\b[COOP]\b Spawn locations Missing!\n");
coop_ent = find (world, classname, "info_player_start");
// no start location, WTF!?!
if (!coop_ent) coop_ent = client_ent;
coop_ent.owner = coop_ent;
}
};
//----------------------------------------------------------------------
// Find a location for the player to spawn at
//----------------------------------------------------------------------
entity() SelectSpawnPoint =
{
local entity spot, thing;
local float pcount, start2, runeactive;
// Always use a player start as default position
if (!lastspawn) lastspawn = find (world, classname, "info_player_start");
// Cycle through list of info_player_coop location
if (coop > 0) {
// Check for coop spawn list
if (!coop_ent) return lastspawn;
// If first entry of coop list start player then its broken
else if (coop_ent.classtype == CT_SPAWNSP) return lastspawn;
else {
// Setup coop list if last location isstart player
if (lastspawn.classtype == CT_SPAWNSP) lastspawn = coop_ent;
// Move forward in coop spawn location list
lastspawn = lastspawn.owner;
// Check if spawn location is active?
if (lastspawn.estate & ESTATE_BLOCK) {
pcount = coop_ent.count;
while(pcount > 0) {
lastspawn = lastspawn.owner;
if (lastspawn.estate == ESTATE_ON) pcount = -1;
else pcount = pcount - 1;
}
}
// Double check if spawn location free?
if (lastspawn.estate & ESTATE_BLOCK) {
// Default back to player start if not available
lastspawn = find (world, classname, "info_player_start");
}
return lastspawn;
}
}
// choose a info_player_deathmatch point
else if (deathmatch > 0) {
spot = lastspawn;
while (1) {
spot = find(spot, classname, "info_player_deathmatch");
if (spot == world) spot = find (world, classname, "info_player_start");
if (spot != world) {
if (spot == lastspawn) return lastspawn;
pcount = 0;
thing = findradius(spot.origin, 32);
while(thing) {
if (thing.flags & FL_CLIENT) pcount = pcount + 1;
thing = thing.chain;
}
if (pcount == 0) {
lastspawn = spot;
return spot;
}
}
}
}
// Check for any info_player_start2 special spawn locations
if (query_configflag(SVR_SPAWN_BIT1) > 0) start2 = 1;
else start2 = 0;
if (query_configflag(SVR_SPAWN_BIT2) > 0) start2 = start2 + 2;
if (query_configflag(SVR_SPAWN_BIT3) > 0) start2 = start2 + 4;
if (start2 > 0) dprint("\b[CLIENT]\b Unique Info_Player_Start2 Active\n");
// Check for any runes active
runeactive = query_configflag(SVR_RUNE_ALL);
// have a rune or new spawnflag option?
if ( runeactive > 0 || start2 > 0 ) {
// Find second spawn locations (can have several)
spot = find (world, classname, "info_player_start2");
while (spot) {
// Check for rune only start 2 setup
if (runeactive > 0 && spot.startspawn2 == 0)
return spot;
// Check for special spawn2 location
else if (spot.startspawn2 == start2)
return spot;
// Keep searching for start2 positions
spot = find(spot, classname, "info_player_start2");
}
}
// Last chance to find a spawn location before giving up!
spot = find (world, classname, "info_player_start");
if (!spot) dprint ("\b[CLIENT_SPAWN]\b missing info_player_start!");
return spot;
};
//----------------------------------------------------------------------
// PutClientInServer - called each time a player is spawned
//----------------------------------------------------------------------
void() PutClientInServer =
{
local entity spot;
client_ent = self;
self.classname = "player";
self.classtype = CT_PLAYER;
self.classgroup = CG_PLAYERS;
// This entity can never be switched off
self.estate = ESTATE_ON;
self.estate_off = SUB_Null;
// Setup entity chains for coop, dm and intermission
SetupSpawnCoopLocations();
SetupIntermissionCamera();
// Find a spawn location in the map
spot = SelectSpawnPoint ();
// Fire any targets on spawn location
if (spot.target) self.target2 = spot.target;
else self.target2 = "";
self.headmdl = MODEL_PLAYER_HEAD;
self.health = self.max_health = HEAL_PLAYMAX;
self.takedamage = DAMAGE_AIM;
self.solid = SOLID_SLIDEBOX;
self.movetype = MOVETYPE_WALK;
self.classmove = MON_MOVEWALK;
self.show_hostile = 0;
self.gibhealth = -40;
self.gibbed = FALSE;
self.flags = FL_CLIENT;
self.air_finished = time + WATER_AIR;
self.dmg = WATER_DAMAGE;
self.effects = 0;
self.steplast = 1;
self.super_damage_finished = self.super_time = 0;
self.radsuit_finished = self.rad_time = 0;
self.invisible_finished = self.invisible_time = 0;
self.invincible_finished = self.invincible_time = 0;
self.sharpshoot_finished = self.sharpshoot_time = 0;
self.nailpiercer_finished = self.nailpiercer_time = 0;
self.wetsuit_finished = self.wetsuit_time = 0;
self.steptype = FS_TYPEMEDIUM; // Standard boots
self.suppressCenterPrint = FALSE; // No centerprint active
ResetDebuffSystem(self); // Reset/initialize debuff system
self.enemy = world;
DecodeLevelParms ();
self.weapon = W_BestWeapon (self); // Pick best weapon from inventory
W_SetCurrentAmmo (self); // Update hud icons and v_model
self.attack_finished = time;
self.th_pain = player_pain;
self.th_die = player_death;
self.deadflag = DEAD_NO; // The player is alive!
self.pausetime = 0; // make sure player has 0 velocity when spawning
update_hud_totals(HUD_MONSTERS); // Make screen total is correct
update_hud_totals(HUD_SECRETS); // Update monsters and secrets
// spot = SelectSpawnPoint ();
self.origin = spot.origin + '0 0 1';
if (spot.mangle) self.angles = spot.mangle;
else self.angles = spot.angles;
self.v_angle = self.angles;
self.fixangle = TRUE; // turn this way immediately
// oh, this is a hack!
setmodel (self, MODEL_PLAYER_EYES);
modelindex_eyes = self.modelindex;
setmodel (self, MODEL_PLAYER);
modelindex_player = self.modelindex;
self.bbmins = VEC_HULLSHORT_MIN; // -16 -16 -24, 16 16 32
self.bbmaxs = VEC_HULLSHORT_MAX;
setsize (self, self.bbmins, self.bbmaxs);
self.view_ofs = '0 0 22';
player_stand1 ();
if (deathmatch || coop) {
makevectors(self.angles);
spawn_tfog (self.origin + v_forward*20);
}
spawn_tdeath (self.origin, self);
};
// Forward compiler links
void() PlayerJump;
void() WaterMove;
void() CheckWaterJump;
void() ClientDeath;
//----------------------------------------------------------------------
// PlayerPreThink - Called every frame before physics are run
//----------------------------------------------------------------------
void() PlayerPreThink =
{
// Once the map has loaded (prethink) set loadflag (never saved)
if (!prethink) {
prethink = TRUE; // prethink flag complete
postthink = FALSE; // postthink flag reset
sync_configflag(); // Setup and sync serverflag
// Some clients load faster, prevent player gasping for air
self.air_finished = time + WATER_AIR;
// Double check which engine is active?
// A DP/FTE Quickload could be happening and the
// Particle system needs to cope with the change
ext_active = cvar("pr_checkextension");
if (!ext_active) {
// Revert to default = Fitz engine setup
engine = ENG_FITZ;
ext_dppart = ext_dpfog = ext_dpsurf = FALSE;
ext_dprain = ext_dpsnow = FALSE;
}
// Check for trigger CD track/Skybox change
// Load/Quickload does not keep track of these changes
// Use a new global variable instead
if (trig_cdtrack > 0) trigger_cdtrack_change();
if (trig_skybox != "") trigger_skybox_change(self);
}
// If Intermission system active check for button0-2 keys
if (intermission_running > 0) { CycleIntermissionCamera(); return; }
makevectors (self.v_angle);
CheckRules ();
WaterMove ();
if (self.waterlevel == 2) CheckWaterJump ();
if (self.deadflag >= DEAD_DEAD) {
ClientDeath ();
return;
}
if (self.deadflag == DEAD_DYING) return; // dying, so do nothing
//----------------------------------------------------------------------
// Rubicon Ladder Code - johnfitz
// Modified by me to support diff climb sounds and on/off/toggle states
//----------------------------------------------------------------------
if (self.onladder) {
self.onladder = 0; // Reset ladder touch function
if (self.button2) { // Is jump key being pressed?
// Reset velocity upwards and all sideways movement
// so that the player stays on the ladder and climbs
// straight up with very little sidways movement
self.velocity = '0 0 0';
self.velocity_z = self.entladder.speed;
self.gravity = 0.0000001;
if (self.timeladder < time) {
// Reset timer for next sound to play
self.timeladder = time + self.entladder.waitmin;
// Check for any sounds and query if footsteps are active
if (self.entladder.sounds != 4 && query_configflag(SVR_FOOTSTEP) == FALSE) {
self.lip = random();
if (self.lip < 0.25 && self.entladder.noise1)
sound (self, CHAN_BODY, self.entladder.noise1, 1, ATTN_NORM);
else if (self.lip < 0.5 && self.entladder.noise2)
sound (self, CHAN_BODY, self.entladder.noise2, 1, ATTN_NORM);
else if (self.lip < 0.75 && self.entladder.noise3)
sound (self, CHAN_BODY, self.entladder.noise3, 1, ATTN_NORM);
else if (self.entladder.noise4)
sound (self, CHAN_BODY, self.entladder.noise4, 1, ATTN_NORM);
}
}
}
else {
// Jump key released, stay floating on the ladder
self.flags = self.flags | FL_JUMPRELEASED;
self.velocity = 0.9 * self.velocity;
self.velocity_z = 0;
}
}
else {
// Reset any ladder gravity settings
self.gravity = 1;
// Original jump conditions
if (self.button2) PlayerJump ();
else self.flags = self.flags | FL_JUMPRELEASED;
}
// When the player/client starts in the map they don't want any velocity
if (time < self.pausetime) self.velocity = '0 0 0';
};
//----------------------------------------------------------------------
// PlayerPostThink - Called every frame after physics are run
//----------------------------------------------------------------------
void() PlayerPostThink =
{
if (prethink && !postthink) {
// Check for the player?
// - first couple of frames the player is not setup correctly
if (self.flags & FL_CLIENT) {
postthink = TRUE; // postthink flag complete
mapvar_cvar = TRUE; // Map Variable are live
// Reset any screen cshift parameters, eng often leaves them
if (!self.cshift_upd) self.cshift_time = -1;
// Check worldspawn water alpha parameters?
// liquid alpha is used by the monster visible system
if (world.water_alpha) {
liquid_alpha = world.water_alpha;
stuffcmd(self, "\nr_wateralpha ");
lftos(self, liquid_alpha ,1,3, BUFFER_STUFFCMD);
stuffcmd(self, "\n");
dprint("\b[CLIENT]\b Water Alpha (");
lftos(self, liquid_alpha ,1,3, BUFFER_DPRINT);
dprint(")\n");
}
else liquid_alpha = cvar("r_wateralpha");
// Check engine for actual autoaim setting
// Always reset with newgame and quickload
autoaim_cvar = cvar("sv_aim");
// Setup/change global fog if enabled (default)
if (!(query_configflag(SVR_NOFOGCMDS))) {
// Create fog controller and/or update engine fog
if (fog_active) change_fog(self);
else setup_fog_controller();
}
// Update console with MOD settings
display_configflag();
display_version();
update_weather();
}
}
if (intermission_running > 0) return; // intermission or finale
if (self.deadflag) return; // Dead
// Any spawn targets to update?
if (self.target2 != "") {
trigger_strs(self.target2, self); self.target2 = "";
}
W_WeaponFrame (); // Impulse commands and Quad updates
// check to see if player landed and play landing sound
if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0)) {
if (self.watertype == CONTENT_WATER)
sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
else if (self.jump_flag < -650) {
T_Damage (self, world, world, 5, DAMARMOR);
sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM);
self.deathtype = "falling";
}
else
sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM);
self.jump_flag = 0;
}
// Check for any fake water exit sounds
if (self.touchedliquid < time && self.touchedsound != "") {
sound (self, CHAN_BODY, self.touchedsound, 1, ATTN_NORM);
self.touchedsound = "";
}
// If flying through the air, store player Z velocity in jump flag
if (!(self.flags & FL_ONGROUND)) self.jump_flag = self.velocity_z;
ClientPowerups ();
ClientDeBuff ();
};
//----------------------------------------------------------------------
void() PlayerJump =
{
if (self.flags & FL_WATERJUMP) return;
if (self.waterlevel >= 2) {
if (self.watertype == CONTENT_WATER) self.velocity_z = 100;
else if (self.watertype == CONTENT_SLIME) self.velocity_z = 80;
else self.velocity_z = 50;
// play swiming sound
if (self.swim_flag < time) {
self.swim_flag = time + 1;
if (random() < 0.5) sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM);
else sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM);
}
return;
}
if (!(self.flags & FL_ONGROUND)) return;
if ( !(self.flags & FL_JUMPRELEASED) ) return; // don't pogo stick
self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
self.flags = self.flags - FL_ONGROUND; // don't stairwalk
self.button2 = 0;
// player jumping sound
sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
self.velocity_z = self.velocity_z + 270;
};
//----------------------------------------------------------------------
// Deals with water, slime and lava
//----------------------------------------------------------------------
void() WaterMove =
{
//dprint (ftos(self.waterlevel));
// Fix noclip water gasping bug by giving air to the player
if (self.movetype == MOVETYPE_NOCLIP) {
if (self.air_finished < time + 2) self.air_finished = time + 2;
return;
}
if (self.health < 0) return;
if (self.waterlevel != 3) {
if (self.air_finished < time)
sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
else if (self.air_finished < time + (WATER_AIR-3))
sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
self.air_finished = time + WATER_AIR;
self.dmg = 2;
}
else if (self.air_finished < time) { // drown!
if (self.pain_finished < time) {
self.dmg = self.dmg + 2;
if (self.dmg > 15) self.dmg = 10;
T_Damage (self, world, world, self.dmg, DAMARMOR);
self.pain_finished = time + 1;
}
}
if (!self.waterlevel) {
if (self.flags & FL_INWATER) {
// play leave water sound
sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
self.flags = self.flags - FL_INWATER;
}
return;
}
if (self.watertype == CONTENT_LAVA) { // do damage
if (self.dmgtime < time) {
if (self.radsuit_finished > time) self.dmgtime = time + 1;
else self.dmgtime = time + 0.2;
T_Damage (self, world, world, 10*self.waterlevel, DAMARMOR);
}
}
else if (self.watertype == CONTENT_SLIME) { // do damage
if (self.dmgtime < time && self.radsuit_finished < time) {
self.dmgtime = time + 1;
T_Damage (self, world, world, 4*self.waterlevel, DAMARMOR);
}
}
if ( !(self.flags & FL_INWATER) ) { // player enter water sound
if (self.watertype == CONTENT_LAVA)
sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
if (self.watertype == CONTENT_WATER)
sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
if (self.watertype == CONTENT_SLIME)
sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
self.flags = self.flags + FL_INWATER;
self.dmgtime = 0;
}
if (! (self.flags & FL_WATERJUMP) )
self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;
};
//----------------------------------------------------------------------
void() CheckWaterJump =
{
local vector start, end;
// check for a jump-out-of-water
makevectors (self.angles);
start = self.origin;
start_z = start_z + 8;
v_forward_z = 0;
normalize(v_forward);
end = start + v_forward*24;
traceline (start, end, TRUE, self);
if (trace_fraction < 1) { // solid at waist
start_z = start_z + self.maxs_z - 8;
end = start + v_forward*24;
self.movedir = trace_plane_normal * -50;
traceline (start, end, TRUE, self);
if (trace_fraction == 1) { // open at eye level
self.flags = self.flags | FL_WATERJUMP;
self.velocity_z = 225;
self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
self.teleport_time = time + 2; // safety net
return;
}
}
};
/*======================================================================
CLIENT GAME EDGE FUNCTIONS
======================================================================*/
// This function cannot be move, ref an exact frame in players model
void() set_suicide_frame; // In player.qc
//----------------------------------------------------------------------
void() ClientRespawn =
{
if (coop) {
CopyToBodyQue (self); // make a copy of the dead body for appearances sake
setspawnparms (self); // get the spawn parms as they were at level start
PutClientInServer (); // respawn
}
else if (deathmatch) {
CopyToBodyQue (self); // make a copy of the dead body for appearances sake
SetNewParms (); // set default spawn parms
PutClientInServer (); // respawn
}
// restart the entire server
else localcmd ("restart\n");
};
//----------------------------------------------------------------------
void() ClientDeath =
{
local float forward;
if ((self.flags & FL_ONGROUND)) {
forward = vlen (self.velocity);
forward = forward - 20;
if (forward <= 0)
self.velocity = '0 0 0';
else
self.velocity = forward * normalize(self.velocity);
}
// wait for all buttons released
if (self.deadflag == DEAD_DEAD) {
if (self.button2 || self.button1 || self.button0) return;
self.deadflag = DEAD_RESPAWNABLE;
return;
}
// wait for any button down
if (!self.button2 && !self.button1 && !self.button0) return;
self.button0 = self.button1 = self.button2 = 0;
ClientRespawn();
};
//----------------------------------------------------------------------
// Player entered the suicide command
//----------------------------------------------------------------------
void() ClientKill =
{
bprint (self.netname);
bprint (" suicides\n");
set_suicide_frame ();
self.modelindex = modelindex_player;
self.frags = self.frags - 2; // extra penalty
ClientRespawn ();
};
//----------------------------------------------------------------------
// Called when a player connects to a server
//----------------------------------------------------------------------
void() ClientConnect =
{
bprint (self.netname);
bprint (" entered the game\n");
// a client connecting during an intermission can cause problems
if (intermission_running > 0) GotoNextMap ();
};
//----------------------------------------------------------------------
// Called when a player disconnects from a server
//----------------------------------------------------------------------
void() ClientDisconnect =
{
if (gameover) return;
// if the level end trigger has been activated, just return
// since they aren't *really* leaving
// let everyone else know
bprint (self.netname);
bprint (" left the game with ");
bprint (ftos(self.frags));
bprint (" frags\n");
sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
set_suicide_frame ();
};
//----------------------------------------------------------------------
// Called when a player dies, deals with all death messages
//----------------------------------------------------------------------
void(entity targ, entity attacker) ClientObituary =
{
local float rnum;
local string clientstring, clientstring2;
rnum = random();
if (targ.flags & FL_CLIENT) {
if (attacker.classname == "teledeath") {
bprint (targ.netname);
bprint (" was telefragged by ");
bprint (attacker.owner.netname);
bprint ("\n");
attacker.owner.frags = attacker.owner.frags + 1;
return;
}
if (attacker.classname == "teledeath2") {
bprint ("Satan's power deflects ");
bprint (targ.netname);
bprint ("'s telefrag\n");
targ.frags = targ.frags - 1;
return;
}
if (attacker.flags & FL_CLIENT) {
if (targ == attacker) {
// killed self
attacker.frags = attacker.frags - 1;
bprint (targ.netname);
if (targ.weapon == 64 && targ.waterlevel > 1) {
bprint (" discharges into the water.\n");
return;
}
if (targ.weapon == IT_GRENADE_LAUNCHER)
bprint (" tries to put the pin back in\n");
else
bprint (" becomes bored with life\n");
return;
}
else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) ) {
if (rnum < 0.25) clientstring = " mows down a teammate\n";
else if (rnum < 0.50) clientstring = " checks his glasses\n";
else if (rnum < 0.75) clientstring = " gets a frag for the other team\n";
else clientstring = " loses another friend\n";
bprint (attacker.netname);
bprint (clientstring);
attacker.frags = attacker.frags - 1;
return;
}
else {
attacker.frags = attacker.frags + 1;
rnum = attacker.weapon;
if (rnum == IT_AXE) {
clientstring = " was ax-murdered by ";
clientstring2 = "\n";
}
if (rnum == IT_SHOTGUN) {
clientstring = " chewed on ";
clientstring2 = "'s boomstick\n";
}
if (rnum == IT_SUPER_SHOTGUN) {
clientstring = " ate 2 loads of ";
clientstring2 = "'s buckshot\n";
}
if (rnum == IT_NAILGUN) {
clientstring = " was nailed by ";
clientstring2 = "\n";
}
if (rnum == IT_SUPER_NAILGUN) {
clientstring = " was punctured by ";
clientstring2 = "\n";
}
if (rnum == IT_GRENADE_LAUNCHER) {
clientstring = " eats ";
clientstring2 = "'s pineapple\n";
if (targ.health < -40) {
clientstring = " was gibbed by ";
clientstring2 = "'s grenade\n";
}
}
if (rnum == IT_ROCKET_LAUNCHER) {
clientstring = " rides ";
clientstring2 = "'s rocket\n";
if (targ.health < -40) {
clientstring = " was gibbed by ";
clientstring2 = "'s rocket\n" ;
}
}
if (rnum == IT_LIGHTNING) {
clientstring = " accepts ";
if (attacker.waterlevel > 1) clientstring2 = "'s discharge\n";
else clientstring2 = "'s shaft\n";
}
bprint (targ.netname);
bprint (clientstring);
bprint (attacker.netname);
bprint (clientstring2);
}
return;
}
else {
targ.frags = targ.frags - 1;
bprint (targ.netname);
//-----------------------------------------------------------------
// All the monster death messages are now defined in the monster QC
// It is much easier to remember to add these with creating the
// QC for each new monster than find/edit them here!
//-----------------------------------------------------------------
// Check for boss or monster flags
if (attacker.bossflag > 0 || attacker.flags & FL_MONSTER) {
if (attacker.deathstring != "") bprint (attacker.deathstring);
else bprint(" was killed by Quake!\n");
return;
}
// tricks and traps
if (attacker.solid == SOLID_BSP && attacker != world) bprint (" was squished\n");
else if (attacker.classtype == CT_EXPLO_BOX) bprint (" blew up\n");
else if (attacker.classtype == CT_SPIKESHOOTER) bprint (" did not stick around\n");
else if (attacker.classtype == CT_GRENADESHOOTER) bprint (" was pineappled\n");
else if (attacker.classtype == CT_ROCKETSHOOTER) bprint (" was blasted\n");
else if (attacker.classtype == CT_LIGHTSHOOTER) bprint (" was struck down\n");
else if (attacker.classtype == CT_GASSHOOTER) bprint (" was burnt to a crisp\n");
else if (attacker.classtype == CT_PENDULUM) bprint (" was cleaved in two\n");
else if (attacker.classtype == CT_SAWBLADE) bprint (" was sliced to pieces\n");
else if (attacker.classtype == CT_FIREBALL) bprint (" ate a lavaball\n");
else if (attacker.classtype == CT_TRIGLEVEL) bprint (" tried to leave\n");
// in-water deaths
else if (targ.watertype == -3) {
if (random() < 0.5) bprint (" sleeps with the fishes\n");
else bprint (" sucks it down\n");
}
else if (targ.watertype == -4) {
if (random() < 0.5) bprint (" gulped a load of slime\n");
else bprint (" can't exist on slime alone\n");
}
else if (targ.watertype == -5) {
if (targ.health < -15) bprint (" burst into flames\n");
else if (random() < 0.5) bprint (" turned into hot slag\n");
else bprint (" visits the Volcano God\n");
}
// Debuff deaths
else if (targ.burning) bprint (" was burnt to a crisp\n");
else if (targ.poisoned) bprint (" was poisoned\n");
else if (targ.bleeding) bprint (" bled to death\n");
// fell to their death?
else if (targ.deathtype == "falling") {
targ.deathtype = string_null;
bprint (" fell to his death\n");
}
else if (targ.touchedvoid) {
targ.deathtype = string_null;
bprint (" touched the void\n");
}
else
// hell if I know; he's just dead!!!
bprint (" is dead Jim!\n");
}
}
};