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