openmohaa/code/fgame/bg_misc.cpp
2024-10-02 14:09:36 +02:00

420 lines
No EOL
11 KiB
C++

/*
===========================================================================
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_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;
}
/*
========================
BG_MapCGMToProtocol
This is done after each set of usercmd_t on the server,
and after local prediction on the client
========================
*/
int BG_MapCGMToProtocol(int protocol, int messageNumber)
{
int newMessageNumber = messageNumber;
if (protocol >= PROTOCOL_MOHTA_MIN) {
// no need translation
return messageNumber;
}
if (messageNumber > 40) {
// unsupported...
return messageNumber;
}
if (messageNumber >= 17) {
return messageNumber - 3;
}
if (messageNumber == 15 || messageNumber == 16) {
// return explosion effect number 2
return 14;
}
if (messageNumber > 10) {
return messageNumber - 1;
}
return newMessageNumber;
}