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