2107 lines
49 KiB
C
2107 lines
49 KiB
C
// 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_WEAPONREADY10,//TORSO_WEAPONREADY11,//WP_TRIP_MINE,
|
|
TORSO_WEAPONREADY10,//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_THERMAL_THROW,//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 */ "gfx/mp/small_shield",
|
|
/* pickup */ "Shield Small",
|
|
25,
|
|
IT_ARMOR,
|
|
1, //special for shield - max on pickup is maxhealth*tag, thus small shield goes up to 100 shield
|
|
/* 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 */ "gfx/mp/large_shield",
|
|
/* pickup */ "Shield Large",
|
|
100,
|
|
IT_ARMOR,
|
|
2, //special for shield - max on pickup is maxhealth*tag, thus large shield goes up to 200 shield
|
|
/* 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 */ "gfx/hud/i_icon_medkit",
|
|
/* 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/movers/doors/forcefield_lp.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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/w_icon_merrsonn",
|
|
/* 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 */ "gfx/hud/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 */ "gfx/hud/w_icon_tripmine",
|
|
/* 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_proj.glm", "models/weapons2/detpack/det_pack_w.glm", 0, 0},
|
|
/* view */ "models/weapons2/detpack/det_pack.md3",
|
|
/* icon */ "gfx/hud/w_icon_detpack",
|
|
/* 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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/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 */ "gfx/hud/i_icon_battery",
|
|
/* 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 */ "gfx/mp/ammo_power_cell",
|
|
/* 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 */ "gfx/mp/ammo_metallic_bolts",
|
|
/* 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 */ "gfx/mp/ammo_rockets",
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
if (ps->fallingToDeath)
|
|
{
|
|
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] * item->giTag ) {
|
|
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 ((ps->fd.forcePowersActive & (1 << FP_RAGE)))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
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_PLAY_EFFECT_ID", //finally gave in and added it..
|
|
|
|
"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;
|
|
|
|
s->time2 = ps->holocronBits;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
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;
|
|
|
|
s->time2 = ps->holocronBits;
|
|
}
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
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;
|
|
}
|