/*====================================================================== 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"); } } };