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