// Copyright (C) 1999-2000 Id Software, Inc. // // bg_misc.c -- both games misc functions, all completely stateless #include "q_shared.h" #include "bg_public.h" #ifdef QAGAME #include "g_local.h" #endif #ifdef UI_EXPORTS #include "../ui/ui_local.h" #endif #ifndef UI_EXPORTS #ifndef QAGAME #include "../cgame/cg_local.h" #endif #endif char *forceMasteryLevels[NUM_FORCE_MASTERY_LEVELS] = { "Uninitiated", // FORCE_MASTERY_UNINITIATED, "@MASTERY1", //"Initiate", // FORCE_MASTERY_INITIATE, "@MASTERY2", //"Padawan", // FORCE_MASTERY_PADAWAN, "@MASTERY3", //"Disciple", // FORCE_MASTERY_DISCIPLE, "@MASTERY4", //"Jedi", // FORCE_MASTERY_JEDI, "@MASTERY5", //"Jedi Adept", // FORCE_MASTERY_JEDI_ADEPT, "@MASTERY6", //"Jedi Master", // FORCE_MASTERY_JEDI_MASTER, "@MASTERY7", //"Jedi Lord" // FORCE_MASTERY_JEDI_LORD, }; int forceMasteryPoints[NUM_FORCE_MASTERY_LEVELS] = { 0, // FORCE_MASTERY_UNINITIATED, 5, // FORCE_MASTERY_INITIATE, 10, // FORCE_MASTERY_PADAWAN, 15, // FORCE_MASTERY_DISCIPLE, 20, // FORCE_MASTERY_JEDI, 25, // FORCE_MASTERY_JEDI_ADEPT, 30, // FORCE_MASTERY_JEDI_MASTER, 40 // FORCE_MASTERY_JEDI_LORD, }; int forcePowerSorted[NUM_FORCE_POWERS] = { //rww - always use this order when drawing force powers for any reason FP_TELEPATHY, FP_HEAL, FP_ABSORB, FP_PROTECT, FP_TEAM_HEAL, FP_LEVITATION, FP_SPEED, FP_PUSH, FP_PULL, FP_SEE, FP_LIGHTNING, FP_DRAIN, FP_RAGE, FP_GRIP, FP_TEAM_FORCE, FP_SABERATTACK, FP_SABERDEFEND, FP_SABERTHROW }; int forcePowerDarkLight[NUM_FORCE_POWERS] = //0 == neutral { //nothing should be usable at rank 0.. FORCE_LIGHTSIDE,//FP_HEAL,//instant 0,//FP_LEVITATION,//hold/duration 0,//FP_SPEED,//duration 0,//FP_PUSH,//hold/duration 0,//FP_PULL,//hold/duration FORCE_LIGHTSIDE,//FP_TELEPATHY,//instant FORCE_DARKSIDE,//FP_GRIP,//hold/duration FORCE_DARKSIDE,//FP_LIGHTNING,//hold/duration FORCE_DARKSIDE,//FP_RAGE,//duration FORCE_LIGHTSIDE,//FP_PROTECT,//duration FORCE_LIGHTSIDE,//FP_ABSORB,//duration FORCE_LIGHTSIDE,//FP_TEAM_HEAL,//instant FORCE_DARKSIDE,//FP_TEAM_FORCE,//instant FORCE_DARKSIDE,//FP_DRAIN,//hold/duration 0,//FP_SEE,//duration 0,//FP_SABERATTACK, 0,//FP_SABERDEFEND, 0//FP_SABERTHROW, //NUM_FORCE_POWERS }; int WeaponReadyAnim[WP_NUM_WEAPONS] = { TORSO_DROPWEAP1,//WP_NONE, TORSO_WEAPONREADY3,//WP_STUN_BATON, BOTH_STAND2,//WP_SABER, TORSO_WEAPONREADY2,//WP_BRYAR_PISTOL, TORSO_WEAPONREADY3,//WP_BLASTER, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY4,//WP_DISRUPTOR, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY5,//WP_BOWCASTER, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY6,//WP_REPEATER, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY7,//WP_DEMP2, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY8,//WP_FLECHETTE, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY9,//WP_ROCKET_LAUNCHER, TORSO_WEAPONREADY10,//WP_THERMAL, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY11,//WP_TRIP_MINE, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY12,//WP_DET_PACK, //NOT VALID (e.g. should never really be used): BOTH_STAND1,//WP_EMPLACED_GUN, TORSO_WEAPONREADY1//WP_TURRET, }; int WeaponAttackAnim[WP_NUM_WEAPONS] = { BOTH_ATTACK1,//WP_NONE, //(shouldn't happen) BOTH_ATTACK3,//WP_STUN_BATON, BOTH_STAND2,//WP_SABER, //(has its own handling) BOTH_ATTACK2,//WP_BRYAR_PISTOL, BOTH_ATTACK3,//WP_BLASTER, BOTH_ATTACK3,//BOTH_ATTACK4,//WP_DISRUPTOR, BOTH_ATTACK3,//BOTH_ATTACK5,//WP_BOWCASTER, BOTH_ATTACK3,//BOTH_ATTACK6,//WP_REPEATER, BOTH_ATTACK3,//BOTH_ATTACK7,//WP_DEMP2, BOTH_ATTACK3,//BOTH_ATTACK8,//WP_FLECHETTE, BOTH_ATTACK3,//BOTH_ATTACK9,//WP_ROCKET_LAUNCHER, BOTH_ATTACK10,//WP_THERMAL, BOTH_ATTACK3,//BOTH_ATTACK11,//WP_TRIP_MINE, BOTH_ATTACK3,//BOTH_ATTACK12,//WP_DET_PACK, //NOT VALID (e.g. should never really be used): BOTH_STAND1,//WP_EMPLACED_GUN, BOTH_ATTACK1//WP_TURRET, }; /*QUAKED item_***** ( 0 0 0 ) (-16 -16 -16) (16 16 16) suspended DO NOT USE THIS CLASS, IT JUST HOLDS GENERAL INFORMATION. The suspended flag will allow items to hang in the air, otherwise they are dropped to the next surface. If an item is the target of another entity, it will not spawn in until fired. An item fires all of its targets when it is picked up. If the toucher can't carry it, the targets won't be fired. "notfree" if set to 1, don't spawn in free for all games "notteam" if set to 1, don't spawn in team games "notsingle" if set to 1, don't spawn in single player games "wait" override the default wait before respawning. -1 = never respawn automatically, which can be used with targeted spawning. "random" random number of plus or minus seconds varied from the respawn time "count" override quantity or duration on most items. */ gitem_t bg_itemlist[] = { { NULL, // classname NULL, // pickup_sound { NULL, // world_model[0] NULL, // world_model[1] 0, 0} , // world_model[2],[3] NULL, // view_model /* icon */ NULL, // icon /* pickup */ NULL, // pickup_name 0, // quantity 0, // giType (IT_*) 0, // giTag /* precache */ "", // precaches /* sounds */ "" // sounds }, // leave index 0 alone // // Pickups // /*QUAKED item_shield_sm_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Instant shield pickup, restores 25 */ { "item_shield_sm_instant", "sound/player/pickupshield.wav", { "models/map_objects/mp/psd_sm.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Shield Small", 25, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_shield_lrg_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Instant shield pickup, restores 100 */ { "item_shield_lrg_instant", "sound/player/pickupshield.wav", { "models/map_objects/mp/psd.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Shield Large", 100, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_medpak_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Instant medpack pickup, heals 25 */ { "item_medpak_instant", "sound/player/pickuphealth.wav", { "models/map_objects/mp/medpac.md3", 0, 0, 0 }, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Medpack", 25, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, // // ITEMS // /*QUAKED item_seeker (.3 .3 1) (-8 -8 -0) (8 8 16) suspended 30 seconds of seeker drone */ { "item_seeker", "sound/weapons/w_pkup.wav", { "models/items/remote.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/i_icon_seeker", /* pickup */ "Seeker Drone", 120, IT_HOLDABLE, HI_SEEKER, /* precache */ "", /* sounds */ "" }, /*QUAKED item_shield (.3 .3 1) (-8 -8 -0) (8 8 16) suspended Portable shield */ { "item_shield", "sound/weapons/w_pkup.wav", { "models/map_objects/mp/shield.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/i_icon_shieldwall", /* pickup */ "Forcefield", 120, IT_HOLDABLE, HI_SHIELD, /* precache */ "", /* sounds */ "sound/weapons/detpack/stick.wav sound/movers/doors/forcefield_on.wav sound/movers/doors/forcefield_off.wav sound/effects/bumpfield.wav", }, /*QUAKED item_medpac (.3 .3 1) (-8 -8 -0) (8 8 16) suspended Bacta canister pickup, heals 25 on use */ { "item_medpac", "sound/weapons/w_pkup.wav", { "models/map_objects/mp/bacta.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/i_icon_bacta", /* pickup */ "Bacta Canister", 25, IT_HOLDABLE, HI_MEDPAC, /* precache */ "", /* sounds */ "" }, /*QUAKED item_datapad (.3 .3 1) (-8 -8 -0) (8 8 16) suspended Do not place this. */ { "item_datapad", "sound/weapons/w_pkup.wav", { "models/items/datapad.md3", 0, 0, 0} , /* view */ NULL, /* icon */ NULL, /* pickup */ "Datapad", 1, IT_HOLDABLE, HI_DATAPAD, /* precache */ "", /* sounds */ "" }, /*QUAKED item_binoculars (.3 .3 1) (-8 -8 -0) (8 8 16) suspended These will be standard equipment on the player - DO NOT PLACE */ { "item_binoculars", "sound/weapons/w_pkup.wav", { "models/items/binoculars.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/i_icon_zoom", /* pickup */ "Binoculars", 60, IT_HOLDABLE, HI_BINOCULARS, /* precache */ "", /* sounds */ "" }, /*QUAKED item_sentry_gun (.3 .3 1) (-8 -8 -0) (8 8 16) suspended Sentry gun inventory pickup. */ { "item_sentry_gun", "sound/weapons/w_pkup.wav", { "models/items/psgun.glm", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/i_icon_sentrygun", /* pickup */ "Sentry Gun", 120, IT_HOLDABLE, HI_SENTRY_GUN, /* precache */ "", /* sounds */ "" }, /*QUAKED item_force_enlighten_light (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Adds one rank to all Force powers temporarily. Only light jedi can use. */ { "item_force_enlighten_light", "sound/player/enlightenment.wav", { "models/map_objects/mp/jedi_enlightenment.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/mpi_jlight", /* pickup */ "Light Force Enlightenment", 25, IT_POWERUP, PW_FORCE_ENLIGHTENED_LIGHT, /* precache */ "", /* sounds */ "" }, /*QUAKED item_force_enlighten_dark (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Adds one rank to all Force powers temporarily. Only dark jedi can use. */ { "item_force_enlighten_dark", "sound/player/enlightenment.wav", { "models/map_objects/mp/dk_enlightenment.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/mpi_dklight", /* pickup */ "Dark Force Enlightenment", 25, IT_POWERUP, PW_FORCE_ENLIGHTENED_DARK, /* precache */ "", /* sounds */ "" }, /*QUAKED item_force_boon (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Unlimited Force Pool for a short time. */ { "item_force_boon", "sound/player/boon.wav", { "models/map_objects/mp/force_boon.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/mpi_fboon", /* pickup */ "Force Boon", 25, IT_POWERUP, PW_FORCE_BOON, /* precache */ "", /* sounds */ "" }, /*QUAKED item_ysalimari (.3 .3 1) (-16 -16 -16) (16 16 16) suspended A small lizard carried on the player, which prevents the possessor from using any Force power. However, he is unaffected by any Force power. */ { "item_ysalimari", "sound/player/ysalimari.wav", { "models/map_objects/mp/ysalimari.md3", 0, 0, 0} , /* view */ NULL, /* icon */ "gfx/hud/mpi_ysamari", /* pickup */ "Ysalimari", 25, IT_POWERUP, PW_YSALIMARI, /* precache */ "", /* sounds */ "" }, // // WEAPONS // /*QUAKED weapon_stun_baton (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Don't place this */ { "weapon_stun_baton", "sound/weapons/w_pkup.wav", { "models/weapons2/stun_baton/baton_w.glm", 0, 0, 0}, /* view */ "models/weapons2/stun_baton/baton.md3", /* icon */ "gfx/hud/w_icon_stunbaton", /* pickup */ "Stun Baton", 50, IT_WEAPON, WP_STUN_BATON, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_saber (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Don't place this */ { "weapon_saber", "sound/weapons/w_pkup.wav", { "models/weapons2/saber/saber_w.glm", 0, 0, 0}, /* view */ "models/weapons2/saber/saber_w.md3", /* icon */ "gfx/hud/w_icon_lightsaber", /* pickup */ "Lightsaber", 50, IT_WEAPON, WP_SABER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_bryar_pistol (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Don't place this */ { "weapon_bryar_pistol", "sound/weapons/w_pkup.wav", { "models/weapons2/briar_pistol/briar_pistol_w.glm", 0, 0, 0}, /* view */ "models/weapons2/briar_pistol/briar_pistol.md3", /* icon */ "icons/w_icon_rifle", /* pickup */ "Bryar Pistol", 50, IT_WEAPON, WP_BRYAR_PISTOL, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_blaster", "sound/weapons/w_pkup.wav", { "models/weapons2/blaster_r/blaster_w.glm", 0, 0, 0}, /* view */ "models/weapons2/blaster_r/blaster.md3", /* icon */ "icons/w_icon_blaster", /* pickup */ "E11 Blaster Rifle", 50, IT_WEAPON, WP_BLASTER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_disruptor (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_disruptor", "sound/weapons/w_pkup.wav", { "models/weapons2/disruptor/disruptor_w.glm", 0, 0, 0}, /* view */ "models/weapons2/disruptor/disruptor.md3", /* icon */ "icons/w_icon_disruptor", /* pickup */ "Tenloss Disruptor Rifle", 50, IT_WEAPON, WP_DISRUPTOR, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_bowcaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_bowcaster", "sound/weapons/w_pkup.wav", { "models/weapons2/bowcaster/bowcaster_w.glm", 0, 0, 0}, /* view */ "models/weapons2/bowcaster/bowcaster.md3", /* icon */ "icons/w_icon_bowcaster", /* pickup */ "Wookiee Bowcaster", 50, IT_WEAPON, WP_BOWCASTER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_repeater (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_repeater", "sound/weapons/w_pkup.wav", { "models/weapons2/heavy_repeater/heavy_repeater_w.glm", 0, 0, 0}, /* view */ "models/weapons2/heavy_repeater/heavy_repeater.md3", /* icon */ "icons/w_icon_repeater", /* pickup */ "Imperial Heavy Repeater", 50, IT_WEAPON, WP_REPEATER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_demp2 (.3 .3 1) (-16 -16 -16) (16 16 16) suspended NOTENOTE This weapon is not yet complete. Don't place it. */ { "weapon_demp2", "sound/weapons/w_pkup.wav", { "models/weapons2/demp2/demp2_w.glm", 0, 0, 0}, /* view */ "models/weapons2/demp2/demp2.md3", /* icon */ "icons/w_icon_demp2", /* pickup */ "DEMP2", 50, IT_WEAPON, WP_DEMP2, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_flechette (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_flechette", "sound/weapons/w_pkup.wav", { "models/weapons2/golan_arms/golan_arms_w.glm", 0, 0, 0}, /* view */ "models/weapons2/golan_arms/golan_arms.md3", /* icon */ "icons/w_icon_flechette", /* pickup */ "Golan Arms Flechette", 50, IT_WEAPON, WP_FLECHETTE, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_rocket_launcher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_rocket_launcher", "sound/weapons/w_pkup.wav", { "models/weapons2/merr_sonn/merr_sonn_w.glm", 0, 0, 0}, /* view */ "models/weapons2/merr_sonn/merr_sonn.md3", /* icon */ "icons/w_icon_launcher", /* pickup */ "Merr-Sonn Missile System", 50, IT_WEAPON, WP_ROCKET_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_thermal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_thermal", "sound/weapons/w_pkup.wav", { "models/weapons2/thermal/thermal_w.glm", 0, 0, 0}, /* view */ "models/weapons2/thermal/thermal.md3", /* icon */ "icons/w_icon_thermal", /* pickup */ "Thermal Detonator", 1, IT_WEAPON, WP_THERMAL, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_trip_mine (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_trip_mine", "sound/weapons/w_pkup.wav", { "models/weapons2/laser_trap/laser_trap_w.glm", 0, 0, 0}, /* view */ "models/weapons2/laser_trap/laser_trap.md3", /* icon */ "icons/w_icon_trip_mine", /* pickup */ "Trip Mine", 1, IT_WEAPON, WP_TRIP_MINE, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_det_pack (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_det_pack", "sound/weapons/w_pkup.wav", { "models/weapons2/detpack/det_pack_w.glm", 0, 0, 0}, /* view */ "models/weapons2/detpack/det_pack.md3", /* icon */ "icons/w_icon_det_pack", /* pickup */ "Det Pack", 1, IT_WEAPON, WP_DET_PACK, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_emplaced (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_emplaced", "sound/weapons/w_pkup.wav", { "models/weapons2/blaster_r/blaster_w.glm", 0, 0, 0}, /* view */ "models/weapons2/blaster_r/blaster.md3", /* icon */ "icons/w_icon_blaster", /* pickup */ "Emplaced Gun", 50, IT_WEAPON, WP_EMPLACED_GUN, /* precache */ "", /* sounds */ "" }, //NOTE: This is to keep things from messing up because the turret weapon type isn't real { "weapon_turretwp", "sound/weapons/w_pkup.wav", { "models/weapons2/blaster_r/blaster_w.glm", 0, 0, 0}, /* view */ "models/weapons2/blaster_r/blaster.md3", /* icon */ "icons/w_icon_blaster", /* pickup */ "Turret Gun", 50, IT_WEAPON, WP_TURRET, /* precache */ "", /* sounds */ "" }, // // AMMO ITEMS // /*QUAKED ammo_force (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Don't place this */ { "ammo_force", "sound/player/pickupenergy.wav", { "models/items/energy_cell.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Force??", 100, IT_AMMO, AMMO_FORCE, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Ammo for the Bryar and Blaster pistols. */ { "ammo_blaster", "sound/player/pickupenergy.wav", { "models/items/energy_cell.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Blaster Pack", 100, IT_AMMO, AMMO_BLASTER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_powercell (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Ammo for Tenloss Disruptor, Wookie Bowcaster, and the Destructive Electro Magnetic Pulse (demp2 ) guns */ { "ammo_powercell", "sound/player/pickupenergy.wav", { "models/items/power_cell.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Power Cell", 100, IT_AMMO, AMMO_POWERCELL, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_metallic_bolts (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Ammo for Imperial Heavy Repeater and the Golan Arms Flechette */ { "ammo_metallic_bolts", "sound/player/pickupenergy.wav", { "models/items/metallic_bolts.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Metallic Bolts", 100, IT_AMMO, AMMO_METAL_BOLTS, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended Ammo for Merr-Sonn portable missile launcher */ { "ammo_rockets", "sound/player/pickupenergy.wav", { "models/items/rockets.md3", 0, 0, 0}, /* view */ NULL, /* icon */ "icons/w_icon_blaster", /* pickup */ "Rockets", 100, IT_AMMO, AMMO_ROCKETS, /* precache */ "", /* sounds */ "" }, // // POWERUP ITEMS // /*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_redflag", NULL, { "models/flags/r_flag.md3", "models/flags/r_flag_ysal.md3", 0, 0 }, /* view */ NULL, /* icon */ "gfx/hud/mpi_rflag", /* pickup */ "Red Flag", 0, IT_TEAM, PW_REDFLAG, /* precache */ "", /* sounds */ "" }, /*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_blueflag", NULL, { "models/flags/b_flag.md3", "models/flags/b_flag_ysal.md3", 0, 0 }, /* view */ NULL, /* icon */ "gfx/hud/mpi_bflag", /* pickup */ "Blue Flag", 0, IT_TEAM, PW_BLUEFLAG, /* precache */ "", /* sounds */ "" }, // // PERSISTANT POWERUP ITEMS // /*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16) Only in One Flag CTF games */ { "team_CTF_neutralflag", NULL, { "models/flags/n_flag.md3", 0, 0, 0 }, /* view */ NULL, /* icon */ "icons/iconf_neutral1", /* pickup */ "Neutral Flag", 0, IT_TEAM, PW_NEUTRALFLAG, /* precache */ "", /* sounds */ "" }, { "item_redcube", "sound/player/pickupenergy.wav", { "models/powerups/orb/r_orb.md3", 0, 0, 0 }, /* view */ NULL, /* icon */ "icons/iconh_rorb", /* pickup */ "Red Cube", 0, IT_TEAM, 0, /* precache */ "", /* sounds */ "" }, { "item_bluecube", "sound/player/pickupenergy.wav", { "models/powerups/orb/b_orb.md3", 0, 0, 0 }, /* view */ NULL, /* icon */ "icons/iconh_borb", /* pickup */ "Blue Cube", 0, IT_TEAM, 0, /* precache */ "", /* sounds */ "" }, // end of list marker {NULL} }; int bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1; qboolean BG_HasYsalimari(int gametype, playerState_t *ps) { if (gametype == GT_CTY && (ps->powerups[PW_REDFLAG] || ps->powerups[PW_BLUEFLAG])) { return qtrue; } if (ps->powerups[PW_YSALIMARI]) { return qtrue; } return qfalse; } qboolean BG_CanUseFPNow(int gametype, playerState_t *ps, int time, forcePowers_t power) { if (BG_HasYsalimari(gametype, ps)) { return qfalse; } if (ps->duelInProgress) { if (power != FP_SABERATTACK && power != FP_SABERDEFEND && power != FP_SABERTHROW && power != FP_LEVITATION) { if (!ps->saberLockFrame || power != FP_PUSH) { return qfalse; } } } if (ps->saberLockFrame || ps->saberLockTime > time) { if (power != FP_PUSH) { return qfalse; } } return qtrue; } /* ============== BG_FindItemForPowerup ============== */ gitem_t *BG_FindItemForPowerup( powerup_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( (bg_itemlist[i].giType == IT_POWERUP || bg_itemlist[i].giType == IT_TEAM) && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } return NULL; } /* ============== BG_FindItemForHoldable ============== */ gitem_t *BG_FindItemForHoldable( holdable_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( bg_itemlist[i].giType == IT_HOLDABLE && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } Com_Error( ERR_DROP, "HoldableItem not found" ); return NULL; } /* =============== BG_FindItemForWeapon =============== */ gitem_t *BG_FindItemForWeapon( weapon_t weapon ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++) { if ( it->giType == IT_WEAPON && it->giTag == weapon ) { return it; } } Com_Error( ERR_DROP, "Couldn't find item for weapon %i", weapon); return NULL; } /* =============== BG_FindItem =============== */ gitem_t *BG_FindItem( const char *pickupName ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++ ) { if ( !Q_stricmp( it->pickup_name, pickupName ) ) return it; } return NULL; } /* ============ BG_PlayerTouchesItem Items can be picked up without actually touching their physical bounds to make grabbing them easier ============ */ qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) { vec3_t origin; BG_EvaluateTrajectory( &item->pos, atTime, origin ); // we are ignoring ducked differences here if ( ps->origin[0] - origin[0] > 44 || ps->origin[0] - origin[0] < -50 || ps->origin[1] - origin[1] > 36 || ps->origin[1] - origin[1] < -36 || ps->origin[2] - origin[2] > 36 || ps->origin[2] - origin[2] < -36 ) { return qfalse; } return qtrue; } int BG_ProperForceIndex(int power) { int i = 0; while (i < NUM_FORCE_POWERS) { if (forcePowerSorted[i] == power) { return i; } i++; } return -1; } void BG_CycleForce(playerState_t *ps, int direction) { int i = ps->fd.forcePowerSelected; int x = i; int presel = i; int foundnext = -1; if (!ps->fd.forcePowersKnown & (1 << x) || x >= NUM_FORCE_POWERS || x == -1) { //apparently we have no valid force powers return; } x = BG_ProperForceIndex(x); presel = x; if (direction == 1) { x++; } else { x--; } if (x >= NUM_FORCE_POWERS) { x = 0; } if (x < 0) { x = NUM_FORCE_POWERS-1; } i = forcePowerSorted[x]; while (x != presel) { if (ps->fd.forcePowersKnown & (1 << i) && i != ps->fd.forcePowerSelected) { if (i != FP_LEVITATION && i != FP_SABERATTACK && i != FP_SABERDEFEND && i != FP_SABERTHROW) { foundnext = i; break; } } if (direction == 1) { x++; } else { x--; } if (x >= NUM_FORCE_POWERS) { x = 0; } if (x < 0) { x = NUM_FORCE_POWERS-1; } i = forcePowerSorted[x]; } if (foundnext != -1) { ps->fd.forcePowerSelected = foundnext; } } int BG_GetItemIndexByTag(int tag, int type) { int i = 0; while (i < bg_numItems) { if (bg_itemlist[i].giTag == tag && bg_itemlist[i].giType == type) { return i; } i++; } return 0; } void BG_CycleInven(playerState_t *ps, int direction) { int i; int original; i = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag; original = i; if (direction == 1) { i++; } else { i--; } while (i != original) { //go in a full loop until hitting something, if hit nothing then select nothing if (ps->stats[STAT_HOLDABLE_ITEMS] & (1 << i)) { ps->stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(i, IT_HOLDABLE); break; } if (direction == 1) { i++; } else { i--; } if (i < 0) { i = HI_NUM_HOLDABLE; } else if (i >= HI_NUM_HOLDABLE) { i = 0; } } } /* ================ BG_CanItemBeGrabbed Returns false if the item should not be picked up. This needs to be the same for client side prediction and server use. ================ */ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ) { gitem_t *item; if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) { Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" ); } item = &bg_itemlist[ent->modelindex]; if (ps && ps->isJediMaster && item && (item->giType == IT_WEAPON || item->giType == IT_AMMO)) { return qfalse; } if (ps && ps->duelInProgress) { //no picking stuff up while in a duel, no matter what the type is return qfalse; } switch( item->giType ) { case IT_WEAPON: if (ent->generic1 == ps->clientNum && ent->powerups) { return qfalse; } return qtrue; // weapons are always picked up case IT_AMMO: if ( ps->ammo[item->giTag] >= ammoData[item->giTag].max) { return qfalse; // can't hold any more } return qtrue; case IT_ARMOR: if ( ps->stats[STAT_ARMOR] >= ps->stats[STAT_MAX_HEALTH]/* * 2*/ ) { return qfalse; } return qtrue; case IT_HEALTH: // small and mega healths will go over the max, otherwise // don't pick up if already at max if ( item->quantity == 5 || item->quantity == 100 ) { if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; } return qtrue; } if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] ) { return qfalse; } return qtrue; case IT_POWERUP: return qtrue; // powerups are always picked up case IT_TEAM: // team items, such as flags if( gametype == GT_CTF || gametype == GT_CTY ) { // ent->modelindex2 is non-zero on items if they are dropped // we need to know this because we can pick up our dropped flag (and return it) // but we can't pick up our flag at base if (ps->persistant[PERS_TEAM] == TEAM_RED) { if (item->giTag == PW_BLUEFLAG || (item->giTag == PW_REDFLAG && ent->modelindex2) || (item->giTag == PW_REDFLAG && ps->powerups[PW_BLUEFLAG]) ) return qtrue; } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG || (item->giTag == PW_BLUEFLAG && ent->modelindex2) || (item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) ) return qtrue; } } return qfalse; case IT_HOLDABLE: if ( ps->stats[STAT_HOLDABLE_ITEMS] & (1 << item->giTag)) { return qfalse; } return qtrue; case IT_BAD: Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" ); default: #ifndef Q3_VM #ifndef NDEBUG // bk0001204 Com_Printf("BG_CanItemBeGrabbed: unknown enum %d\n", item->giType ); #endif #endif break; } return qfalse; } //====================================================================== /* ================ BG_EvaluateTrajectory ================ */ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorCopy( tr->trBase, result ); break; case TR_LINEAR: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = sin( deltaTime * M_PI * 2 ); VectorMA( tr->trBase, phase, tr->trDelta, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { atTime = tr->trTime + tr->trDuration; } deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds if ( deltaTime < 0 ) { deltaTime = 0; } VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime ); break; } } /* ================ BG_EvaluateTrajectoryDelta For determining velocity at a given time ================ */ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorClear( result ); break; case TR_LINEAR: VectorCopy( tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = cos( deltaTime * M_PI * 2 ); // derivative of sin = cos phase *= 0.5; VectorScale( tr->trDelta, phase, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { VectorClear( result ); return; } VectorCopy( tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorCopy( tr->trDelta, result ); result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime ); break; } } char *eventnames[] = { "EV_NONE", "EV_CLIENTJOIN", "EV_FOOTSTEP", "EV_FOOTSTEP_METAL", "EV_FOOTSPLASH", "EV_FOOTWADE", "EV_SWIM", "EV_STEP_4", "EV_STEP_8", "EV_STEP_12", "EV_STEP_16", "EV_FALL", "EV_JUMP_PAD", // boing sound at origin", jump sound on player "EV_PRIVATE_DUEL", "EV_JUMP", "EV_ROLL", "EV_WATER_TOUCH", // foot touches "EV_WATER_LEAVE", // foot leaves "EV_WATER_UNDER", // head touches "EV_WATER_CLEAR", // head leaves "EV_ITEM_PICKUP", // normal item pickups are predictable "EV_GLOBAL_ITEM_PICKUP", // powerup / team sounds are broadcast to everyone "EV_NOAMMO", "EV_CHANGE_WEAPON", "EV_FIRE_WEAPON", "EV_ALT_FIRE", "EV_SABER_ATTACK", "EV_SABER_HIT", "EV_SABER_BLOCK", "EV_SABER_UNHOLSTER", "EV_BECOME_JEDIMASTER", "EV_DISRUPTOR_MAIN_SHOT", "EV_DISRUPTOR_SNIPER_SHOT", "EV_DISRUPTOR_SNIPER_MISS", "EV_DISRUPTOR_HIT", "EV_DISRUPTOR_ZOOMSOUND", "EV_USE", // +Use key "EV_USE_ITEM0", "EV_USE_ITEM1", "EV_USE_ITEM2", "EV_USE_ITEM3", "EV_USE_ITEM4", "EV_USE_ITEM5", "EV_USE_ITEM6", "EV_USE_ITEM7", "EV_USE_ITEM8", "EV_USE_ITEM9", "EV_USE_ITEM10", "EV_USE_ITEM11", "EV_USE_ITEM12", "EV_USE_ITEM13", "EV_USE_ITEM14", "EV_USE_ITEM15", "EV_ITEMUSEFAIL", "EV_ITEM_RESPAWN", "EV_ITEM_POP", "EV_PLAYER_TELEPORT_IN", "EV_PLAYER_TELEPORT_OUT", "EV_GRENADE_BOUNCE", // eventParm will be the soundindex "EV_PLAY_EFFECT", "EV_MUTE_SOUND", "EV_GENERAL_SOUND", "EV_GLOBAL_SOUND", // no attenuation "EV_GLOBAL_TEAM_SOUND", "EV_ENTITY_SOUND", "EV_PLAY_ROFF", "EV_GLASS_SHATTER", "EV_DEBRIS", "EV_MISSILE_HIT", "EV_MISSILE_MISS", "EV_MISSILE_MISS_METAL", "EV_BULLET", // otherEntity is the shooter "EV_PAIN", "EV_DEATH1", "EV_DEATH2", "EV_DEATH3", "EV_OBITUARY", "EV_POWERUP_QUAD", "EV_POWERUP_BATTLESUIT", //"EV_POWERUP_REGEN", "EV_FORCE_DRAINED", "EV_GIB_PLAYER", // gib a previously living player "EV_SCOREPLUM", // score plum "EV_CTFMESSAGE", "EV_SAGA_ROUNDOVER", "EV_SAGA_OBJECTIVECOMPLETE", "EV_DESTROY_GHOUL2_INSTANCE", "EV_DESTROY_WEAPON_MODEL", "EV_GIVE_NEW_RANK", "EV_SET_FREE_SABER", "EV_WEAPON_CHARGE", "EV_WEAPON_CHARGE_ALT", "EV_SHIELD_HIT", "EV_DEBUG_LINE", "EV_TESTLINE", "EV_STOPLOOPINGSOUND", "EV_STARTLOOPINGSOUND", "EV_TAUNT" }; /* =============== BG_AddPredictableEventToPlayerstate Handles the sequence numbers =============== */ void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) { #ifdef _DEBUG { char buf[256]; trap_Cvar_VariableStringBuffer("showevents", buf, sizeof(buf)); if ( atof(buf) != 0 ) { #ifdef QAGAME Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #else Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #endif } } #endif ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent; ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm; ps->eventSequence++; } /* ======================== BG_TouchJumpPad ======================== */ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { vec3_t angles; float p; int effectNum; // spectators don't use jump pads if ( ps->pm_type != PM_NORMAL && ps->pm_type != PM_FLOAT ) { return; } // if we didn't hit this same jumppad the previous frame // then don't play the event sound again if we are in a fat trigger if ( ps->jumppad_ent != jumppad->number ) { vectoangles( jumppad->origin2, angles); p = fabs( AngleNormalize180( angles[PITCH] ) ); if( p < 45 ) { effectNum = 0; } else { effectNum = 1; } BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); } // remember hitting this jumppad this frame ps->jumppad_ent = jumppad->number; ps->jumppad_frame = ps->pmove_framecount; // give the player the velocity from the jumppad VectorCopy( jumppad->origin2, ps->velocity ); } /* ======================== BG_PlayerStateToEntityState This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_INTERPOLATE; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction VectorCopy( ps->velocity, s->pos.trDelta ); s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->trickedentindex = ps->fd.forceMindtrickTargetIndex; s->trickedentindex2 = ps->fd.forceMindtrickTargetIndex2; s->trickedentindex3 = ps->fd.forceMindtrickTargetIndex3; s->trickedentindex4 = ps->fd.forceMindtrickTargetIndex4; s->forceFrame = ps->saberLockFrame; s->emplacedOwner = ps->electrifyTime; s->speed = ps->speed; s->genericenemyindex = ps->genericEnemyIndex; s->activeForcePass = ps->activeForcePass; s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; s->saberInFlight = ps->saberInFlight; s->saberEntityNum = ps->saberEntityNum; s->saberMove = ps->saberMove; s->forcePowersActive = ps->fd.forcePowersActive; if (ps->duelInProgress) { s->bolt1 = 1; } else { s->bolt1 = 0; } if (ps->dualBlade) { s->bolt2 = 1; } else { s->bolt2 = 0; } s->otherEntityNum2 = ps->emplacedIndex; s->shouldtarget = ps->saberHolstered; //reuse bool in entitystate for players differently s->teamowner = ps->usingATST; if (ps->genericEnemyIndex != -1) { s->eFlags |= EF_SEEKERDRONE; } if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; //NOT INCLUDED IN ENTITYSTATETOPLAYERSTATE: s->modelindex2 = ps->weaponstate; s->constantLight = ps->weaponChargeTime; VectorCopy(ps->lastHitLoc, s->origin2); s->isJediMaster = ps->isJediMaster; } /* ======================== BG_PlayerStateToEntityStateExtraPolate This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_LINEAR_STOP; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction and linear prediction VectorCopy( ps->velocity, s->pos.trDelta ); // set the time for linear prediction s->pos.trTime = time; // set maximum extra polation time s->pos.trDuration = 50; // 1000 / sv_fps (default = 20) s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->trickedentindex = ps->fd.forceMindtrickTargetIndex; s->trickedentindex2 = ps->fd.forceMindtrickTargetIndex2; s->trickedentindex3 = ps->fd.forceMindtrickTargetIndex3; s->trickedentindex4 = ps->fd.forceMindtrickTargetIndex4; s->forceFrame = ps->saberLockFrame; s->emplacedOwner = ps->electrifyTime; s->speed = ps->speed; s->genericenemyindex = ps->genericEnemyIndex; s->activeForcePass = ps->activeForcePass; s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; s->saberInFlight = ps->saberInFlight; s->saberEntityNum = ps->saberEntityNum; s->saberMove = ps->saberMove; s->forcePowersActive = ps->fd.forcePowersActive; if (ps->duelInProgress) { s->bolt1 = 1; } else { s->bolt1 = 0; } if (ps->dualBlade) { s->bolt2 = 1; } else { s->bolt2 = 0; } s->otherEntityNum2 = ps->emplacedIndex; s->shouldtarget = ps->saberHolstered; //reuse bool in entitystate for players differently s->teamowner = ps->usingATST; if (ps->genericEnemyIndex != -1) { s->eFlags |= EF_SEEKERDRONE; } if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; //NOT INCLUDED IN ENTITYSTATETOPLAYERSTATE: s->modelindex2 = ps->weaponstate; s->constantLight = ps->weaponChargeTime; VectorCopy(ps->lastHitLoc, s->origin2); s->isJediMaster = ps->isJediMaster; } /* ============================================================================= PLAYER ANGLES ============================================================================= */ /* ================== BG_SwingAngles ================== */ static void BG_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qboolean *swinging, int frameTime ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = frameTime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = frameTime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } } /* ================= CG_AddPainTwitch ================= */ #define PAIN_TWITCH_TIME 200 static void BG_AddPainTwitch( int painTime, int painDirection, int currentTime, vec3_t torsoAngles ) { int t; float f; t = currentTime - painTime; if ( t >= PAIN_TWITCH_TIME ) { return; } f = 1.0 - (float)t / PAIN_TWITCH_TIME; if ( painDirection ) { torsoAngles[ROLL] += 20 * f; } else { torsoAngles[ROLL] -= 20 * f; } } void BG_G2PlayerAngles( vec3_t startAngles, vec3_t legs[3], vec3_t legsAngles, int painTime, int painDirection, int currentTime, qboolean *torso_yawing, float *torso_yawAngle, qboolean *torso_pitching, float *torso_pitchAngle, qboolean *legs_yawing, float *legs_yawAngle, int frameTime, vec3_t velocity, int legsAnim, int torsoAnim, qboolean dead, float movementDir, void *ghoul2, qhandle_t *modelList, int weapon){ vec3_t torsoAngles, headAngles; float dest; static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 }; float speed; int dir; VectorCopy( startAngles, headAngles ); headAngles[YAW] = AngleMod( headAngles[YAW] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( legsAnim & ~ANIM_TOGGLEBIT ) != WeaponReadyAnim[weapon] || ( torsoAnim & ~ANIM_TOGGLEBIT ) != WeaponReadyAnim[weapon] ) { // if not standing still, always point all in the same direction *torso_yawing = qtrue; // always center *torso_pitching = qtrue; // always center *legs_yawing = qtrue; // always center } // adjust legs for movement dir if (dead ) { // don't let dead bodies twitch dir = 0; } else { dir = movementDir; // if ( dir < 0 || dir > 7 ) { // CG_Error( "Bad player movement angle" ); // } } legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ]; torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ]; // torso BG_SwingAngles( torsoAngles[YAW], 25, 90, /*cg_swingSpeed.value*/ 0.3, torso_yawAngle, torso_yawing, frameTime ); BG_SwingAngles( legsAngles[YAW], 40, 90, /*cg_swingSpeed.value*/ 0.3, legs_yawAngle, legs_yawing, frameTime ); torsoAngles[YAW] = *torso_yawAngle; legsAngles[YAW] = *legs_yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[PITCH] > 180 ) { dest = (-360 + headAngles[PITCH]) * 0.75; } else { dest = headAngles[PITCH] * 0.75; } BG_SwingAngles( dest, 15, 30, 0.1, torso_pitchAngle, torso_pitching, frameTime ); torsoAngles[PITCH] = *torso_pitchAngle; // --------- roll ------------- // lean towards the direction of travel speed = VectorNormalize( velocity ); if ( speed ) { vec3_t axis[3]; float side; speed *= 0.05; AnglesToAxis( legsAngles, axis ); side = speed * DotProduct( velocity, axis[1] ); legsAngles[ROLL] -= side; side = speed * DotProduct( velocity, axis[0] ); legsAngles[PITCH] += side; } // pain twitch BG_AddPainTwitch( painTime, painDirection, currentTime, torsoAngles ); // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesToAxis( legsAngles, legs ); // we assume that model 0 is the player model. //g2r trap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", torsoAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, modelList, 0, currentTime); //g2r trap_G2API_SetBoneAngles(ghoul2, 0, "cranium", headAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, modelList,0, currentTime); } #define MAX_POOL_SIZE 2048000 //1024000 static char bg_pool[MAX_POOL_SIZE]; static int bg_poolSize = 0; static int bg_poolTail = MAX_POOL_SIZE; void *BG_Alloc ( int size ) { bg_poolSize = ((bg_poolSize + 0x00000003) & 0xfffffffc); if (bg_poolSize + size > bg_poolTail) { Com_Error( ERR_DROP, "BG_Alloc: buffer exceeded tail (%d > %d)", bg_poolSize + size, bg_poolTail); return 0; } bg_poolSize += size; return &bg_pool[bg_poolSize-size]; } void *BG_AllocUnaligned ( int size ) { if (bg_poolSize + size > bg_poolTail) { Com_Error( ERR_DROP, "BG_AllocUnaligned: buffer exceeded tail (%d > %d)", bg_poolSize + size, bg_poolTail); return 0; } bg_poolSize += size; return &bg_pool[bg_poolSize-size]; } void *BG_TempAlloc( int size ) { size = ((size + 0x00000003) & 0xfffffffc); if (bg_poolTail - size < bg_poolSize) { Com_Error( ERR_DROP, "BG_TempAlloc: buffer exceeded head (%d > %d)", bg_poolTail - size, bg_poolSize); return 0; } bg_poolTail -= size; return &bg_pool[bg_poolTail]; } void BG_TempFree( int size ) { size = ((size + 0x00000003) & 0xfffffffc); if (bg_poolTail+size > MAX_POOL_SIZE) { Com_Error( ERR_DROP, "BG_TempFree: tail greater than size (%d > %d)", bg_poolTail+size, MAX_POOL_SIZE ); } bg_poolTail += size; } char *BG_StringAlloc ( const char *source ) { char *dest; dest = BG_Alloc ( strlen ( source ) + 1 ); strcpy ( dest, source ); return dest; } qboolean BG_OutOfMemory ( void ) { return bg_poolSize >= MAX_POOL_SIZE; }