Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1458 lines (1264 sloc) 33.7 KB
/*
* Wolfenstein: Enemy Territory GPL Source Code
* Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
*
* ET: Legacy
* Copyright (C) 2012-2018 ET:Legacy team <mail@etlegacy.com>
*
* This file is part of ET: Legacy - http://www.etlegacy.com
*
* ET: Legacy 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 3 of the License, or
* (at your option) any later version.
*
* ET: Legacy 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 ET: Legacy. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, Wolfenstein: Enemy Territory GPL Source Code is also
* subject to certain additional terms. You should have received a copy
* of these additional terms immediately following the terms and conditions
* of the GNU General Public License which accompanied the source code.
* If not, please request a copy in writing from id Software at the address below.
*
* id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*/
/**
* @file g_teammapdata.c
*/
#include "g_local.h"
/**
* @brief G_PushMapEntityToBuffer
* @param[out] buffer
* @param[in] size
* @param[in] mEnt
*/
void G_PushMapEntityToBuffer(char *buffer, size_t size, mapEntityData_t *mEnt)
{
char buf[32];
if (level.ccLayers)
{
Com_sprintf(buf, sizeof(buf), "%i %i %i", ((int)mEnt->org[0]) / 128, ((int)mEnt->org[1]) / 128, ((int)mEnt->org[2]) / 128);
}
else
{
Com_sprintf(buf, sizeof(buf), "%i %i", ((int)mEnt->org[0]) / 128, ((int)mEnt->org[1]) / 128);
}
switch (mEnt->type)
{
// These need to be send so that icons can display correct command map layer
case ME_CONSTRUCT:
case ME_DESTRUCT:
case ME_DESTRUCT_2:
case ME_TANK:
case ME_TANK_DEAD:
case ME_COMMANDMAP_MARKER:
Q_strcat(buffer, size, va(" %i %s %i", mEnt->type, buf, mEnt->data));
break;
default:
Q_strcat(buffer, size, va(" %i %s %i %i", mEnt->type, buf, mEnt->yaw, mEnt->data));
break;
}
}
/**
* @brief G_InitMapEntityData
* @param[out] teamList
*/
void G_InitMapEntityData(mapEntityData_Team_t *teamList)
{
int i;
mapEntityData_t *trav, *lasttrav;
Com_Memset(teamList, 0, sizeof(mapEntityData_Team_t));
teamList->activeMapEntityData.next = &teamList->activeMapEntityData;
teamList->activeMapEntityData.prev = &teamList->activeMapEntityData;
teamList->freeMapEntityData = teamList->mapEntityData_Team;
for (i = 0, trav = teamList->mapEntityData_Team + 1, lasttrav = teamList->mapEntityData_Team ; i < MAX_GENTITIES - 1 ; i++, trav++)
{
lasttrav->next = trav;
lasttrav = trav;
}
}
/**
* @brief G_FreeMapEntityData
* @param[in,out] teamList
* @param[in,out] mEnt
* @return Next entity in the array
*/
mapEntityData_t *G_FreeMapEntityData(mapEntityData_Team_t *teamList, mapEntityData_t *mEnt)
{
mapEntityData_t *ret = mEnt->next;
if (!mEnt->prev)
{
G_Error("G_FreeMapEntityData: not active\n");
}
// remove from the doubly linked active list
mEnt->prev->next = mEnt->next;
mEnt->next->prev = mEnt->prev;
// the free list is only singly linked
mEnt->next = teamList->freeMapEntityData;
teamList->freeMapEntityData = mEnt;
return(ret);
}
/**
* @brief G_AllocMapEntityData
* @param[in,out] teamList
* @return
*/
mapEntityData_t *G_AllocMapEntityData(mapEntityData_Team_t *teamList)
{
mapEntityData_t *mEnt;
if (!teamList->freeMapEntityData)
{
// no free entities - bomb out
G_Error("G_AllocMapEntityData: out of entities\n");
}
mEnt = teamList->freeMapEntityData;
teamList->freeMapEntityData = teamList->freeMapEntityData->next;
Com_Memset(mEnt, 0, sizeof(*mEnt));
mEnt->singleClient = -1;
// link into the active list
mEnt->next = teamList->activeMapEntityData.next;
mEnt->prev = &teamList->activeMapEntityData;
teamList->activeMapEntityData.next->prev = mEnt;
teamList->activeMapEntityData.next = mEnt;
return mEnt;
}
/**
* @brief G_FindMapEntityData
* @param[in] teamList
* @param[in] entNum
* @return
*/
mapEntityData_t *G_FindMapEntityData(mapEntityData_Team_t *teamList, int entNum)
{
mapEntityData_t *mEnt;
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->singleClient >= 0)
{
continue;
}
if (entNum == mEnt->entNum)
{
return mEnt;
}
}
// not found
return NULL;
}
/**
* @brief G_FindMapEntityDataSingleClient
* @param[in] teamList
* @param[in] start
* @param[in] entNum
* @param[in] clientNum
* @return
*/
mapEntityData_t *G_FindMapEntityDataSingleClient(mapEntityData_Team_t *teamList, mapEntityData_t *start, int entNum, int clientNum)
{
mapEntityData_t *mEnt;
if (start)
{
mEnt = start->next;
}
else
{
mEnt = teamList->activeMapEntityData.next;
}
for ( ; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (clientNum == -1)
{
if (mEnt->singleClient < 0)
{
continue;
}
}
else if (mEnt->singleClient >= 0 && clientNum != mEnt->singleClient)
{
continue;
}
if (entNum == mEnt->entNum)
{
return(mEnt);
}
}
// not found
return NULL;
}
////////////////////////////////////////////////////////////////////
/**
* @struct plane_s
* @brief Some culling bits
*/
typedef struct plane_s
{
vec3_t normal;
float dist;
} plane_t;
static plane_t frustum[4];
/**
* @brief G_SetupFrustum
* @param[in] ent
*/
void G_SetupFrustum(gentity_t *ent)
{
float xs, xc;
float ang;
vec3_t axis[3];
vec3_t vieworg;
ang = (float)(DEG2RAD(90) * 0.5);
SinCos(ang, xs, xc);
AnglesToAxis(ent->client->ps.viewangles, axis);
VectorScale(axis[0], xs, frustum[0].normal);
VectorMA(frustum[0].normal, xc, axis[1], frustum[0].normal);
VectorScale(axis[0], xs, frustum[1].normal);
VectorMA(frustum[1].normal, -xc, axis[1], frustum[1].normal);
VectorScale(axis[0], xs, frustum[2].normal);
VectorMA(frustum[2].normal, xc, axis[2], frustum[2].normal);
VectorScale(axis[0], xs, frustum[3].normal);
VectorMA(frustum[3].normal, -xc, axis[2], frustum[3].normal);
VectorCopy(ent->client->ps.origin, vieworg);
vieworg[2] += ent->client->ps.viewheight;
frustum[0].dist = DotProduct(vieworg, frustum[0].normal);
frustum[1].dist = DotProduct(vieworg, frustum[1].normal);
frustum[2].dist = DotProduct(vieworg, frustum[2].normal);
frustum[3].dist = DotProduct(vieworg, frustum[3].normal);
}
// Give bots a larger view angle through binoculars than players get - this should help the
// landmine detection...
#define BINOCULAR_ANGLE 10.0f
#define BOT_BINOCULAR_ANGLE 60.0f
/**
* @brief G_SetupFrustum_ForBinoculars
* @param[in] ent
*/
void G_SetupFrustum_ForBinoculars(gentity_t *ent)
{
float xs, xc;
float ang;
vec3_t axis[3];
vec3_t vieworg;
float baseAngle = (ent->r.svFlags & SVF_BOT) ? BOT_BINOCULAR_ANGLE : BINOCULAR_ANGLE;
ang = (float)(DEG2RAD(baseAngle) * 0.5);
SinCos(ang, xs, xc);
AnglesToAxis(ent->client->ps.viewangles, axis);
VectorScale(axis[0], xs, frustum[0].normal);
VectorMA(frustum[0].normal, xc, axis[1], frustum[0].normal);
VectorScale(axis[0], xs, frustum[1].normal);
VectorMA(frustum[1].normal, -xc, axis[1], frustum[1].normal);
VectorScale(axis[0], xs, frustum[2].normal);
VectorMA(frustum[2].normal, xc, axis[2], frustum[2].normal);
VectorScale(axis[0], xs, frustum[3].normal);
VectorMA(frustum[3].normal, -xc, axis[2], frustum[3].normal);
VectorCopy(ent->client->ps.origin, vieworg);
vieworg[2] += ent->client->ps.viewheight;
frustum[0].dist = DotProduct(vieworg, frustum[0].normal);
frustum[1].dist = DotProduct(vieworg, frustum[1].normal);
frustum[2].dist = DotProduct(vieworg, frustum[2].normal);
frustum[3].dist = DotProduct(vieworg, frustum[3].normal);
}
/**
* @brief G_CullPointAndRadius
* @param[in] pt
* @param[in] radius
* @return true if not culled
*/
static qboolean G_CullPointAndRadius(vec3_t pt, float radius)
{
float dist = DotProduct(pt, frustum[0].normal) - frustum[0].dist;
if (dist < -radius || dist <= radius)
{
return qfalse;
}
dist = DotProduct(pt, frustum[1].normal) - frustum[1].dist;
if (dist < -radius || dist <= radius)
{
return qfalse;
}
dist = DotProduct(pt, frustum[2].normal) - frustum[2].dist;
if (dist < -radius || dist <= radius)
{
return qfalse;
}
dist = DotProduct(pt, frustum[3].normal) - frustum[3].dist;
if (dist < -radius || dist <= radius)
{
return qfalse;
}
return qtrue;
}
/**
* @brief G_VisibleFromBinoculars
* @param[in] viewer
* @param[in] ent
* @param[in,out] origin
* @return
*/
qboolean G_VisibleFromBinoculars(gentity_t *viewer, gentity_t *ent, vec3_t origin)
{
vec3_t vieworg;
trace_t trace;
VectorCopy(viewer->client->ps.origin, vieworg);
vieworg[2] += viewer->client->ps.viewheight;
// check if head is visible
if (ent->methodOfDeath != MOD_LANDMINE)
{
origin[2] += ent->client->ps.viewheight;
}
if (!G_CullPointAndRadius(origin, 0))
{
return qfalse;
}
if (!trap_InPVS(vieworg, origin))
{
return qfalse;
}
trap_Trace(&trace, vieworg, NULL, NULL, origin, viewer->s.number, MASK_SHOT);
if (trace.fraction != 1.f)
{
if (trace.entityNum != ent->s.number)
{
return qfalse;
}
else
{
return qtrue;
}
}
return qtrue;
}
/**
* @brief G_VisibleFromBinoculars_Box
* @param[in] viewer
* @param[in] ent
* @param[in,out] origin
* @param[in] mins
* @param[in] maxs
* @return
*/
qboolean G_VisibleFromBinoculars_Box(gentity_t *viewer, gentity_t *ent, vec3_t origin, vec3_t mins, vec3_t maxs)
{
vec3_t vieworg;
trace_t trace;
VectorCopy(viewer->client->ps.origin, vieworg);
vieworg[2] += viewer->client->ps.viewheight;
// check if head is visible
if (ent->methodOfDeath != MOD_LANDMINE)
{
origin[2] += ent->client->ps.viewheight;
}
if (!G_CullPointAndRadius(origin, 0))
{
return qfalse;
}
if (!trap_InPVS(vieworg, origin))
{
return qfalse;
}
trap_Trace(&trace, vieworg, mins, maxs, origin, viewer->s.number, MASK_SHOT);
if (trace.fraction != 1.f)
{
if (trace.entityNum != ent->s.number)
{
return qfalse;
}
else
{
return qtrue;
}
}
return qtrue;
}
/**
* @brief G_ResetTeamMapData
*/
void G_ResetTeamMapData()
{
G_InitMapEntityData(&mapEntityData[0]);
G_InitMapEntityData(&mapEntityData[1]);
}
/**
* @brief G_UpdateTeamMapData_Construct
* @param[in] ent
*/
void G_UpdateTeamMapData_Construct(gentity_t *ent)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
switch (ent->s.teamNum)
{
case TEAM_SPECTATOR: // both teams - do twice
{
teamList = &mapEntityData[0];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->type = ME_CONSTRUCT;
mEnt->startTime = level.time;
mEnt->yaw = 0;
teamList = &mapEntityData[1];
break;
}
case TEAM_AXIS:
{
teamList = &mapEntityData[0];
break;
}
case TEAM_ALLIES:
{
teamList = &mapEntityData[1];
break;
}
default:
return;
}
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->type = ME_CONSTRUCT;
mEnt->startTime = level.time;
mEnt->yaw = 0;
}
/**
* @brief G_UpdateTeamMapData_Tank
* @param[in] ent
*/
void G_UpdateTeamMapData_Tank(gentity_t *ent)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList = &mapEntityData[0];
mapEntityData_t *mEnt;
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = ent->s.modelindex2;
mEnt->startTime = level.time;
if (ent->s.eType == ET_TANK_INDICATOR_DEAD)
{
mEnt->type = ME_TANK_DEAD;
}
else
{
mEnt->type = ME_TANK;
}
mEnt->yaw = 0;
teamList = &mapEntityData[1];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = ent->s.modelindex2;
mEnt->startTime = level.time;
if (ent->s.eType == ET_TANK_INDICATOR_DEAD)
{
mEnt->type = ME_TANK_DEAD;
}
else
{
mEnt->type = ME_TANK;
}
mEnt->yaw = 0;
}
/**
* @brief G_UpdateTeamMapData_Destruct
* @param[in] ent
*/
void G_UpdateTeamMapData_Destruct(gentity_t *ent)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
if (ent->s.teamNum == TEAM_AXIS)
{
teamList = &mapEntityData[1]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT;
mEnt->yaw = 0;
}
else
{
if (ent->parent->target_ent && (ent->parent->target_ent->s.eType == ET_CONSTRUCTIBLE))
{
if (ent->parent->spawnflags & ((1 << 6) | (1 << 4)))
{
teamList = &mapEntityData[1]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT_2;
mEnt->yaw = 0;
}
}
else if (ent->parent->target_ent && ent->parent->target_ent->s.eType == ET_EXPLOSIVE)
{
// do we have any spawn vars to check?
teamList = &mapEntityData[1]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT; // or ME_DESTRUCT_2?
mEnt->yaw = 0;
}
}
if (ent->s.teamNum == TEAM_ALLIES)
{
teamList = &mapEntityData[0]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT;
mEnt->yaw = 0;
}
else
{
if (ent->parent->target_ent && (ent->parent->target_ent->s.eType == ET_CONSTRUCTIBLE))
{
if (ent->parent->spawnflags & ((1 << 6) | (1 << 4)))
{
teamList = &mapEntityData[0]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT_2;
mEnt->yaw = 0;
}
}
else if (ent->parent->target_ent && ent->parent->target_ent->s.eType == ET_EXPLOSIVE)
{
// do we have any spawn vars to check?
teamList = &mapEntityData[0]; // inverted
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.pos.trBase, mEnt->org);
mEnt->data = mEnt->entNum; //ent->s.modelindex2;
mEnt->startTime = level.time;
mEnt->type = ME_DESTRUCT; // or ME_DESTRUCT_2?
mEnt->yaw = 0;
}
}
}
/**
* @brief G_UpdateTeamMapData_Player
* @param[in] ent
* @param[in] forceAllied
* @param[in] forceAxis
*/
void G_UpdateTeamMapData_Player(gentity_t *ent, qboolean forceAllied, qboolean forceAxis)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
if (!ent->client)
{
return;
}
if (ent->client->ps.pm_flags & PMF_LIMBO)
{
return;
}
if (ent->client->sess.sessionTeam == TEAM_AXIS)
{
forceAxis = qtrue;
}
else if (ent->client->sess.sessionTeam == TEAM_ALLIES)
{
forceAllied = qtrue;
}
if (forceAxis)
{
teamList = &mapEntityData[0];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->client->ps.origin, mEnt->org);
mEnt->yaw = ent->client->ps.viewangles[YAW];
mEnt->data = num;
mEnt->startTime = level.time;
if (ent->health <= 0)
{
mEnt->type = ME_PLAYER_REVIVE;
}
else if (ent->client->ps.powerups[PW_REDFLAG] || ent->client->ps.powerups[PW_BLUEFLAG])
{
mEnt->type = ME_PLAYER_OBJECTIVE;
}
else
{
mEnt->type = ME_PLAYER;
}
}
if (forceAllied)
{
teamList = &mapEntityData[1];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->client->ps.origin, mEnt->org);
mEnt->yaw = ent->client->ps.viewangles[YAW];
mEnt->data = num;
mEnt->startTime = level.time;
if (ent->health <= 0)
{
mEnt->type = ME_PLAYER_REVIVE;
}
else if (ent->client->ps.powerups[PW_REDFLAG] || ent->client->ps.powerups[PW_BLUEFLAG])
{
mEnt->type = ME_PLAYER_OBJECTIVE;
}
else
{
mEnt->type = ME_PLAYER;
}
}
}
/**
* @brief G_UpdateTeamMapData_DisguisedPlayer
* @param[in] spotter
* @param[in] ent
* @param[in] forceAllied
* @param[in] forceAxis
*/
static void G_UpdateTeamMapData_DisguisedPlayer(gentity_t *spotter, gentity_t *ent, qboolean forceAllied, qboolean forceAxis)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
if (!ent->client)
{
return;
}
if (ent->client->ps.pm_flags & PMF_LIMBO)
{
return;
}
switch (ent->client->sess.sessionTeam)
{
case TEAM_AXIS:
forceAxis = qtrue;
break;
case TEAM_ALLIES:
forceAllied = qtrue;
break;
default:
break;
}
if (forceAxis)
{
teamList = &mapEntityData[0];
mEnt = G_FindMapEntityDataSingleClient(teamList, NULL, num, spotter->s.clientNum);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
mEnt->singleClient = spotter->s.clientNum;
}
VectorCopy(ent->client->ps.origin, mEnt->org);
mEnt->yaw = ent->client->ps.viewangles[YAW];
mEnt->data = num;
mEnt->startTime = level.time;
mEnt->type = ME_PLAYER_DISGUISED;
}
if (forceAllied)
{
teamList = &mapEntityData[1];
mEnt = G_FindMapEntityDataSingleClient(teamList, NULL, num, spotter->s.clientNum);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
mEnt->singleClient = spotter->s.clientNum;
}
VectorCopy(ent->client->ps.origin, mEnt->org);
mEnt->yaw = ent->client->ps.viewangles[YAW];
mEnt->data = num;
mEnt->startTime = level.time;
mEnt->type = ME_PLAYER_DISGUISED;
}
}
/**
* @brief G_UpdateTeamMapData_LandMine
* @param[in] ent
*/
void G_UpdateTeamMapData_LandMine(gentity_t *ent)
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
int team = ent->s.teamNum & 3; //ent->s.teamNum % 4
if (!(ent->s.teamNum < 4 || ent->s.teamNum >= 8))
{
return; // must be armed..
}
// inversed teamlists, we want to see the enemy mines
if (ent->s.modelindex2) // must be spotted..
{
teamList = &mapEntityData[(team == TEAM_AXIS) ? 1 : 0];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->r.currentOrigin, mEnt->org);
mEnt->data = team;
mEnt->startTime = level.time;
mEnt->type = ME_LANDMINE;
}
// team mines..
teamList = &mapEntityData[(team == TEAM_AXIS) ? 0 : 1];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->r.currentOrigin, mEnt->org);
mEnt->data = team;
mEnt->startTime = level.time;
mEnt->type = ME_LANDMINE;
}
/**
* @brief G_UpdateTeamMapData_CommandmapMarker
* @param[in] ent
*/
void G_UpdateTeamMapData_CommandmapMarker(gentity_t *ent)
{
if (!ent->parent)
{
return;
}
if (ent->entstate != STATE_DEFAULT)
{
return;
}
if (ent->parent->spawnflags & (ALLIED_OBJECTIVE | AXIS_OBJECTIVE))
{
int num = ent - g_entities;
mapEntityData_Team_t *teamList;
mapEntityData_t *mEnt;
// alies
teamList = &mapEntityData[0];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.origin, mEnt->org);
mEnt->data = ent->parent ? ent->parent->s.teamNum : -1;
mEnt->startTime = level.time;
mEnt->type = ME_COMMANDMAP_MARKER;
mEnt->yaw = 0;
// axis
teamList = &mapEntityData[1];
mEnt = G_FindMapEntityData(teamList, num);
if (!mEnt)
{
mEnt = G_AllocMapEntityData(teamList);
mEnt->entNum = num;
}
VectorCopy(ent->s.origin, mEnt->org);
mEnt->data = ent->parent ? ent->parent->s.teamNum : -1;
mEnt->startTime = level.time;
mEnt->type = ME_COMMANDMAP_MARKER;
mEnt->yaw = 0;
}
}
/**
* @brief G_SendSpectatorMapEntityInfo
* @param[in] e
*/
void G_SendSpectatorMapEntityInfo(gentity_t *e)
{
// special version, sends different set of ents - only the objectives, but also team info (string is split in two basically)
mapEntityData_t *mEnt;
mapEntityData_Team_t *teamList = &mapEntityData[0]; // Axis data init
char buffer[2048];
int al_cnt = 0, ax_cnt = 0;
buffer[0] = '\0';
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->type != ME_CONSTRUCT && mEnt->type != ME_DESTRUCT && mEnt->type != ME_DESTRUCT_2 && mEnt->type != ME_TANK && mEnt->type != ME_TANK_DEAD && mEnt->type != ME_COMMANDMAP_MARKER)
{
continue;
}
if (mEnt->singleClient >= 0 && e->s.clientNum != mEnt->singleClient)
{
continue;
}
ax_cnt++;
}
// Allied data init
teamList = &mapEntityData[1];
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->type != ME_CONSTRUCT && mEnt->type != ME_DESTRUCT && mEnt->type != ME_DESTRUCT_2 && mEnt->type != ME_TANK && mEnt->type != ME_TANK_DEAD && mEnt->type != ME_COMMANDMAP_MARKER)
{
continue;
}
if (mEnt->singleClient >= 0 && e->s.clientNum != mEnt->singleClient)
{
continue;
}
al_cnt++;
}
// Data setup
// FIXME: Find out why objective counts are reset to zero when a new player connects
if (ax_cnt > 0 || al_cnt > 0)
{
Com_sprintf(buffer, sizeof(buffer), "entnfo %i %i", ax_cnt, al_cnt);
}
// Axis data
teamList = &mapEntityData[0];
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->type != ME_CONSTRUCT && mEnt->type != ME_DESTRUCT && mEnt->type != ME_DESTRUCT_2 && mEnt->type != ME_TANK && mEnt->type != ME_TANK_DEAD && mEnt->type != ME_COMMANDMAP_MARKER)
{
continue;
}
if (mEnt->singleClient >= 0 && e->s.clientNum != mEnt->singleClient)
{
continue;
}
G_PushMapEntityToBuffer(buffer, sizeof(buffer), mEnt);
}
// Allied data
teamList = &mapEntityData[1];
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->type != ME_CONSTRUCT && mEnt->type != ME_DESTRUCT && mEnt->type != ME_DESTRUCT_2 && mEnt->type != ME_TANK && mEnt->type != ME_TANK_DEAD && mEnt->type != ME_COMMANDMAP_MARKER)
{
continue;
}
if (mEnt->singleClient >= 0 && e->s.clientNum != mEnt->singleClient)
{
continue;
}
G_PushMapEntityToBuffer(buffer, sizeof(buffer), mEnt);
}
if (buffer[0] != '\0') // don't send an emtpy buffer
{
trap_SendServerCommand(e - g_entities, buffer);
}
}
/**
* @brief G_SendMapEntityInfo
* @param[in] e
*/
void G_SendMapEntityInfo(gentity_t *e)
{
mapEntityData_t *mEnt;
mapEntityData_Team_t *teamList;
char buffer[2048];
int cnt = 0;
buffer[0] = '\0';
if (e->client->sess.sessionTeam == TEAM_SPECTATOR)
{
G_SendSpectatorMapEntityInfo(e);
return;
}
// something really went wrong if this evaluates to true - TEAM_FREE
if (e->client->sess.sessionTeam != TEAM_AXIS && e->client->sess.sessionTeam != TEAM_ALLIES)
{
return;
}
teamList = e->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[0] : &mapEntityData[1];
mEnt = teamList->activeMapEntityData.next;
while (mEnt && mEnt != &teamList->activeMapEntityData)
{
if (level.time - mEnt->startTime > 1000)
{
// we can free this player from the list now
if (mEnt->type == ME_PLAYER || mEnt->type == ME_PLAYER_REVIVE || mEnt->type == ME_PLAYER_OBJECTIVE)
{
mEnt = G_FreeMapEntityData(teamList, mEnt);
continue;
}
else if (mEnt->type == ME_PLAYER_DISGUISED)
{
if (mEnt->singleClient == e->s.clientNum)
{
mEnt = G_FreeMapEntityData(teamList, mEnt);
continue;
}
}
}
cnt++;
mEnt = mEnt->next;
}
if (e->client->sess.sessionTeam == TEAM_AXIS)
{
if (cnt > 0)
{
Com_sprintf(buffer, sizeof(buffer), "entnfo %i 0", cnt);
}
}
else
{
if (cnt > 0)
{
Com_sprintf(buffer, sizeof(buffer), "entnfo 0 %i", cnt);
}
}
for (mEnt = teamList->activeMapEntityData.next; mEnt && mEnt != &teamList->activeMapEntityData; mEnt = mEnt->next)
{
if (mEnt->singleClient >= 0 && e->s.clientNum != mEnt->singleClient)
{
continue;
}
G_PushMapEntityToBuffer(buffer, sizeof(buffer), mEnt);
}
if (buffer[0] != '\0') // don't send an emtpy buffer
{
trap_SendServerCommand(e - g_entities, buffer);
}
}
/**
* @brief G_PopupMessageForMines
* @param[in] player
*/
void G_PopupMessageForMines(gentity_t *player) // int sound
{
gentity_t *pm;
pm = G_PopupMessage(PM_MINES);
VectorCopy(player->client->landmineSpotted->r.currentOrigin, pm->s.origin);
pm->s.effect2Time = (player->client->sess.sessionTeam == TEAM_AXIS) ? TEAM_ALLIES : TEAM_AXIS;
pm->s.effect3Time = player - g_entities;
//pm->s.loopSound = sound;
}
/**
* @brief G_CheckSpottedLandMines
*/
void G_CheckSpottedLandMines(void)
{
int i, j;
gentity_t *ent, *ent2;
if (level.time - level.lastMapSpottedMinesUpdate < 1000)
{
return;
}
level.lastMapSpottedMinesUpdate = level.time;
for (i = 0; i < level.numConnectedClients; i++)
{
ent = &g_entities[level.sortedClients[i]];
if (!ent->inuse || !ent->client)
{
continue;
}
if (ent->health <= 0)
{
continue;
}
// must be in a valid team
if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE)
{
continue;
}
if (ent->client->ps.pm_flags & PMF_LIMBO)
{
continue;
}
if (ent->client->sess.playerType == PC_COVERTOPS && (ent->client->ps.eFlags & EF_ZOOMING))
{
G_SetupFrustum_ForBinoculars(ent);
for (j = 0, ent2 = g_entities; j < level.num_entities; j++, ent2++)
{
if (!ent2->inuse || ent2 == ent)
{
continue;
}
if (ent2->s.eType != ET_MISSILE)
{
continue;
}
if (ent2->methodOfDeath == MOD_LANDMINE)
{
if ((ent2->s.teamNum < 4 || ent2->s.teamNum >= 8) && (ent2->s.teamNum % 4 != ent->client->sess.sessionTeam))
{
// as before, we can only detect a mine if we can see it from our binoculars
if (G_VisibleFromBinoculars(ent, ent2, ent2->r.currentOrigin))
{
G_UpdateTeamMapData_LandMine(ent2);
switch (ent2->s.teamNum % 4)
{
case TEAM_AXIS:
case TEAM_ALLIES:
if (!ent2->s.modelindex2)
{
ent->client->landmineSpottedTime = level.time;
ent->client->landmineSpotted = ent2;
ent2->s.density = ent - g_entities + 1;
ent2->missionLevel = level.time;
ent->client->landmineSpotted->count2 += 50; // @sv_fps
if (ent->client->landmineSpotted->count2 >= 250)
{
ent->client->landmineSpotted->count2 = 250;
ent->client->landmineSpotted->s.modelindex2 = 1;
// for marker
// Landmine flags shouldn't block our view
// don't do this if the mine has been triggered.
if (!G_LandmineTriggered(ent->client->landmineSpotted))
{
ent->client->landmineSpotted->s.frame = rand() % 20;
ent->client->landmineSpotted->r.contents = CONTENTS_TRANSLUCENT;
trap_LinkEntity(ent->client->landmineSpotted);
}
G_PopupMessageForMines(ent);
trap_SendServerCommand(ent - g_entities, "cp \"Landmine revealed\"");
G_AddSkillPoints(ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 3.f);
G_DebugAddSkillPoints(ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 3.f, "spotting a landmine");
}
}
break;
default:
break;
}
}
else
{
// if we can't see the mine from our binoculars, make sure we clear out the landmineSpotted ptr,
// because bots looking for mines are getting confused
ent->client->landmineSpotted = NULL;
}
}
}
}
}
}
}
/**
* @brief G_UpdateTeamMapData
*/
void G_UpdateTeamMapData(void)
{
int i, j;
gentity_t *ent, *ent2;
mapEntityData_t *mEnt;
qboolean f1, f2;
G_CheckSpottedLandMines();
if (level.time - level.lastMapEntityUpdate < 1000)
{
return;
}
level.lastMapEntityUpdate = level.time;
// all ents - comon update
for (i = 0; i < level.num_entities; i++)
{
ent = &g_entities[i];
if (!ent->inuse)
{
//mapEntityData[0][i].valid = qfalse;
//mapEntityData[1][i].valid = qfalse;
continue;
}
switch (ent->s.eType)
{
case ET_PLAYER:
G_UpdateTeamMapData_Player(ent, qfalse, qfalse);
for (j = 0; j < 2; j++)
{
mapEntityData_Team_t *teamList = &mapEntityData[j];
mEnt = G_FindMapEntityDataSingleClient(teamList, NULL, ent->s.number, -1);
while (mEnt)
{
VectorCopy(ent->client->ps.origin, mEnt->org);
mEnt->yaw = ent->client->ps.viewangles[YAW];
mEnt = G_FindMapEntityDataSingleClient(teamList, mEnt, ent->s.number, -1);
}
}
break;
case ET_CONSTRUCTIBLE_INDICATOR:
if (ent->parent && ent->parent->entstate == STATE_DEFAULT)
{
G_UpdateTeamMapData_Construct(ent);
}
break;
case ET_EXPLOSIVE_INDICATOR:
if (ent->parent && ent->parent->entstate == STATE_DEFAULT)
{
G_UpdateTeamMapData_Destruct(ent);
}
break;
case ET_TANK_INDICATOR:
case ET_TANK_INDICATOR_DEAD:
G_UpdateTeamMapData_Tank(ent);
break;
case ET_MISSILE:
if (ent->methodOfDeath == MOD_LANDMINE)
{
G_UpdateTeamMapData_LandMine(ent);
}
break;
case ET_COMMANDMAP_MARKER:
G_UpdateTeamMapData_CommandmapMarker(ent);
break;
default:
break;
}
}
// clients again - do special stuff for field- and covert ops
for (i = 0; i < level.numConnectedClients; i++)
{
ent = &g_entities[level.sortedClients[i]];
f1 = ent->client->sess.sessionTeam == TEAM_ALLIES ? qtrue : qfalse;
f2 = ent->client->sess.sessionTeam == TEAM_AXIS ? qtrue : qfalse;
if (!ent->inuse)
{
continue;
}
if (ent->health <= 0)
{
continue;
}
// must be in a valid team
if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE)
{
continue;
}
if (ent->client->ps.pm_flags & PMF_LIMBO)
{
continue;
}
if (ent->client->sess.playerType == PC_FIELDOPS && (ent->client->ps.eFlags & EF_ZOOMING) && ent->client->sess.skill[SK_SIGNALS] >= 4)
{
vec3_t pos[3];
G_SetupFrustum_ForBinoculars(ent);
for (j = 0; j < level.numConnectedClients; j++)
{
ent2 = &g_entities[level.sortedClients[j]];
if (!ent2->inuse || ent2 == ent)
{
continue;
}
// players are sometimes of type ET_GENERAL
if (ent2->s.eType != ET_PLAYER)
{
continue;
}
if (ent2->client->sess.sessionTeam == TEAM_SPECTATOR)
{
continue;
}
if (ent2->health <= 0 ||
ent2->client->sess.sessionTeam == ent->client->sess.sessionTeam ||
!ent2->client->ps.powerups[PW_OPS_DISGUISED])
{
continue;
}
VectorCopy(ent2->client->ps.origin, pos[0]);
VectorCopy(ent2->client->ps.mins, pos[1]);
VectorCopy(ent2->client->ps.maxs, pos[2]);
if (G_VisibleFromBinoculars_Box(ent, ent2, pos[0], pos[1], pos[2]))
{
G_UpdateTeamMapData_DisguisedPlayer(ent, ent2, f1, f2);
}
}
}
else if (ent->client->sess.playerType == PC_COVERTOPS)
{
vec3_t pos[3];
G_SetupFrustum(ent);
for (j = 0; j < level.numConnectedClients; j++)
{
ent2 = &g_entities[level.sortedClients[j]];
if (!ent2->inuse || ent2 == ent)
{
continue;
}
// players are sometimes of type ET_GENERAL
if (ent2->s.eType != ET_PLAYER)
{
continue;
}
// no spectators thanks
if (ent2->client->sess.sessionTeam == TEAM_SPECTATOR || ent2->client->sess.sessionTeam == TEAM_FREE)
{
continue;
}
// do not add 'dead' players
if (ent2->health <= 0)
{
continue;
}
// we will only add other team members here
if (ent2->client->sess.sessionTeam == ent->client->sess.sessionTeam)
{
continue;
}
VectorCopy(ent2->client->ps.origin, pos[0]);
VectorCopy(ent2->client->ps.mins, pos[1]);
VectorCopy(ent2->client->ps.maxs, pos[2]);
if (G_VisibleFromBinoculars_Box(ent, ent2, pos[0], pos[1], pos[2]))
{
G_UpdateTeamMapData_Player(ent2, f1, f2);
}
}
}
}
}