Rework g_utils.c and fix the removal of some entity classes

This commit is contained in:
Yamagi Burmeister 2011-10-02 08:33:36 +00:00
parent ce3602c663
commit e9220c468a
2 changed files with 765 additions and 472 deletions

View File

@ -1,244 +1,376 @@
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// g_turret.c
* Copyright (C) 1997-2001 Id Software, Inc.
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Turrets aka big cannons with a driver.
*
* =======================================================================
*/
#include "g_local.h"
void AnglesNormalize(vec3_t vec)
void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
int damage);
void infantry_stand(edict_t *self);
void monster_use(edict_t *self, edict_t *other, edict_t *activator);
qboolean FindTarget(edict_t *self);
void
AnglesNormalize(vec3_t vec)
{
while(vec[0] > 360)
while (vec[0] > 360)
{
vec[0] -= 360;
while(vec[0] < 0)
}
while (vec[0] < 0)
{
vec[0] += 360;
while(vec[1] > 360)
}
while (vec[1] > 360)
{
vec[1] -= 360;
while(vec[1] < 0)
}
while (vec[1] < 0)
{
vec[1] += 360;
}
}
float SnapToEights(float x)
float
SnapToEights(float x)
{
x *= 8.0;
if (x > 0.0)
{
x += 0.5;
}
else
{
x -= 0.5;
}
return 0.125 * (int)x;
}
void turret_blocked(edict_t *self, edict_t *other)
void
turret_blocked(edict_t *self, edict_t *other)
{
edict_t *attacker;
edict_t *attacker;
if (!self || !other)
{
return;
}
if (other->takedamage)
{
if (self->teammaster->owner)
{
attacker = self->teammaster->owner;
}
else
{
attacker = self->teammaster;
T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
}
T_Damage(other, self, attacker, vec3_origin, other->s.origin,
vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
}
}
/*QUAKED turret_breach (0 0 0) ?
This portion of the turret can change both pitch and yaw.
The model should be made with a flat pitch.
It (and the associated base) need to be oriented towards 0.
Use "angle" to set the starting angle.
"speed" default 50
"dmg" default 10
"angle" point this forward
"target" point this at an info_notnull at the muzzle tip
"minpitch" min acceptable pitch angle : default -30
"maxpitch" max acceptable pitch angle : default 30
"minyaw" min acceptable yaw angle : default 0
"maxyaw" max acceptable yaw angle : default 360
*/
void turret_breach_fire (edict_t *self)
/*
* QUAKED turret_breach (0 0 0) ?
* This portion of the turret can change both pitch and yaw.
* The model should be made with a flat pitch.
* It (and the associated base) need to be oriented towards 0.
* Use "angle" to set the starting angle.
*
* "speed" default 50
* "dmg" default 10
* "angle" point this forward
* "target" point this at an info_notnull at the muzzle tip
* "minpitch" min acceptable pitch angle : default -30
* "maxpitch" max acceptable pitch angle : default 30
* "minyaw" min acceptable yaw angle : default 0
* "maxyaw" max acceptable yaw angle : default 360
*/
void
turret_breach_fire(edict_t *self)
{
vec3_t f, r, u;
vec3_t start;
int damage;
int speed;
AngleVectors (self->s.angles, f, r, u);
VectorMA (self->s.origin, self->move_origin[0], f, start);
VectorMA (start, self->move_origin[1], r, start);
VectorMA (start, self->move_origin[2], u, start);
vec3_t f, r, u;
vec3_t start;
int damage;
int speed;
if (!self)
{
return;
}
AngleVectors(self->s.angles, f, r, u);
VectorMA(self->s.origin, self->move_origin[0], f, start);
VectorMA(start, self->move_origin[1], r, start);
VectorMA(start, self->move_origin[2], u, start);
damage = 100 + random() * 50;
speed = 550 + 50 * skill->value;
fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage);
gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
fire_rocket(self->teammaster->owner, start, f, damage, speed, 150, damage);
gi.positioned_sound(start, self, CHAN_WEAPON,
gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
}
void turret_breach_think (edict_t *self)
void
turret_breach_think(edict_t *self)
{
edict_t *ent;
vec3_t current_angles;
vec3_t delta;
VectorCopy (self->s.angles, current_angles);
edict_t *ent;
vec3_t current_angles;
vec3_t delta;
if (!self)
{
return;
}
VectorCopy(self->s.angles, current_angles);
AnglesNormalize(current_angles);
AnglesNormalize(self->move_angles);
if (self->move_angles[PITCH] > 180)
self->move_angles[PITCH] -= 360;
// clamp angles to mins & maxs
if (self->move_angles[PITCH] > self->pos1[PITCH])
self->move_angles[PITCH] = self->pos1[PITCH];
else if (self->move_angles[PITCH] < self->pos2[PITCH])
self->move_angles[PITCH] = self->pos2[PITCH];
if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
{
float dmin, dmax;
dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
if (dmin < -180)
dmin += 360;
else if (dmin > 180)
dmin -= 360;
dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
if (dmax < -180)
dmax += 360;
else if (dmax > 180)
dmax -= 360;
if (fabs(dmin) < fabs(dmax))
self->move_angles[YAW] = self->pos1[YAW];
else
self->move_angles[YAW] = self->pos2[YAW];
self->move_angles[PITCH] -= 360;
}
VectorSubtract (self->move_angles, current_angles, delta);
/* clamp angles to mins & maxs */
if (self->move_angles[PITCH] > self->pos1[PITCH])
{
self->move_angles[PITCH] = self->pos1[PITCH];
}
else if (self->move_angles[PITCH] < self->pos2[PITCH])
{
self->move_angles[PITCH] = self->pos2[PITCH];
}
if ((self->move_angles[YAW] < self->pos1[YAW]) ||
(self->move_angles[YAW] > self->pos2[YAW]))
{
float dmin, dmax;
dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
if (dmin < -180)
{
dmin += 360;
}
else if (dmin > 180)
{
dmin -= 360;
}
dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
if (dmax < -180)
{
dmax += 360;
}
else if (dmax > 180)
{
dmax -= 360;
}
if (fabs(dmin) < fabs(dmax))
{
self->move_angles[YAW] = self->pos1[YAW];
}
else
{
self->move_angles[YAW] = self->pos2[YAW];
}
}
VectorSubtract(self->move_angles, current_angles, delta);
if (delta[0] < -180)
{
delta[0] += 360;
}
else if (delta[0] > 180)
{
delta[0] -= 360;
}
if (delta[1] < -180)
{
delta[1] += 360;
}
else if (delta[1] > 180)
{
delta[1] -= 360;
}
delta[2] = 0;
if (delta[0] > self->speed * FRAMETIME)
{
delta[0] = self->speed * FRAMETIME;
if (delta[0] < -1 * self->speed * FRAMETIME)
delta[0] = -1 * self->speed * FRAMETIME;
if (delta[1] > self->speed * FRAMETIME)
delta[1] = self->speed * FRAMETIME;
if (delta[1] < -1 * self->speed * FRAMETIME)
delta[1] = -1 * self->speed * FRAMETIME;
}
VectorScale (delta, 1.0/FRAMETIME, self->avelocity);
if (delta[0] < -1 * self->speed * FRAMETIME)
{
delta[0] = -1 * self->speed * FRAMETIME;
}
if (delta[1] > self->speed * FRAMETIME)
{
delta[1] = self->speed * FRAMETIME;
}
if (delta[1] < -1 * self->speed * FRAMETIME)
{
delta[1] = -1 * self->speed * FRAMETIME;
}
VectorScale(delta, 1.0 / FRAMETIME, self->avelocity);
self->nextthink = level.time + FRAMETIME;
for (ent = self->teammaster; ent; ent = ent->teamchain)
{
ent->avelocity[1] = self->avelocity[1];
}
// if we have adriver, adjust his velocities
/* if we have adriver, adjust his velocities */
if (self->owner)
{
float angle;
float target_z;
float diff;
vec3_t target;
vec3_t dir;
float angle;
float target_z;
float diff;
vec3_t target;
vec3_t dir;
// angular is easy, just copy ours
/* angular is easy, just copy ours */
self->owner->avelocity[0] = self->avelocity[0];
self->owner->avelocity[1] = self->avelocity[1];
// x & y
/* x & y */
angle = self->s.angles[1] + self->owner->move_origin[1];
angle *= (M_PI*2 / 360);
target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
angle *= (M_PI * 2 / 360);
target[0] = SnapToEights(self->s.origin[0] + cos(
angle) * self->owner->move_origin[0]);
target[1] = SnapToEights(self->s.origin[1] + sin(
angle) * self->owner->move_origin[0]);
target[2] = self->owner->s.origin[2];
VectorSubtract (target, self->owner->s.origin, dir);
VectorSubtract(target, self->owner->s.origin, dir);
self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
// z
angle = self->s.angles[PITCH] * (M_PI*2 / 360);
target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
/* z */
angle = self->s.angles[PITCH] * (M_PI * 2 / 360);
target_z = SnapToEights(
self->s.origin[2] + self->owner->move_origin[0] * tan(
angle) + self->owner->move_origin[2]);
diff = target_z - self->owner->s.origin[2];
self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
if (self->spawnflags & 65536)
{
turret_breach_fire (self);
turret_breach_fire(self);
self->spawnflags &= ~65536;
}
}
}
void turret_breach_finish_init (edict_t *self)
{
// get and save info for muzzle location
void
turret_breach_finish_init(edict_t *self)
{
if (!self)
{
return;
}
/* get and save info for muzzle location */
if (!self->target)
{
gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
gi.dprintf("%s at %s needs a target\n", self->classname,
vtos(self->s.origin));
}
else
{
self->target_ent = G_PickTarget (self->target);
VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin);
self->target_ent = G_PickTarget(self->target);
VectorSubtract(self->target_ent->s.origin,
self->s.origin, self->move_origin);
G_FreeEdict(self->target_ent);
}
self->teammaster->dmg = self->dmg;
self->think = turret_breach_think;
self->think (self);
self->think(self);
}
void SP_turret_breach (edict_t *self)
{
void
SP_turret_breach(edict_t *self)
{
if (!self)
{
return;
}
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
gi.setmodel (self, self->model);
gi.setmodel(self, self->model);
if (!self->speed)
{
self->speed = 50;
}
if (!self->dmg)
{
self->dmg = 10;
}
if (!st.minpitch)
{
st.minpitch = -30;
}
if (!st.maxpitch)
{
st.maxpitch = 30;
}
if (!st.maxyaw)
{
st.maxyaw = 360;
}
self->pos1[PITCH] = -1 * st.minpitch;
self->pos1[YAW] = st.minyaw;
self->pos1[YAW] = st.minyaw;
self->pos2[PITCH] = -1 * st.maxpitch;
self->pos2[YAW] = st.maxyaw;
self->pos2[YAW] = st.maxyaw;
self->ideal_yaw = self->s.angles[YAW];
self->move_angles[YAW] = self->ideal_yaw;
@ -247,44 +379,56 @@ void SP_turret_breach (edict_t *self)
self->think = turret_breach_finish_init;
self->nextthink = level.time + FRAMETIME;
gi.linkentity (self);
gi.linkentity(self);
}
/*
* QUAKED turret_base (0 0 0) ?
* This portion of the turret changes yaw only.
* MUST be teamed with a turret_breach.
*/
/*QUAKED turret_base (0 0 0) ?
This portion of the turret changes yaw only.
MUST be teamed with a turret_breach.
*/
void SP_turret_base (edict_t *self)
{
void
SP_turret_base(edict_t *self)
{
if (!self)
{
return;
}
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
gi.setmodel (self, self->model);
gi.setmodel(self, self->model);
self->blocked = turret_blocked;
gi.linkentity (self);
gi.linkentity(self);
}
/*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
Must NOT be on the team with the rest of the turret parts.
Instead it must target the turret_breach.
*/
void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage);
void infantry_stand (edict_t *self);
void monster_use (edict_t *self, edict_t *other, edict_t *activator);
void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
/*
* QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
* Must NOT be on the team with the rest of the turret parts.
* Instead it must target the turret_breach.
*/
void
turret_driver_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
int damage, vec3_t point /* unused */)
{
edict_t *ent;
// level the gun
edict_t *ent;
if (!self || !inflictor || !attacker)
{
return;
}
/* level the gun */
self->target_ent->move_angles[0] = 0;
// remove the driver from the end of them team chain
for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
;
/* remove the driver from the end of them team chain */
for (ent = self->target_ent->teammaster;
ent->teamchain != self;
ent = ent->teamchain)
{
}
ent->teamchain = NULL;
self->teammaster = NULL;
self->flags &= ~FL_TEAMSLAVE;
@ -292,32 +436,41 @@ void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, in
self->target_ent->owner = NULL;
self->target_ent->teammaster->owner = NULL;
infantry_die (self, inflictor, attacker, damage);
infantry_die(self, inflictor, attacker, damage);
}
qboolean FindTarget (edict_t *self);
void turret_driver_think (edict_t *self)
void
turret_driver_think(edict_t *self)
{
vec3_t target;
vec3_t dir;
float reaction_time;
vec3_t target;
vec3_t dir;
float reaction_time;
if (!self)
{
return;
}
self->nextthink = level.time + FRAMETIME;
if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
if (self->enemy && (!self->enemy->inuse || (self->enemy->health <= 0)))
{
self->enemy = NULL;
}
if (!self->enemy)
{
if (!FindTarget (self))
if (!FindTarget(self))
{
return;
}
self->monsterinfo.trail_time = level.time;
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
}
else
{
if (visible (self, self->enemy))
if (visible(self, self->enemy))
{
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
{
@ -332,71 +485,91 @@ void turret_driver_think (edict_t *self)
}
}
// let the turret know where we want it to aim
VectorCopy (self->enemy->s.origin, target);
/* let the turret know where we want it to aim */
VectorCopy(self->enemy->s.origin, target);
target[2] += self->enemy->viewheight;
VectorSubtract (target, self->target_ent->s.origin, dir);
vectoangles (dir, self->target_ent->move_angles);
VectorSubtract(target, self->target_ent->s.origin, dir);
vectoangles(dir, self->target_ent->move_angles);
// decide if we should shoot
/* decide if we should shoot */
if (level.time < self->monsterinfo.attack_finished)
{
return;
}
reaction_time = (3 - skill->value) * 1.0;
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
{
return;
}
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
//FIXME how do we really want to pass this along?
self->target_ent->spawnflags |= 65536;
}
void turret_driver_link (edict_t *self)
void
turret_driver_link(edict_t *self)
{
vec3_t vec;
edict_t *ent;
vec3_t vec;
edict_t *ent;
if (!self)
{
return;
}
self->think = turret_driver_think;
self->nextthink = level.time + FRAMETIME;
self->target_ent = G_PickTarget (self->target);
self->target_ent = G_PickTarget(self->target);
self->target_ent->owner = self;
self->target_ent->teammaster->owner = self;
VectorCopy (self->target_ent->s.angles, self->s.angles);
VectorCopy(self->target_ent->s.angles, self->s.angles);
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
vec[2] = 0;
self->move_origin[0] = VectorLength(vec);
VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
vectoangles (vec, vec);
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
vectoangles(vec, vec);
AnglesNormalize(vec);
self->move_origin[1] = vec[1];
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
// add the driver to the end of them team chain
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
;
/* add the driver to the end of them team chain */
for (ent = self->target_ent->teammaster;
ent->teamchain;
ent = ent->teamchain)
{
}
ent->teamchain = self;
self->teammaster = self->target_ent->teammaster;
self->flags |= FL_TEAMSLAVE;
}
void SP_turret_driver (edict_t *self)
{
void
SP_turret_driver(edict_t *self)
{
if (!self)
{
return;
}
if (deathmatch->value)
{
G_FreeEdict (self);
G_FreeEdict(self);
return;
}
self->movetype = MOVETYPE_PUSH;
self->solid = SOLID_BBOX;
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
VectorSet (self->mins, -16, -16, -24);
VectorSet (self->maxs, 16, 16, 32);
VectorSet(self->mins, -16, -16, -24);
VectorSet(self->maxs, 16, 16, 32);
self->health = 100;
self->gib_health = 0;
@ -415,19 +588,23 @@ void SP_turret_driver (edict_t *self)
self->takedamage = DAMAGE_AIM;
self->use = monster_use;
self->clipmask = MASK_MONSTERSOLID;
VectorCopy (self->s.origin, self->s.old_origin);
self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED;
VectorCopy(self->s.origin, self->s.old_origin);
self->monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED;
if (st.item)
{
self->item = FindItemByClassname (st.item);
self->item = FindItemByClassname(st.item);
if (!self->item)
gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
{
gi.dprintf("%s at %s has bad item: %s\n", self->classname,
vtos(self->s.origin), st.item);
}
}
self->think = turret_driver_link;
self->nextthink = level.time + FRAMETIME;
gi.linkentity (self);
gi.linkentity(self);
}

View File

@ -1,125 +1,158 @@
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// g_utils.c -- misc utility functions for game module
* Copyright (C) 1997-2001 Id Software, Inc.
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Misc. utility functions for the game logic.
*
* =======================================================================
*/
#include "g_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];
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++)
if (!match)
{
return NULL;
}
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)
{
@ -127,14 +160,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)
@ -146,75 +186,103 @@ edict_t *G_PickTarget (char *targetname)
return choice[rand() % num_choices];
}
void Think_Delay (edict_t *ent)
{
G_UseTargets (ent, ent->activator);
G_FreeEdict (ent);
void
Think_Delay(edict_t *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;
//
// check for a delay
//
edict_t *t;
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);
/* decrement secret count if target_secret is removed */
if (!Q_stricmp(t->classname,"target_secret"))
{
level.total_secrets--;
}
/* same deal with target_goal, but also turn off CD music if applicable */
else if (!Q_stricmp(t->classname,"target_goal"))
{
level.total_goals--;
if (level.found_goals >= level.total_goals)
{
gi.configstring (CS_CDTRACK, "0");
}
}
G_FreeEdict(t);
if (!ent->inuse)
{
gi.dprintf("entity was removed while using killtargets\n");
@ -223,28 +291,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");
@ -254,25 +327,22 @@ 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;
@ -281,106 +351,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;
@ -388,17 +479,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";
@ -407,159 +499,183 @@ 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 s
* hould 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 */
}