1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "g_local.h"
23
24
G_ProjectSource(vec3_t point,vec3_t distance,vec3_t forward,vec3_t right,vec3_t result)25 void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result){
26 result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
27 result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
28 result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
29 }
30
31
32 /*
33 G_Find
34
35 Searches all active entities for the next one that holds
36 the matching string at fieldofs(use the FOFS() macro) in the structure.
37
38 Searches beginning at the edict after from, or the beginning if NULL
39 NULL will be returned if the end of the list is reached.
40
41 */
G_Find(edict_t * from,int fieldofs,char * match)42 edict_t *G_Find(edict_t *from, int fieldofs, char *match){
43 char *s;
44
45 if(!from)
46 from = g_edicts;
47 else
48 from++;
49
50 for(; from < &g_edicts[globals.num_edicts]; from++){
51 if(!from->inuse)
52 continue;
53 s = *(char **)((byte *)from + fieldofs);
54 if(!s)
55 continue;
56 if(!Q_stricmp(s, match))
57 return from;
58 }
59
60 return NULL;
61 }
62
63
64 /*
65 findradius
66
67 Returns entities that have origins within a spherical area
68
69 findradius(origin, radius)
70 */
findradius(edict_t * from,vec3_t org,float rad)71 edict_t *findradius(edict_t *from, vec3_t org, float rad){
72 vec3_t eorg;
73 int j;
74
75 if(!from)
76 from = g_edicts;
77 else
78 from++;
79 for(; from < &g_edicts[globals.num_edicts]; from++){
80 if(!from->inuse)
81 continue;
82 if(from->solid == SOLID_NOT)
83 continue;
84 for(j = 0; j < 3; j++)
85 eorg[j] = org[j] -(from->s.origin[j] +(from->mins[j] + from->maxs[j]) * 0.5);
86 if(VectorLength(eorg) > rad)
87 continue;
88 return from;
89 }
90
91 return NULL;
92 }
93
94
95 /*
96 G_PickTarget
97
98 Searches all active entities for the next one that holds
99 the matching string at fieldofs(use the FOFS() macro) in the structure.
100
101 Searches beginning at the edict after from, or the beginning if NULL
102 NULL will be returned if the end of the list is reached.
103
104 */
105 #define MAXCHOICES 8
106
G_PickTarget(char * targetname)107 edict_t *G_PickTarget(char *targetname){
108 edict_t *ent = NULL;
109 int num_choices = 0;
110 edict_t *choice[MAXCHOICES];
111
112 if(!targetname){
113 gi.dprintf("G_PickTarget called with NULL targetname\n");
114 return NULL;
115 }
116
117 while(1){
118 ent = G_Find(ent, FOFS(targetname), targetname);
119 if(!ent)
120 break;
121 choice[num_choices++] = ent;
122 if(num_choices == MAXCHOICES)
123 break;
124 }
125
126 if(!num_choices){
127 gi.dprintf("G_PickTarget: target %s not found\n", targetname);
128 return NULL;
129 }
130
131 return choice[rand() % num_choices];
132 }
133
134
135
Think_Delay(edict_t * ent)136 void Think_Delay(edict_t *ent){
137 G_UseTargets(ent, ent->activator);
138 G_FreeEdict(ent);
139 }
140
141 /*
142 G_UseTargets
143
144 the global "activator" should be set to the entity that initiated the firing.
145
146 If self.delay is set, a DelayedUse entity will be created that will actually
147 do the SUB_UseTargets after that many seconds have passed.
148
149 Centerprints any self.message to the activator.
150
151 Search for(string)targetname in all entities that
152 match(string)self.target and call their .use function
153
154 */
G_UseTargets(edict_t * ent,edict_t * activator)155 void G_UseTargets(edict_t *ent, edict_t *activator){
156 edict_t *t;
157
158 //
159 // check for a delay
160 //
161 if(ent->delay){
162 // create a temp object to fire at a later time
163 t = G_Spawn();
164 t->classname = "DelayedUse";
165 t->nextthink = level.time + ent->delay;
166 t->think = Think_Delay;
167 t->activator = activator;
168 if(!activator)
169 gi.dprintf("Think_Delay with no activator\n");
170 t->message = ent->message;
171 t->target = ent->target;
172 t->killtarget = ent->killtarget;
173 return;
174 }
175
176
177 //
178 // print the message
179 //
180 if((ent->message) && !(activator->svflags & SVF_MONSTER)){
181 gi.centerprintf(activator, "%s", ent->message);
182 if(ent->noise_index)
183 gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
184 else
185 gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
186 }
187
188 //
189 // kill killtargets
190 //
191 if(ent->killtarget){
192 t = NULL;
193 while((t = G_Find(t, FOFS(targetname), ent->killtarget))){
194 G_FreeEdict(t);
195 if(!ent->inuse){
196 gi.dprintf("entity was removed while using killtargets\n");
197 return;
198 }
199 }
200 }
201
202 //
203 // fire targets
204 //
205 if(ent->target){
206 t = NULL;
207 while((t = G_Find(t, FOFS(targetname), ent->target))){
208 // doors fire area portals in a specific way
209 if(!Q_stricmp(t->classname, "func_areaportal") &&
210 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
211 continue;
212
213 if(t == ent){
214 gi.dprintf("WARNING: Entity used itself.\n");
215 } else {
216 if(t->use)
217 t->use(t, ent, activator);
218 }
219 if(!ent->inuse){
220 gi.dprintf("entity was removed while using targets\n");
221 return;
222 }
223 }
224 }
225 }
226
227
228 /*
229 TempVector
230
231 This is just a convenience function
232 for making temporary vectors for function calls
233 */
tv(float x,float y,float z)234 float *tv(float x, float y, float z){
235 static int index;
236 static vec3_t vecs[8];
237 float *v;
238
239 // use an array so that multiple tempvectors won't collide
240 // for a while
241 v = vecs[index];
242 index =(index + 1) & 7;
243
244 v[0] = x;
245 v[1] = y;
246 v[2] = z;
247
248 return v;
249 }
250
251
252 /*
253 VectorToString
254
255 This is just a convenience function
256 for printing vectors
257 */
vtos(vec3_t v)258 char *vtos(vec3_t v){
259 static int index;
260 static char str[8][32];
261 char *s;
262
263 // use an array so that multiple vtos won't collide
264 s = str[index];
265 index =(index + 1) & 7;
266
267 Com_sprintf(s, 32, "(%i %i %i)",(int)v[0],(int)v[1],(int)v[2]);
268
269 return s;
270 }
271
272
273 vec3_t VEC_UP = {0, -1, 0};
274 vec3_t MOVEDIR_UP = {0, 0, 1};
275 vec3_t VEC_DOWN = {0, -2, 0};
276 vec3_t MOVEDIR_DOWN = {0, 0, -1};
277
G_SetMovedir(vec3_t angles,vec3_t movedir)278 void G_SetMovedir(vec3_t angles, vec3_t movedir){
279 if(VectorCompare(angles, VEC_UP)){
280 VectorCopy(MOVEDIR_UP, movedir);
281 } else if(VectorCompare(angles, VEC_DOWN)){
282 VectorCopy(MOVEDIR_DOWN, movedir);
283 } else {
284 AngleVectors(angles, movedir, NULL, NULL);
285 }
286
287 VectorClear(angles);
288 }
289
290
vectoyaw(vec3_t vec)291 float vectoyaw(vec3_t vec){
292 float yaw;
293
294 if(/*vec[YAW] == 0 &&*/ vec[PITCH] == 0){
295 yaw = 0;
296 if(vec[YAW] > 0)
297 yaw = 90;
298 else if(vec[YAW] < 0)
299 yaw = -90;
300 } else {
301 yaw =(int)(atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
302 if(yaw < 0)
303 yaw += 360;
304 }
305
306 return yaw;
307 }
308
309
vectoangles(vec3_t value1,vec3_t angles)310 void vectoangles(vec3_t value1, vec3_t angles){
311 float forward;
312 float yaw, pitch;
313
314 if(value1[1] == 0 && value1[0] == 0){
315 yaw = 0;
316 if(value1[2] > 0)
317 pitch = 90;
318 else
319 pitch = 270;
320 } else {
321 if(value1[0])
322 yaw =(int)(atan2(value1[1], value1[0]) * 180 / M_PI);
323 else if(value1[1] > 0)
324 yaw = 90;
325 else
326 yaw = -90;
327 if(yaw < 0)
328 yaw += 360;
329
330 forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
331 pitch =(int)(atan2(value1[2], forward) * 180 / M_PI);
332 if(pitch < 0)
333 pitch += 360;
334 }
335
336 angles[PITCH] = -pitch;
337 angles[YAW] = yaw;
338 angles[ROLL] = 0;
339 }
340
G_CopyString(char * in)341 char *G_CopyString(char *in){
342 char *out;
343
344 out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL);
345 strcpy(out, in);
346 return out;
347 }
348
349
G_InitEdict(edict_t * e)350 void G_InitEdict(edict_t *e){
351 e->inuse = true;
352 e->classname = "noclass";
353 e->gravity = 1.0;
354 e->s.number = e - g_edicts;
355 }
356
357 /*
358 G_Spawn
359
360 Either finds a free edict, or allocates a new one.
361 Try to avoid reusing an entity that was recently freed, because it
362 can cause the client to think the entity morphed into something else
363 instead of being removed and recreated, which can cause interpolated
364 angles and bad trails.
365 */
G_Spawn(void)366 edict_t *G_Spawn(void){
367 int i;
368 edict_t *e;
369
370 e = &g_edicts[(int)maxclients->value + 1];
371 for(i = maxclients->value + 1; i < globals.num_edicts; i++, e++){
372 // the first couple seconds of server time can involve a lot of
373 // freeing and allocating, so relax the replacement policy
374 if(!e->inuse &&(e->freetime < 2 || level.time - e->freetime > 0.5)){
375 G_InitEdict(e);
376 return e;
377 }
378 }
379
380 if(i == game.maxentities)
381 gi.error("ED_Alloc: no free edicts");
382
383 globals.num_edicts++;
384 G_InitEdict(e);
385 return e;
386 }
387
388 /*
389 G_FreeEdict
390
391 Marks the edict as free
392 */
G_FreeEdict(edict_t * ed)393 void G_FreeEdict(edict_t *ed){
394 gi.unlinkentity(ed); // unlink from world
395
396 if((ed - g_edicts) <=(maxclients->value + BODY_QUEUE_SIZE)){
397 // gi.dprintf("tried to free special edict\n");
398 return;
399 }
400
401 memset(ed, 0, sizeof(*ed));
402 ed->classname = "freed";
403 ed->freetime = level.time;
404 ed->inuse = false;
405 }
406
407
408 /*
409 G_TouchTriggers
410
411 */
G_TouchTriggers(edict_t * ent)412 void G_TouchTriggers(edict_t *ent){
413 int i, num;
414 edict_t *touch[MAX_EDICTS], *hit;
415
416 // dead things don't activate triggers!
417 if((ent->client ||(ent->svflags & SVF_MONSTER)) &&(ent->health <= 0))
418 return;
419
420 num = gi.BoxEdicts(ent->absmin, ent->absmax, touch
421 , MAX_EDICTS, AREA_TRIGGERS);
422
423 // be careful, it is possible to have an entity in this
424 // list removed before we get to it(killtriggered)
425 for(i = 0; i < num; i++){
426 hit = touch[i];
427 if(!hit->inuse)
428 continue;
429 if(!hit->touch)
430 continue;
431 hit->touch(hit, ent, NULL, NULL);
432 }
433 }
434
435 /*
436 G_TouchSolids
437
438 Call after linking a new trigger in during gameplay
439 to force all entities it covers to immediately touch it
440 */
G_TouchSolids(edict_t * ent)441 void G_TouchSolids(edict_t *ent){
442 int i, num;
443 edict_t *touch[MAX_EDICTS], *hit;
444
445 num = gi.BoxEdicts(ent->absmin, ent->absmax, touch
446 , MAX_EDICTS, AREA_SOLID);
447
448 // be careful, it is possible to have an entity in this
449 // list removed before we get to it(killtriggered)
450 for(i = 0; i < num; i++){
451 hit = touch[i];
452 if(!hit->inuse)
453 continue;
454 if(ent->touch)
455 ent->touch(hit, ent, NULL, NULL);
456 if(!ent->inuse)
457 break;
458 }
459 }
460
461
462
463
464 /*
465
466 Kill box
467
468 */
469
470 /*
471 KillBox
472
473 Kills all entities that would touch the proposed new positioning
474 of ent. Ent should be unlinked before calling this!
475 */
KillBox(edict_t * ent)476 qboolean KillBox(edict_t *ent){
477 trace_t tr;
478
479 while(1){
480 tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
481 if(!tr.ent)
482 break;
483
484 // nail it
485 T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
486
487 // if we didn't kill it, fail
488 if(tr.ent->solid)
489 return false;
490 }
491
492 return true; // all clear
493 }
494