Cleanup g_utils.c and add sanity checks

This commit is contained in:
Yamagi Burmeister 2013-01-05 16:56:44 +01:00
parent b6b88565ef
commit a96616d85f

View file

@ -1,106 +1,127 @@
// g_utils.c -- misc utility functions for game module
/* =======================================================================
*
* Misc. utility functions for the game logic.
*
* =======================================================================
*/
#include "header/local.h"
#define MAXCHOICES 8
void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
void
G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward,
vec3_t right, vec3_t result)
{
result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
}
/*
=============
G_Find
Searches all active entities for the next one that holds
the matching string at fieldofs (use the FOFS() macro) in the structure.
Searches beginning at the edict after from, or the beginning if NULL
NULL will be returned if the end of the list is reached.
=============
*/
edict_t *G_Find (edict_t *from, int fieldofs, char *match)
* Searches all active entities for the next one that holds
* the matching string at fieldofs (use the FOFS() macro) in the structure.
*
* Searches beginning at the edict after from, or the beginning if NULL
* NULL will be returned if the end of the list is reached.
*/
edict_t *
G_Find(edict_t *from, int fieldofs, char *match)
{
char *s;
char *s;
if (!from)
{
from = g_edicts;
}
else
{
from++;
}
for ( ; from < &g_edicts[globals.num_edicts] ; from++)
for ( ; from < &g_edicts[globals.num_edicts]; from++)
{
if (!from->inuse)
{
continue;
s = *(char **) ((byte *)from + fieldofs);
}
s = *(char **)((byte *)from + fieldofs);
if (!s)
{
continue;
if (!Q_stricmp (s, match))
}
if (!Q_stricmp(s, match))
{
return from;
}
}
return NULL;
}
/*
=================
findradius
Returns entities that have origins within a spherical area
findradius (origin, radius)
=================
*/
edict_t *findradius (edict_t *from, vec3_t org, float rad)
* Returns entities that have origins within a spherical area
*/
edict_t *
findradius(edict_t *from, vec3_t org, float rad)
{
vec3_t eorg;
int j;
vec3_t eorg;
int j;
if (!from)
{
from = g_edicts;
}
else
{
from++;
}
for ( ; from < &g_edicts[globals.num_edicts]; from++)
{
if (!from->inuse)
{
continue;
}
if (from->solid == SOLID_NOT)
{
continue;
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
}
for (j = 0; j < 3; j++)
{
eorg[j] = org[j] - (from->s.origin[j] +
(from->mins[j] + from->maxs[j]) * 0.5);
}
if (VectorLength(eorg) > rad)
{
continue;
}
return from;
}
return NULL;
}
/*
=============
G_PickTarget
Searches all active entities for the next one that holds
the matching string at fieldofs (use the FOFS() macro) in the structure.
Searches beginning at the edict after from, or the beginning if NULL
NULL will be returned if the end of the list is reached.
=============
*/
#define MAXCHOICES 8
edict_t *G_PickTarget (char *targetname)
* Searches all active entities for the next one that holds
* the matching string at fieldofs (use the FOFS() macro) in the structure.
*
* Searches beginning at the edict after from, or the beginning if NULL
* NULL will be returned if the end of the list is reached.
*/
edict_t *
G_PickTarget(char *targetname)
{
edict_t *ent = NULL;
int num_choices = 0;
edict_t *choice[MAXCHOICES];
edict_t *ent = NULL;
int num_choices = 0;
edict_t *choice[MAXCHOICES];
if (!targetname)
{
@ -108,14 +129,21 @@ edict_t *G_PickTarget (char *targetname)
return NULL;
}
while(1)
while (1)
{
ent = G_Find (ent, FOFS(targetname), targetname);
ent = G_Find(ent, FOFS(targetname), targetname);
if (!ent)
{
break;
}
choice[num_choices++] = ent;
if (num_choices == MAXCHOICES)
{
break;
}
}
if (!num_choices)
@ -127,75 +155,85 @@ edict_t *G_PickTarget (char *targetname)
return choice[rand() % num_choices];
}
void Think_Delay (edict_t *ent)
void
Think_Delay(edict_t *ent)
{
G_UseTargets (ent, ent->activator);
G_FreeEdict (ent);
if (!ent)
{
return;
}
G_UseTargets(ent, ent->activator);
G_FreeEdict(ent);
}
/*
==============================
G_UseTargets
the global "activator" should be set to the entity that initiated the firing.
If self.delay is set, a DelayedUse entity will be created that will actually
do the SUB_UseTargets after that many seconds have passed.
Centerprints any self.message to the activator.
Search for (string)targetname in all entities that
match (string)self.target and call their .use function
==============================
*/
void G_UseTargets (edict_t *ent, edict_t *activator)
* the global "activator" should be set to the entity that initiated the firing.
*
* If self.delay is set, a DelayedUse entity will be created that will actually
* do the SUB_UseTargets after that many seconds have passed.
*
* Centerprints any self.message to the activator.
*
* Search for (string)targetname in all entities that
* match (string)self.target and call their .use function
*/
void
G_UseTargets(edict_t *ent, edict_t *activator)
{
edict_t *t;
edict_t *t;
//
// check for a delay
//
if (!ent || !activator)
{
return;
}
/* check for a delay */
if (ent->delay)
{
// create a temp object to fire at a later time
/* create a temp object to fire at a later time */
t = G_Spawn();
t->classname = "DelayedUse";
t->nextthink = level.time + ent->delay;
t->think = Think_Delay;
t->activator = activator;
if (!activator)
gi.dprintf ("Think_Delay with no activator\n");
{
gi.dprintf("Think_Delay with no activator\n");
}
t->message = ent->message;
t->target = ent->target;
t->killtarget = ent->killtarget;
return;
}
//
// print the message
//
/* print the message */
if ((ent->message) && !(activator->svflags & SVF_MONSTER))
{
gi.centerprintf (activator, "%s", ent->message);
gi.centerprintf(activator, "%s", ent->message);
if (ent->noise_index)
gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
{
gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
}
else
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
{
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"),
1, ATTN_NORM, 0);
}
}
//
// kill killtargets
//
/* kill killtargets */
if (ent->killtarget)
{
t = NULL;
while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
while ((t = G_Find(t, FOFS(targetname), ent->killtarget)))
{
G_FreeEdict (t);
G_FreeEdict(t);
if (!ent->inuse)
{
gi.dprintf("entity was removed while using killtargets\n");
@ -204,28 +242,33 @@ void G_UseTargets (edict_t *ent, edict_t *activator)
}
}
//
// fire targets
//
/* fire targets */
if (ent->target)
{
t = NULL;
while ((t = G_Find (t, FOFS(targetname), ent->target)))
while ((t = G_Find(t, FOFS(targetname), ent->target)))
{
// doors fire area portals in a specific way
/* doors fire area portals in a specific way */
if (!Q_stricmp(t->classname, "func_areaportal") &&
(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
(!Q_stricmp(ent->classname, "func_door") ||
!Q_stricmp(ent->classname, "func_door_rotating")))
{
continue;
}
if (t == ent)
{
gi.dprintf ("WARNING: Entity used itself.\n");
gi.dprintf("WARNING: Entity used itself.\n");
}
else
{
if (t->use)
t->use (t, ent, activator);
{
t->use(t, ent, activator);
}
}
if (!ent->inuse)
{
gi.dprintf("entity was removed while using targets\n");
@ -235,25 +278,21 @@ void G_UseTargets (edict_t *ent, edict_t *activator)
}
}
/*
=============
TempVector
This is just a convenience function
for making temporary vectors for function calls
=============
*/
float *tv (float x, float y, float z)
* This is just a convenience function
* for making temporary vectors for function calls
*/
float *
tv(float x, float y, float z)
{
static int index;
static vec3_t vecs[8];
float *v;
static int index;
static vec3_t vecs[8];
float *v;
// use an array so that multiple tempvectors won't collide
// for a while
/* use an array so that multiple tempvectors
won't collide for a while */
v = vecs[index];
index = (index + 1)&7;
index = (index + 1) & 7;
v[0] = x;
v[1] = y;
@ -262,106 +301,127 @@ float *tv (float x, float y, float z)
return v;
}
/*
=============
VectorToString
This is just a convenience function
for printing vectors
=============
*/
char *vtos (vec3_t v)
* This is just a convenience function
* for printing vectors
*/
char *
vtos(vec3_t v)
{
static int index;
static char str[8][32];
char *s;
static int index;
static char str[8][32];
char *s;
// use an array so that multiple vtos won't collide
/* use an array so that multiple vtos won't collide */
s = str[index];
index = (index + 1)&7;
index = (index + 1) & 7;
Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
Com_sprintf(s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
return s;
}
vec3_t VEC_UP = {0, -1, 0};
vec3_t MOVEDIR_UP = {0, 0, 1};
vec3_t VEC_DOWN = {0, -2, 0};
vec3_t MOVEDIR_DOWN = {0, 0, -1};
vec3_t VEC_UP = {0, -1, 0};
vec3_t MOVEDIR_UP = {0, 0, 1};
vec3_t VEC_DOWN = {0, -2, 0};
vec3_t MOVEDIR_DOWN = {0, 0, -1};
void G_SetMovedir (vec3_t angles, vec3_t movedir)
void
G_SetMovedir(vec3_t angles, vec3_t movedir)
{
if (VectorCompare (angles, VEC_UP))
if (VectorCompare(angles, VEC_UP))
{
VectorCopy (MOVEDIR_UP, movedir);
VectorCopy(MOVEDIR_UP, movedir);
}
else if (VectorCompare (angles, VEC_DOWN))
else if (VectorCompare(angles, VEC_DOWN))
{
VectorCopy (MOVEDIR_DOWN, movedir);
VectorCopy(MOVEDIR_DOWN, movedir);
}
else
{
AngleVectors (angles, movedir, NULL, NULL);
AngleVectors(angles, movedir, NULL, NULL);
}
VectorClear (angles);
VectorClear(angles);
}
float vectoyaw (vec3_t vec)
float
vectoyaw(vec3_t vec)
{
float yaw;
if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
float yaw;
if (vec[PITCH] == 0)
{
yaw = 0;
if (vec[YAW] > 0)
{
yaw = 90;
}
else if (vec[YAW] < 0)
{
yaw = -90;
}
}
}
else
{
yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
yaw = (int)(atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
if (yaw < 0)
{
yaw += 360;
}
}
return yaw;
}
void vectoangles (vec3_t value1, vec3_t angles)
void
vectoangles(vec3_t value1, vec3_t angles)
{
float forward;
float yaw, pitch;
if (value1[1] == 0 && value1[0] == 0)
float forward;
float yaw, pitch;
if ((value1[1] == 0) && (value1[0] == 0))
{
yaw = 0;
if (value1[2] > 0)
{
pitch = 90;
}
else
{
pitch = 270;
}
}
else
{
if (value1[0])
yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
{
yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI);
}
else if (value1[1] > 0)
{
yaw = 90;
}
else
{
yaw = -90;
if (yaw < 0)
yaw += 360;
}
if (yaw < 0)
{
yaw += 360;
}
forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
pitch = (int)(atan2(value1[2], forward) * 180 / M_PI);
forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
if (pitch < 0)
{
pitch += 360;
}
}
angles[PITCH] = -pitch;
@ -369,17 +429,18 @@ void vectoangles (vec3_t value1, vec3_t angles)
angles[ROLL] = 0;
}
char *G_CopyString (char *in)
char *
G_CopyString(char *in)
{
char *out;
out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
strcpy (out, in);
char *out;
out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL);
strcpy(out, in);
return out;
}
void G_InitEdict (edict_t *e)
void
G_InitEdict(edict_t *e)
{
e->inuse = true;
e->classname = "noclass";
@ -388,159 +449,175 @@ void G_InitEdict (edict_t *e)
}
/*
=================
G_Spawn
Either finds a free edict, or allocates a new one.
Try to avoid reusing an entity that was recently freed, because it
can cause the client to think the entity morphed into something else
instead of being removed and recreated, which can cause interpolated
angles and bad trails.
=================
*/
edict_t *G_Spawn (void)
* Either finds a free edict, or allocates a new one.
* Try to avoid reusing an entity that was recently freed, because it
* can cause the client to think the entity morphed into something else
* instead of being removed and recreated, which can cause interpolated
* angles and bad trails.
*/
edict_t *
G_Spawn(void)
{
int i;
edict_t *e;
int i;
edict_t *e;
e = &g_edicts[(int)maxclients->value+1];
for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
e = &g_edicts[(int)maxclients->value + 1];
for (i = maxclients->value + 1; i < globals.num_edicts; i++, e++)
{
// the first couple seconds of server time can involve a lot of
// freeing and allocating, so relax the replacement policy
if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
/* the first couple seconds of server time can involve a lot of
freeing and allocating, so relax the replacement policy */
if (!e->inuse && ((e->freetime < 2) || (level.time - e->freetime > 0.5)))
{
G_InitEdict (e);
G_InitEdict(e);
return e;
}
}
if (i == game.maxentities)
gi.error ("ED_Alloc: no free edicts");
{
gi.error("ED_Alloc: no free edicts");
}
globals.num_edicts++;
G_InitEdict (e);
G_InitEdict(e);
return e;
}
/*
=================
G_FreeEdict
Marks the edict as free
=================
*/
void G_FreeEdict (edict_t *ed)
* Marks the edict as free
*/
void
G_FreeEdict(edict_t *ed)
{
gi.unlinkentity (ed); // unlink from world
gi.unlinkentity(ed); /* unlink from world */
if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
{
return;
}
memset (ed, 0, sizeof(*ed));
memset(ed, 0, sizeof(*ed));
ed->classname = "freed";
ed->freetime = level.time;
ed->inuse = false;
}
/*
============
G_TouchTriggers
============
*/
void G_TouchTriggers (edict_t *ent)
void
G_TouchTriggers(edict_t *ent)
{
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
// dead things don't activate triggers!
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
if (!ent)
{
return;
}
/* dead things don't activate triggers! */
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
{
return;
}
num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
, MAX_EDICTS, AREA_TRIGGERS);
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch,
MAX_EDICTS, AREA_TRIGGERS);
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for (i=0 ; i<num ; i++)
/* be careful, it is possible to have an entity in this
list removed before we get to it (killtriggered) */
for (i = 0; i < num; i++)
{
hit = touch[i];
if (!hit->inuse)
{
continue;
}
if (!hit->touch)
{
continue;
hit->touch (hit, ent, NULL, NULL);
}
hit->touch(hit, ent, NULL, NULL);
}
}
/*
============
G_TouchSolids
Call after linking a new trigger in during gameplay
to force all entities it covers to immediately touch it
============
*/
void G_TouchSolids (edict_t *ent)
* Call after linking a new trigger in during gameplay
* to force all entities it covers to immediately touch it
*/
void
G_TouchSolids(edict_t *ent)
{
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
if (!ent)
{
return;
}
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch,
MAX_EDICTS, AREA_SOLID);
num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
, MAX_EDICTS, AREA_SOLID);
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for (i=0 ; i<num ; i++)
/* be careful, it is possible to have an entity in this
list removed before we get to it (killtriggered) */
for (i = 0; i < num; i++)
{
hit = touch[i];
if (!hit->inuse)
{
continue;
}
if (ent->touch)
ent->touch (hit, ent, NULL, NULL);
{
ent->touch(hit, ent, NULL, NULL);
}
if (!ent->inuse)
{
break;
}
}
}
/*
==============================================================================
Kill box
==============================================================================
*/
/*
=================
KillBox
Kills all entities that would touch the proposed new positioning
of ent. Ent should be unlinked before calling this!
=================
*/
qboolean KillBox (edict_t *ent)
* Kills all entities that would touch the proposed new positioning
* of ent. Ent should be unlinked before calling this!
*/
qboolean
KillBox(edict_t *ent)
{
trace_t tr;
trace_t tr;
if (!ent)
{
return false;
}
while (1)
{
tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin,
NULL, MASK_PLAYERSOLID);
if (!tr.ent)
{
break;
}
// nail it
T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
/* nail it */
T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin,
100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
// if we didn't kill it, fail
/* if we didn't kill it, fail */
if (tr.ent->solid)
{
return false;
}
}
return true; // all clear
return true; /* all clear */
}