/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_misc.c -- both games misc functions, all completely stateless #include "../qcommon/q_shared.h" #include "bg_public.h" /* ================ BG_EvaluateTrajectory ================ */ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) { } /* ================ BG_EvaluateTrajectoryDelta For determining velocity at a given time ================ */ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) { } // FIXME: OLD Q3 CODE #if 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; #ifdef MISSIONPACK int upperBound; #endif if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) { Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" ); } item = &bg_itemlist[ent->modelindex]; switch( item->giType ) { case IT_WEAPON: return qtrue; // weapons are always picked up case IT_AMMO: if ( ps->ammo[ item->giTag ] >= 200 ) { return qfalse; // can't hold any more } return qtrue; case IT_ARMOR: #ifdef MISSIONPACK if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) { return qfalse; } // we also clamp armor to the maxhealth for handicapping if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { upperBound = ps->stats[STAT_MAX_HEALTH]; } else { upperBound = ps->stats[STAT_MAX_HEALTH] * 2; } if ( ps->stats[STAT_ARMOR] >= upperBound ) { return qfalse; } #else if ( ps->stats[STAT_ARMOR] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; } #endif return qtrue; case IT_HEALTH: // small and mega healths will go over the max, otherwise // don't pick up if already at max #ifdef MISSIONPACK if( bg_itemlist[ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { upperBound = ps->stats[STAT_MAX_HEALTH]; } else #endif 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 #ifdef MISSIONPACK case IT_PERSISTANT_POWERUP: // can only hold one item at a time if ( ps->stats[STAT_PERSISTANT_POWERUP] ) { return qfalse; } // check team only if( ( ent->generic1 & 2 ) && ( ps->stats[STAT_TEAM] != TEAM_RED ) ) { return qfalse; } if( ( ent->generic1 & 4 ) && ( ps->stats[STAT_TEAM] != TEAM_BLUE ) ) { return qfalse; } return qtrue; #endif case IT_TEAM: // team items, such as flags #ifdef MISSIONPACK if( gametype == GT_1FCTF ) { // neutral flag can always be picked up if( item->giTag == PW_NEUTRALFLAG ) { return qtrue; } if (ps->stats[STAT_TEAM] == TEAM_RED) { if (item->giTag == PW_BLUEFLAG && ps->powerups[PW_NEUTRALFLAG] ) { return qtrue; } } else if (ps->stats[STAT_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG && ps->powerups[PW_NEUTRALFLAG] ) { return qtrue; } } } #endif if( gametype == GT_CTF ) { // 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->stats[STAT_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->stats[STAT_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG || (item->giTag == PW_BLUEFLAG && ent->modelindex2) || (item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) ) return qtrue; } } #ifdef MISSIONPACK if( gametype == GT_HARVESTER ) { return qtrue; } #endif return qfalse; case IT_HOLDABLE: // can only hold one item at a time if ( ps->stats[STAT_HOLDABLE_ITEM] ) { return qfalse; } return qtrue; case IT_BAD: Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" ); default: #ifndef Q3_VM #ifndef NDEBUG 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; } } #endif /* ======================== BG_TouchJumpPad ======================== */ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { } /* ======================== 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 ) { if ( ps->pm_type == PM_NOCLIP ) { s->eType = 0;//ET_INVISIBLE; //} else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { // s->eType = 0;//ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; VectorCopy( ps->origin, s->origin ); if ( snap ) { SnapVector( s->origin ); } // set the trDelta for flag direction VectorCopy( ps->velocity, s->pos.trDelta ); VectorCopy( ps->viewangles, s->angles ); if ( snap ) { SnapVector( s->angles ); } //s->angles2[YAW] = ps->movementDir; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config //s->eFlags = ps->eFlags; //if ( ps->stats[STAT_HEALTH] <= 0 ) { // s->eFlags |= EF_DEAD; //} else { // s->eFlags &= ~EF_DEAD; //} s->groundEntityNum = ps->groundEntityNum; } /* ======================== 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 ) { if ( ps->pm_type == PM_NOCLIP ) { s->eType = 0;//ET_INVISIBLE; //} else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { // s->eType = 0;//ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; VectorCopy( ps->origin, s->origin ); if ( snap ) { SnapVector( s->origin ); } // 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; VectorCopy( ps->viewangles, s->angles ); if ( snap ) { SnapVector( s->angles ); } s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->groundEntityNum = ps->groundEntityNum; }