#include "g_local.h"
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];
}
edict_t *G_Find(int cmodel_index, edict_t *from, int fieldofs, char *match) {
char *s;
if(!from)
from = g_edicts;
else
from++;
for(; from < &g_edicts[globals.num_edicts]; from++) {
if(!from->inuse)
continue;
if(cmodel_index < CMODEL_COUNT && from->s.cmodel_index != cmodel_index)
continue;
s = *(char **)((byte *)from + fieldofs);
if(!s)
continue;
if(!Q_stricmp(s, match))
return from;
}
return NULL;
}
edict_t *findradius(edict_t *from, vec3_t org, float rad) {
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);
if(VectorLength(eorg) > rad)
continue;
return from;
}
return NULL;
}
#define MAXCHOICES 8
edict_t *G_PickTarget(int cmodel_index, char *targetname) {
edict_t *ent = NULL;
int num_choices = 0;
edict_t *choice[MAXCHOICES];
if(!targetname) {
gi.dprintf("G_PickTarget called with NULL targetname\n");
return NULL;
}
while(1) {
ent = G_Find(cmodel_index, ent, FOFS(targetname), targetname);
if(!ent)
break;
choice[num_choices++] = ent;
if(num_choices == MAXCHOICES)
break;
}
if(!num_choices) {
gi.dprintf("G_PickTarget: target %s not found\n", targetname);
return NULL;
}
return choice[rand() % num_choices];
}
void Think_Delay(edict_t *ent) {
G_UseTargets(ent, ent->activator);
G_FreeEdict(ent);
}
void G_UseTargets(edict_t *ent, edict_t *activator) {
edict_t *t;
if(ent->delay) {
t = G_Spawn(ent->s.cmodel_index);
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");
t->message = ent->message;
t->target = ent->target;
t->killtarget = ent->killtarget;
return;
}
if((ent->message) && !(activator->svflags & SVF_MONSTER)) {
gi.centerprintf(activator, "%s", ent->message);
if(ent->noise_index)
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);
}
if(ent->killtarget) {
t = NULL;
while((t = G_Find(ent->s.cmodel_index, t, FOFS(targetname), ent->killtarget))) {
G_FreeEdict(t);
if(!ent->inuse) {
gi.dprintf("entity was removed while using killtargets\n");
return;
}
}
}
if(ent->target) {
t = NULL;
while((t = G_Find(ent->s.cmodel_index, t, FOFS(targetname), ent->target))) {
if(!Q_stricmp(t->classname, "func_areaportal") &&
(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
continue;
if(t == ent) {
gi.dprintf("WARNING: Entity used itself.\n");
} else {
if(t->use)
t->use(t, ent, activator);
}
if(!ent->inuse) {
gi.dprintf("entity was removed while using targets\n");
return;
}
}
}
}
float *tv(float x, float y, float z) {
static int index;
static vec3_t vecs[8];
float *v;
v = vecs[index];
index = (index + 1) & 7;
v[0] = x;
v[1] = y;
v[2] = z;
return v;
}
char *vtos(vec3_t v) {
static int index;
static char str[8][32];
char *s;
s = str[index];
index = (index + 1) & 7;
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};
void G_SetMovedir(vec3_t angles, vec3_t movedir) {
if(VectorCompare(angles, VEC_UP)) {
VectorCopy(MOVEDIR_UP, movedir);
} else if(VectorCompare(angles, VEC_DOWN)) {
VectorCopy(MOVEDIR_DOWN, movedir);
} else {
AngleVectors(angles, movedir, NULL, NULL);
}
VectorClear(angles);
}
float vectoyaw(vec3_t vec) {
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);
if(yaw < 0)
yaw += 360;
}
return yaw;
}
void vectoangles(vec3_t value1, vec3_t angles) {
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);
else if(value1[1] > 0)
yaw = 90;
else
yaw = -90;
if(yaw < 0)
yaw += 360;
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;
angles[YAW] = yaw;
angles[ROLL] = 0;
}
char *G_CopyString(char *in) {
char *out;
out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL);
strcpy(out, in);
return out;
}
void G_InitEdict(int cmodel_index, edict_t *e) {
e->inuse = true;
e->classname = "noclass";
e->gravity = 1.0;
e->s.number = e - g_edicts;
e->s.cmodel_index = cmodel_index;
if(e->entity_handle == 0 &&
alias_ecs_spawn(
GAME_ECS_INSTANCE(),
&(alias_ecs_EntitySpawnInfo){
.count = 1,
.num_components = 1,
.layer = GAME_MAIN_LAYER(cmodel_index),
.components = &(alias_ecs_EntitySpawnComponent){.component = GAME_EDICT_COMPONENT_HANDLE(), .data = &e}},
&e->entity_handle) != ALIAS_ECS_SUCCESS) {
gi.error("failed to spawn alias ECS entity with edict\n");
}
}
edict_t *G_Spawn(int cmodel_index) {
int i;
edict_t *e;
e = &g_edicts[(int)maxclients->value + 1];
for(i = maxclients->value + 1; i < globals.num_edicts; i++, e++) {
if(!e->inuse && (e->freetime < 2 || level.time - e->freetime > 0.5)) {
G_InitEdict(cmodel_index, e);
return e;
}
}
if(i == game.maxentities)
gi.error("ED_Alloc: no free edicts");
globals.num_edicts++;
G_InitEdict(cmodel_index, e);
return e;
}
void G_FreeEdict(edict_t *ed) {
gi.unlinkentity(ed);
if((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE)) {
return;
}
if(alias_ecs_despawn(GAME_ECS_INSTANCE(), 1, &ed->entity_handle) != ALIAS_ECS_SUCCESS) {
gi.error("failed to despawn alias ECS entity with edict\n");
}
memset(ed, 0, sizeof(*ed));
ed->classname = "freed";
ed->freetime = level.time;
ed->inuse = false;
}
void G_TouchTriggers(edict_t *ent) {
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
if((ent->client || (ent->svflags & SVF_MONSTER)) && (ent_read_health(ent)->value <= 0))
return;
num = gi.BoxEdicts(ent->s.cmodel_index, ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_TRIGGERS);
for(i = 0; i < num; i++) {
hit = touch[i];
if(!hit->inuse)
continue;
if(!hit->touch)
continue;
hit->touch(hit, ent, NULL, NULL);
}
}
void G_TouchSolids(edict_t *ent) {
int i, num;
edict_t *touch[MAX_EDICTS], *hit;
num = gi.BoxEdicts(ent->s.cmodel_index, ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID);
for(i = 0; i < num; i++) {
hit = touch[i];
if(!hit->inuse)
continue;
if(ent->touch)
ent->touch(hit, ent, NULL, NULL);
if(!ent->inuse)
break;
}
}
bool KillBox(edict_t *ent) {
trace_t tr;
while(1) {
tr = gi.trace(ent->s.cmodel_index, ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
if(!tr.ent)
break;
T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
if(tr.ent->solid)
return false;
}
return true; }