mirror of
https://github.com/id-Software/quake2-rerelease-dll.git
synced 2026-03-20 00:49:33 +01:00
Initial commit
This commit is contained in:
153
rerelease/p_trail.cpp
Normal file
153
rerelease/p_trail.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) ZeniMax Media Inc.
|
||||
// Licensed under the GNU General Public License 2.0.
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PLAYER TRAIL
|
||||
|
||||
==============================================================================
|
||||
|
||||
This is a two-way list containing the a list of points of where
|
||||
the player has been recently. It is used by monsters for pursuit.
|
||||
|
||||
This is improved from vanilla; now, the list itself is stored in
|
||||
client data so it can be stored for multiple clients.
|
||||
|
||||
chain = next
|
||||
enemy = prev
|
||||
|
||||
The head node will always have a null "chain", the tail node
|
||||
will always have a null "enemy".
|
||||
*/
|
||||
|
||||
constexpr size_t TRAIL_LENGTH = 8;
|
||||
|
||||
// places a new entity at the head of the player trail.
|
||||
// the tail entity may be moved to the front if the length
|
||||
// is at the end.
|
||||
static edict_t *PlayerTrail_Spawn(edict_t *owner)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (edict_t *tail = owner->client->trail_tail; tail; tail = tail->chain)
|
||||
len++;
|
||||
|
||||
edict_t *trail;
|
||||
|
||||
// move the tail to the head
|
||||
if (len == TRAIL_LENGTH)
|
||||
{
|
||||
// unlink the old tail
|
||||
trail = owner->client->trail_tail;
|
||||
owner->client->trail_tail = trail->chain;
|
||||
owner->client->trail_tail->enemy = nullptr;
|
||||
trail->chain = trail->enemy = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// spawn a new head
|
||||
trail = G_Spawn();
|
||||
trail->classname = "player_trail";
|
||||
}
|
||||
|
||||
// link as new head
|
||||
if (owner->client->trail_head)
|
||||
owner->client->trail_head->chain = trail;
|
||||
trail->enemy = owner->client->trail_head;
|
||||
owner->client->trail_head = trail;
|
||||
|
||||
// if there's no tail, we become the tail too
|
||||
if (!owner->client->trail_tail)
|
||||
owner->client->trail_tail = trail;
|
||||
|
||||
return trail;
|
||||
}
|
||||
|
||||
// destroys all player trail entities in the map.
|
||||
// we don't want these to stay around across level loads.
|
||||
void PlayerTrail_Destroy(edict_t *player)
|
||||
{
|
||||
for (size_t i = 0; i < globals.num_edicts; i++)
|
||||
if (g_edicts[i].classname && strcmp(g_edicts[i].classname, "player_trail") == 0)
|
||||
if (!player || g_edicts[i].owner == player)
|
||||
G_FreeEdict(&g_edicts[i]);
|
||||
|
||||
if (player)
|
||||
player->client->trail_head = player->client->trail_tail = nullptr;
|
||||
else for (size_t i = 0; i < game.maxclients; i++)
|
||||
game.clients[i].trail_head = game.clients[i].trail_tail = nullptr;
|
||||
}
|
||||
|
||||
// check to see if we can add a new player trail spot
|
||||
// for this player.
|
||||
void PlayerTrail_Add(edict_t *player)
|
||||
{
|
||||
// if we can still see the head, we don't want a new one.
|
||||
if (player->client->trail_head && visible(player, player->client->trail_head))
|
||||
return;
|
||||
// don't spawn trails in intermission, if we're dead, if we're noclipping or not on ground yet
|
||||
else if (level.intermissiontime || player->health <= 0 || player->movetype == MOVETYPE_NOCLIP ||
|
||||
!player->groundentity)
|
||||
return;
|
||||
|
||||
edict_t *trail = PlayerTrail_Spawn(player);
|
||||
trail->s.origin = player->s.old_origin;
|
||||
trail->timestamp = level.time;
|
||||
trail->owner = player;
|
||||
}
|
||||
|
||||
// pick a trail node that matches the player
|
||||
// we're hunting that is visible to us.
|
||||
edict_t *PlayerTrail_Pick(edict_t *self, bool next)
|
||||
{
|
||||
// not player or doesn't have a trail yet
|
||||
if (!self->enemy->client || !self->enemy->client->trail_head)
|
||||
return nullptr;
|
||||
|
||||
// find which marker head that was dropped while we
|
||||
// were searching for this enemy
|
||||
edict_t *marker;
|
||||
|
||||
for (marker = self->enemy->client->trail_head; marker; marker = marker->enemy)
|
||||
{
|
||||
if (marker->timestamp <= self->monsterinfo.trail_time)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (next)
|
||||
{
|
||||
// find the marker we're closest to
|
||||
float closest_dist = std::numeric_limits<float>::infinity();
|
||||
edict_t *closest = nullptr;
|
||||
|
||||
for (edict_t *m2 = marker; m2; m2 = m2->enemy)
|
||||
{
|
||||
float len = (m2->s.origin - self->s.origin).lengthSquared();
|
||||
|
||||
if (len < closest_dist)
|
||||
{
|
||||
closest_dist = len;
|
||||
closest = m2;
|
||||
}
|
||||
}
|
||||
|
||||
// should never happen
|
||||
if (!closest)
|
||||
return nullptr;
|
||||
|
||||
// use the next one from the closest one
|
||||
marker = closest->chain;
|
||||
}
|
||||
else
|
||||
{
|
||||
// from that marker, find the first one we can see
|
||||
for (; marker && !visible(self, marker); marker = marker->enemy)
|
||||
continue;
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
Reference in New Issue
Block a user