reaction/reaction/game/zcam_target.c

224 lines
5.5 KiB
C
Raw Normal View History

/*
* ZCam v1.0.4
* Spectator Camera for Quake III Arena
* Copyright (C), 2001 by Avi "Zung!" Rozen
*
* http://www.telefragged.com/zungbang/zcam
*
* Credits:
* FLIC camera mode is based on code taken from q2cam by Paul Jordan
* SWING camera mode is based on ideas taken from CreepCam for Quake I
*
*/
/* Camera Target Selection */
#include "g_local.h"
#include "zcam.h"
/* IsVisible:
* Is player #1 visible by player #2 ?
*/
qboolean IsVisible(gentity_t *player1, gentity_t *player2, float maxrange)
{
vec3_t length;
float distance;
trace_t trace;
// check for looking through non-transparent water
if (!trap_InPVS(player1->client->ps.origin, player2->client->ps.origin))
return qfalse;
trap_Trace ( &trace,
player1->client->ps.origin,
NULL,
NULL,
player2->client->ps.origin,
player1->s.clientNum,
MASK_SOLID );
VectorSubtract(player1->client->ps.origin, player2->client->ps.origin, length);
distance = VectorLength(length);
return ((maxrange == 0 || distance < maxrange) && trace.fraction == 1.0f);
}
/* NumPlayersVisible:
* Return the number of players visible by the specified viewer.
*/
int NumPlayersVisible(gentity_t *viewer, float maxrange)
{
int count = 0;
int i;
if (viewer == NULL)
{
return 0;
}
for (i = 0; i < level.maxclients; i++)
{
if (viewer->s.clientNum != i
&& level.clients[i].pers.connected == CON_CONNECTED
&& level.clients[i].sess.sessionTeam != TEAM_SPECTATOR
&& level.clients[i].ps.pm_type != PM_DEAD)
{
if (IsVisible(viewer, &g_entities[i], maxrange))
{
count++;
}
}
}
return count;
}
/* PriorityTarget:
* Override camera target selection. Returns the new favorite
* player (or the one already selected).
* Currently used to select flag carriers in CTF.
*/
gentity_t *PriorityTarget(gentity_t *target, qboolean *override)
{
int i;
gentity_t *favorite = NULL;
for (i = 0; i < level.maxclients; i++)
{
if (level.clients[i].pers.connected == CON_CONNECTED
&& level.clients[i].sess.sessionTeam != TEAM_SPECTATOR
&& level.clients[i].ps.pm_type == PM_NORMAL
&& (level.clients[i].ps.powerups[PW_REDFLAG]
|| level.clients[i].ps.powerups[PW_BLUEFLAG]))
{
if (favorite == NULL
|| NumPlayersVisible (favorite, MAX_VISIBLE_RANGE) < NumPlayersVisible (&g_entities[i], MAX_VISIBLE_RANGE))
{
favorite = &g_entities[i];
}
}
}
/* set override flag */
if (favorite)
*override = qtrue;
else
*override = qfalse;
return (favorite)? favorite:target;
}
/* PlayerToFollow:
* Select camera target. Selects the player that sees the maximum
* number of other players, unless a priority target exists (as
* determined by the function PriorityTarget above).
*/
gentity_t *PlayerToFollow(gentity_t *ent, qboolean *override)
{
gentity_t *viewer;
gentity_t *best = NULL;
int i, players, best_count = -1;
*override = qfalse;
for (i = 0; i < level.maxclients; i++)
{
/* don't switch to dead people */
viewer = &g_entities[i];
if (viewer->client->pers.connected != CON_CONNECTED)
continue;
if (viewer->client->sess.sessionTeam != TEAM_SPECTATOR
&& viewer->client->ps.pm_type == PM_NORMAL)
{
players = NumPlayersVisible(viewer, MAX_VISIBLE_RANGE);
if (players > best_count)
{
best_count = players;
best = viewer;
}
else if (players == best_count)
{
if (best->client->ps.persistant[PERS_SCORE] <
viewer->client->ps.persistant[PERS_SCORE])
{
best = viewer;
}
}
}
}
if (best == NULL)
return best;
return PriorityTarget(best, override);
}
/* PlayerToTrack:
* select swing camera target in the following priority order:
* - enemy flag carrier
* - closest enemy player
* - own team flag carrier
* - closest own team player
* (in DM this amount to picking the closest player)
*/
gentity_t *PlayerToTrack (gentity_t *ent, gentity_t *target1st)
{
int i;
gentity_t *target2nd;
gentity_t *best1 = NULL;
gentity_t *best2 = NULL;
vec3_t distance;
float current, closest1 = -1.0F, closest2 = -1.0F;
for (i = 0; i < level.maxclients; i++)
{
target2nd = &g_entities[i];
if (target1st != target2nd
&& target2nd->client->pers.connected == CON_CONNECTED
&& target2nd->client->sess.sessionTeam != TEAM_SPECTATOR
&& target2nd->client->ps.pm_type == PM_NORMAL
&& trap_InPVS (target1st->client->ps.origin, target2nd->client->ps.origin))
{
VectorSubtract(target1st->client->ps.origin, target2nd->client->ps.origin, distance);
current = VectorLength(distance);
if (target1st->client->sess.sessionTeam == target2nd->client->sess.sessionTeam)
{
if (target2nd->client->ps.powerups[PW_REDFLAG]
|| target2nd->client->ps.powerups[PW_BLUEFLAG])
{
best1 = target2nd;
}
if (!(best1
&& (best1->client->ps.powerups[PW_REDFLAG]
|| best1->client->ps.powerups[PW_BLUEFLAG]))
&& (closest1 < 0 || current < closest1))
{
best1 = target2nd;
closest1 = current;
}
}
else
{
if (target2nd->client->ps.powerups[PW_REDFLAG]
|| target2nd->client->ps.powerups[PW_BLUEFLAG])
{
best2 = target2nd;
}
if (!(best2
&& (best2->client->ps.powerups[PW_REDFLAG]
|| best2->client->ps.powerups[PW_BLUEFLAG]))
&& (closest2 < 0 || current < closest2))
{
best2 = target2nd;
closest2 = current;
}
}
}
}
return ((best2)? best2:best1);
}