1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // g_misc.c
21 
22 #include "g_local.h"
23 
24 
25 /*QUAKED func_group (0 0 0) ?
26 Used to group brushes together just for editor convenience.
27 */
28 
29 //=====================================================
30 
Use_Areaportal(edict_t * ent,edict_t * other,edict_t * activator)31 void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
32 {
33 	ent->count ^= 1;		// toggle state
34 //	gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
35 	gi.SetAreaPortalState (ent->style, ent->count);
36 }
37 
38 /*QUAKED func_areaportal (0 0 0) ?
39 
40 This is a non-visible object that divides the world into
41 areas that are seperated when this portal is not activated.
42 Usually enclosed in the middle of a door.
43 */
SP_func_areaportal(edict_t * ent)44 void SP_func_areaportal (edict_t *ent)
45 {
46 	ent->use = Use_Areaportal;
47 	ent->count = 0;		// allways start closed;
48 }
49 
50 //=====================================================
51 
52 
53 /*
54 =================
55 Misc functions
56 =================
57 */
VelocityForDamage(int damage,vec3_t v)58 void VelocityForDamage (int damage, vec3_t v)
59 {
60 	v[0] = 100.0 * crandom();
61 	v[1] = 100.0 * crandom();
62 	v[2] = 200.0 + 100.0 * random();
63 
64 	if (damage < 50)
65 		VectorScale (v, 0.7, v);
66 	else
67 		VectorScale (v, 1.2, v);
68 }
69 
ClipGibVelocity(edict_t * ent)70 void ClipGibVelocity (edict_t *ent)
71 {
72 	if (ent->velocity[0] < -300)
73 		ent->velocity[0] = -300;
74 	else if (ent->velocity[0] > 300)
75 		ent->velocity[0] = 300;
76 	if (ent->velocity[1] < -300)
77 		ent->velocity[1] = -300;
78 	else if (ent->velocity[1] > 300)
79 		ent->velocity[1] = 300;
80 	if (ent->velocity[2] < 200)
81 		ent->velocity[2] = 200;	// always some upwards
82 	else if (ent->velocity[2] > 500)
83 		ent->velocity[2] = 500;
84 }
85 
86 
87 /*
88 =================
89 gibs
90 =================
91 */
gib_think(edict_t * self)92 void gib_think (edict_t *self)
93 {
94 	self->s.frame++;
95 	self->nextthink = level.time + FRAMETIME;
96 
97 	if (self->s.frame == 10)
98 	{
99 		self->think = G_FreeEdict;
100 		self->nextthink = level.time + 8 + random()*10;
101 	}
102 }
103 
gib_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)104 void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
105 {
106 	vec3_t	normal_angles, right;
107 
108 	if (!self->groundentity)
109 		return;
110 
111 	self->touch = NULL;
112 
113 	if (plane)
114 	{
115 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
116 
117 		vectoangles (plane->normal, normal_angles);
118 		AngleVectors (normal_angles, NULL, right, NULL);
119 		vectoangles (right, self->s.angles);
120 
121 		if (self->s.modelindex == sm_meat_index)
122 		{
123 			self->s.frame++;
124 			self->think = gib_think;
125 			self->nextthink = level.time + FRAMETIME;
126 		}
127 	}
128 }
129 
gib_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)130 void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
131 {
132 	G_FreeEdict (self);
133 }
134 
ThrowGib(edict_t * self,char * gibname,int damage,int type)135 void ThrowGib (edict_t *self, char *gibname, int damage, int type)
136 {
137 	edict_t *gib;
138 	vec3_t	vd;
139 	vec3_t	origin;
140 	vec3_t	size;
141 	float	vscale;
142 
143 	gib = G_Spawn();
144 
145 	VectorScale (self->size, 0.5, size);
146 	VectorAdd (self->absmin, size, origin);
147 	gib->s.origin[0] = origin[0] + crandom() * size[0];
148 	gib->s.origin[1] = origin[1] + crandom() * size[1];
149 	gib->s.origin[2] = origin[2] + crandom() * size[2];
150 
151 	gi.setmodel (gib, gibname);
152 	gib->solid = SOLID_NOT;
153 	gib->s.effects |= EF_GIB;
154 	gib->flags |= FL_NO_KNOCKBACK;
155 	gib->takedamage = DAMAGE_YES;
156 	gib->die = gib_die;
157 
158 	if (type == GIB_ORGANIC)
159 	{
160 		gib->movetype = MOVETYPE_TOSS;
161 		gib->touch = gib_touch;
162 		vscale = 0.5;
163 	}
164 	else
165 	{
166 		gib->movetype = MOVETYPE_BOUNCE;
167 		vscale = 1.0;
168 	}
169 
170 	VelocityForDamage (damage, vd);
171 	VectorMA (self->velocity, vscale, vd, gib->velocity);
172 	ClipGibVelocity (gib);
173 	gib->avelocity[0] = random()*600;
174 	gib->avelocity[1] = random()*600;
175 	gib->avelocity[2] = random()*600;
176 
177 	gib->think = G_FreeEdict;
178 	gib->nextthink = level.time + 10 + random()*10;
179 
180 	gi.linkentity (gib);
181 }
182 
ThrowHead(edict_t * self,char * gibname,int damage,int type)183 void ThrowHead (edict_t *self, char *gibname, int damage, int type)
184 {
185 	vec3_t	vd;
186 	float	vscale;
187 
188 	self->s.skinnum = 0;
189 	self->s.frame = 0;
190 	VectorClear (self->mins);
191 	VectorClear (self->maxs);
192 
193 	self->s.modelindex2 = 0;
194 	gi.setmodel (self, gibname);
195 	self->solid = SOLID_NOT;
196 	self->s.effects |= EF_GIB;
197 	self->s.effects &= ~EF_FLIES;
198 	self->s.sound = 0;
199 	self->flags |= FL_NO_KNOCKBACK;
200 	self->svflags &= ~SVF_MONSTER;
201 	self->takedamage = DAMAGE_YES;
202 	self->die = gib_die;
203 
204 	if (type == GIB_ORGANIC)
205 	{
206 		self->movetype = MOVETYPE_TOSS;
207 		self->touch = gib_touch;
208 		vscale = 0.5;
209 	}
210 	else
211 	{
212 		self->movetype = MOVETYPE_BOUNCE;
213 		vscale = 1.0;
214 	}
215 
216 	VelocityForDamage (damage, vd);
217 	VectorMA (self->velocity, vscale, vd, self->velocity);
218 	ClipGibVelocity (self);
219 
220 	self->avelocity[YAW] = crandom()*600;
221 
222 	self->think = G_FreeEdict;
223 	self->nextthink = level.time + 10 + random()*10;
224 
225 	gi.linkentity (self);
226 }
227 
228 
ThrowClientHead(edict_t * self,int damage)229 void ThrowClientHead (edict_t *self, int damage)
230 {
231 	vec3_t	vd;
232 	char	*gibname;
233 
234 	if (rand()&1)
235 	{
236 		gibname = "models/objects/gibs/head2/tris.md2";
237 		self->s.skinnum = 1;		// second skin is player
238 	}
239 	else
240 	{
241 		gibname = "models/objects/gibs/skull/tris.md2";
242 		self->s.skinnum = 0;
243 	}
244 
245 	self->s.origin[2] += 32;
246 	self->s.frame = 0;
247 	gi.setmodel (self, gibname);
248 	VectorSet (self->mins, -16, -16, 0);
249 	VectorSet (self->maxs, 16, 16, 16);
250 
251 	self->takedamage = DAMAGE_NO;
252 	self->solid = SOLID_NOT;
253 	self->s.effects = EF_GIB;
254 	self->s.sound = 0;
255 	self->flags |= FL_NO_KNOCKBACK;
256 
257 	self->movetype = MOVETYPE_BOUNCE;
258 	VelocityForDamage (damage, vd);
259 	VectorAdd (self->velocity, vd, self->velocity);
260 
261 	if (self->client)	// bodies in the queue don't have a client anymore
262 	{
263 		self->client->anim_priority = ANIM_DEATH;
264 		self->client->anim_end = self->s.frame;
265 	}
266 
267 	gi.linkentity (self);
268 }
269 
270 
271 /*
272 =================
273 debris
274 =================
275 */
debris_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)276 void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
277 {
278 	G_FreeEdict (self);
279 }
280 
ThrowDebris(edict_t * self,char * modelname,float speed,vec3_t origin)281 void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
282 {
283 	edict_t	*chunk;
284 	vec3_t	v;
285 
286 	chunk = G_Spawn();
287 	VectorCopy (origin, chunk->s.origin);
288 	gi.setmodel (chunk, modelname);
289 	v[0] = 100 * crandom();
290 	v[1] = 100 * crandom();
291 	v[2] = 100 + 100 * crandom();
292 	VectorMA (self->velocity, speed, v, chunk->velocity);
293 	chunk->movetype = MOVETYPE_BOUNCE;
294 	chunk->solid = SOLID_NOT;
295 	chunk->avelocity[0] = random()*600;
296 	chunk->avelocity[1] = random()*600;
297 	chunk->avelocity[2] = random()*600;
298 	chunk->think = G_FreeEdict;
299 	chunk->nextthink = level.time + 5 + random()*5;
300 	chunk->s.frame = 0;
301 	chunk->flags = 0;
302 	chunk->classname = "debris";
303 	chunk->takedamage = DAMAGE_YES;
304 	chunk->die = debris_die;
305 	gi.linkentity (chunk);
306 }
307 
308 
BecomeExplosion1(edict_t * self)309 void BecomeExplosion1 (edict_t *self)
310 {
311 //ZOID
312 	//flags are important
313 	if (strcmp(self->classname, "item_flag_team1") == 0) {
314 		CTFResetFlag(CTF_TEAM1); // this will free self!
315 		gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
316 			CTFTeamName(CTF_TEAM1));
317 		return;
318 	}
319 	if (strcmp(self->classname, "item_flag_team2") == 0) {
320 		CTFResetFlag(CTF_TEAM2); // this will free self!
321 		gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
322 			CTFTeamName(CTF_TEAM1));
323 		return;
324 	}
325 	// techs are important too
326 	if (self->item && (self->item->flags & IT_TECH)) {
327 		CTFRespawnTech(self); // this frees self!
328 		return;
329 	}
330 //ZOID
331 
332 	gi.WriteByte (svc_temp_entity);
333 	gi.WriteByte (TE_EXPLOSION1);
334 	gi.WritePosition (self->s.origin);
335 	gi.multicast (self->s.origin, MULTICAST_PVS);
336 
337 	G_FreeEdict (self);
338 }
339 
340 
BecomeExplosion2(edict_t * self)341 void BecomeExplosion2 (edict_t *self)
342 {
343 	gi.WriteByte (svc_temp_entity);
344 	gi.WriteByte (TE_EXPLOSION2);
345 	gi.WritePosition (self->s.origin);
346 	gi.multicast (self->s.origin, MULTICAST_PVS);
347 
348 	G_FreeEdict (self);
349 }
350 
351 
352 /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
353 Target: next path corner
354 Pathtarget: gets used when an entity that has
355 	this path_corner targeted touches it
356 */
357 
path_corner_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)358 void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
359 {
360 	vec3_t		v;
361 	edict_t		*next;
362 
363 	if (other->movetarget != self)
364 		return;
365 
366 	if (other->enemy)
367 		return;
368 
369 	if (self->pathtarget)
370 	{
371 		char *savetarget;
372 
373 		savetarget = self->target;
374 		self->target = self->pathtarget;
375 		G_UseTargets (self, other);
376 		self->target = savetarget;
377 	}
378 
379 	if (self->target)
380 		next = G_PickTarget(self->target);
381 	else
382 		next = NULL;
383 
384 	if ((next) && (next->spawnflags & 1))
385 	{
386 		VectorCopy (next->s.origin, v);
387 		v[2] += next->mins[2];
388 		v[2] -= other->mins[2];
389 		VectorCopy (v, other->s.origin);
390 		next = G_PickTarget(next->target);
391 	}
392 
393 	other->goalentity = other->movetarget = next;
394 
395 	if (self->wait)
396 	{
397 		other->monsterinfo.pausetime = level.time + self->wait;
398 		other->monsterinfo.stand (other);
399 		return;
400 	}
401 
402 	if (!other->movetarget)
403 	{
404 		other->monsterinfo.pausetime = level.time + 100000000;
405 		other->monsterinfo.stand (other);
406 	}
407 	else
408 	{
409 		VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
410 		other->ideal_yaw = vectoyaw (v);
411 	}
412 }
413 
SP_path_corner(edict_t * self)414 void SP_path_corner (edict_t *self)
415 {
416 	if (!self->targetname)
417 	{
418 		gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
419 		G_FreeEdict (self);
420 		return;
421 	}
422 
423 	self->solid = SOLID_TRIGGER;
424 	self->touch = path_corner_touch;
425 	VectorSet (self->mins, -8, -8, -8);
426 	VectorSet (self->maxs, 8, 8, 8);
427 	self->svflags |= SVF_NOCLIENT;
428 	gi.linkentity (self);
429 }
430 
431 
432 /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
433 Makes this the target of a monster and it will head here
434 when first activated before going after the activator.  If
435 hold is selected, it will stay here.
436 */
point_combat_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)437 void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
438 {
439 	edict_t	*activator;
440 
441 	if (other->movetarget != self)
442 		return;
443 
444 	if (self->target)
445 	{
446 		other->target = self->target;
447 		other->goalentity = other->movetarget = G_PickTarget(other->target);
448 		if (!other->goalentity)
449 		{
450 			gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
451 			other->movetarget = self;
452 		}
453 		self->target = NULL;
454 	}
455 	else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
456 	{
457 		other->monsterinfo.pausetime = level.time + 100000000;
458 		other->monsterinfo.aiflags |= AI_STAND_GROUND;
459 		other->monsterinfo.stand (other);
460 	}
461 
462 	if (other->movetarget == self)
463 	{
464 		other->target = NULL;
465 		other->movetarget = NULL;
466 		other->goalentity = other->enemy;
467 		other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
468 	}
469 
470 	if (self->pathtarget)
471 	{
472 		char *savetarget;
473 
474 		savetarget = self->target;
475 		self->target = self->pathtarget;
476 		if (other->enemy && other->enemy->client)
477 			activator = other->enemy;
478 		else if (other->oldenemy && other->oldenemy->client)
479 			activator = other->oldenemy;
480 		else if (other->activator && other->activator->client)
481 			activator = other->activator;
482 		else
483 			activator = other;
484 		G_UseTargets (self, activator);
485 		self->target = savetarget;
486 	}
487 }
488 
SP_point_combat(edict_t * self)489 void SP_point_combat (edict_t *self)
490 {
491 	if (deathmatch->value)
492 	{
493 		G_FreeEdict (self);
494 		return;
495 	}
496 	self->solid = SOLID_TRIGGER;
497 	self->touch = point_combat_touch;
498 	VectorSet (self->mins, -8, -8, -16);
499 	VectorSet (self->maxs, 8, 8, 16);
500 	self->svflags = SVF_NOCLIENT;
501 	gi.linkentity (self);
502 }
503 
504 
505 /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
506 Just for the debugging level.  Don't use
507 */
508 static int robotron[4];
509 
TH_viewthing(edict_t * ent)510 void TH_viewthing(edict_t *ent)
511 {
512 	ent->s.frame = (ent->s.frame + 1) % 7;
513 //	ent->s.frame = (ent->s.frame + 1) % 9;
514 	ent->nextthink = level.time + FRAMETIME;
515 //	return;
516 
517 	if (ent->spawnflags)
518 	{
519 		if (ent->s.frame == 0)
520 		{
521 			ent->spawnflags = (ent->spawnflags + 1) % 4 + 1;
522 			ent->s.modelindex = robotron[ent->spawnflags - 1];
523 		}
524 	}
525 }
526 
SP_viewthing(edict_t * ent)527 void SP_viewthing(edict_t *ent)
528 {
529 	gi.dprintf ("viewthing spawned\n");
530 
531 	ent->movetype = MOVETYPE_NONE;
532 	ent->solid = SOLID_BBOX;
533 	ent->s.renderfx = RF_FRAMELERP;
534 	VectorSet (ent->mins, -16, -16, -24);
535 	VectorSet (ent->maxs, 16, 16, 32);
536 //	ent->s.modelindex = gi.modelindex ("models/player_y/tris.md2");
537 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
538 	gi.linkentity (ent);
539 	ent->nextthink = level.time + 0.5;
540 	ent->think = TH_viewthing;
541 	return;
542 }
543 
544 
545 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
546 Used as a positional target for spotlights, etc.
547 */
SP_info_null(edict_t * self)548 void SP_info_null (edict_t *self)
549 {
550 	G_FreeEdict (self);
551 }
552 
553 
554 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
555 Used as a positional target for lightning.
556 */
SP_info_notnull(edict_t * self)557 void SP_info_notnull (edict_t *self)
558 {
559 	VectorCopy (self->s.origin, self->absmin);
560 	VectorCopy (self->s.origin, self->absmax);
561 }
562 
563 
564 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
565 Non-displayed light.
566 Default light value is 300.
567 Default style is 0.
568 If targeted, will toggle between on and off.
569 Default _cone value is 10 (used to set size of light for spotlights)
570 */
571 
572 #define START_OFF	1
573 
light_use(edict_t * self,edict_t * other,edict_t * activator)574 static void light_use (edict_t *self, edict_t *other, edict_t *activator)
575 {
576 	if (self->spawnflags & START_OFF)
577 	{
578 		gi.configstring (CS_LIGHTS+self->style, "m");
579 		self->spawnflags &= ~START_OFF;
580 	}
581 	else
582 	{
583 		gi.configstring (CS_LIGHTS+self->style, "a");
584 		self->spawnflags |= START_OFF;
585 	}
586 }
587 
SP_light(edict_t * self)588 void SP_light (edict_t *self)
589 {
590 	// no targeted lights in deathmatch, because they cause global messages
591 	if (!self->targetname || deathmatch->value)
592 	{
593 		G_FreeEdict (self);
594 		return;
595 	}
596 
597 	if (self->style >= 32)
598 	{
599 		self->use = light_use;
600 		if (self->spawnflags & START_OFF)
601 			gi.configstring (CS_LIGHTS+self->style, "a");
602 		else
603 			gi.configstring (CS_LIGHTS+self->style, "m");
604 	}
605 }
606 
607 
608 /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
609 This is just a solid wall if not inhibited
610 
611 TRIGGER_SPAWN	the wall will not be present until triggered
612 				it will then blink in to existance; it will
613 				kill anything that was in it's way
614 
615 TOGGLE			only valid for TRIGGER_SPAWN walls
616 				this allows the wall to be turned on and off
617 
618 START_ON		only valid for TRIGGER_SPAWN walls
619 				the wall will initially be present
620 */
621 
func_wall_use(edict_t * self,edict_t * other,edict_t * activator)622 void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
623 {
624 	if (self->solid == SOLID_NOT)
625 	{
626 		self->solid = SOLID_BSP;
627 		self->svflags &= ~SVF_NOCLIENT;
628 		KillBox (self);
629 	}
630 	else
631 	{
632 		self->solid = SOLID_NOT;
633 		self->svflags |= SVF_NOCLIENT;
634 	}
635 	gi.linkentity (self);
636 
637 	if (!(self->spawnflags & 2))
638 		self->use = NULL;
639 }
640 
SP_func_wall(edict_t * self)641 void SP_func_wall (edict_t *self)
642 {
643 	self->movetype = MOVETYPE_PUSH;
644 	gi.setmodel (self, self->model);
645 
646 	if (self->spawnflags & 8)
647 		self->s.effects |= EF_ANIM_ALL;
648 	if (self->spawnflags & 16)
649 		self->s.effects |= EF_ANIM_ALLFAST;
650 
651 	// just a wall
652 	if ((self->spawnflags & 7) == 0)
653 	{
654 		self->solid = SOLID_BSP;
655 		gi.linkentity (self);
656 		return;
657 	}
658 
659 	// it must be TRIGGER_SPAWN
660 	if (!(self->spawnflags & 1))
661 	{
662 //		gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
663 		self->spawnflags |= 1;
664 	}
665 
666 	// yell if the spawnflags are odd
667 	if (self->spawnflags & 4)
668 	{
669 		if (!(self->spawnflags & 2))
670 		{
671 			gi.dprintf("func_wall START_ON without TOGGLE\n");
672 			self->spawnflags |= 2;
673 		}
674 	}
675 
676 	self->use = func_wall_use;
677 	if (self->spawnflags & 4)
678 	{
679 		self->solid = SOLID_BSP;
680 	}
681 	else
682 	{
683 		self->solid = SOLID_NOT;
684 		self->svflags |= SVF_NOCLIENT;
685 	}
686 	gi.linkentity (self);
687 }
688 
689 
690 /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
691 This is solid bmodel that will fall if it's support it removed.
692 */
693 
func_object_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)694 void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
695 {
696 	// only squash thing we fall on top of
697 	if (!plane)
698 		return;
699 	if (plane->normal[2] < 1.0)
700 		return;
701 	if (other->takedamage == DAMAGE_NO)
702 		return;
703 	T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
704 }
705 
func_object_release(edict_t * self)706 void func_object_release (edict_t *self)
707 {
708 	self->movetype = MOVETYPE_TOSS;
709 	self->touch = func_object_touch;
710 }
711 
func_object_use(edict_t * self,edict_t * other,edict_t * activator)712 void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
713 {
714 	self->solid = SOLID_BSP;
715 	self->svflags &= ~SVF_NOCLIENT;
716 	self->use = NULL;
717 	KillBox (self);
718 	func_object_release (self);
719 }
720 
SP_func_object(edict_t * self)721 void SP_func_object (edict_t *self)
722 {
723 	gi.setmodel (self, self->model);
724 
725 	self->mins[0] += 1;
726 	self->mins[1] += 1;
727 	self->mins[2] += 1;
728 	self->maxs[0] -= 1;
729 	self->maxs[1] -= 1;
730 	self->maxs[2] -= 1;
731 
732 	if (!self->dmg)
733 		self->dmg = 100;
734 
735 	if (self->spawnflags == 0)
736 	{
737 		self->solid = SOLID_BSP;
738 		self->movetype = MOVETYPE_PUSH;
739 		self->think = func_object_release;
740 		self->nextthink = level.time + 2 * FRAMETIME;
741 	}
742 	else
743 	{
744 		self->solid = SOLID_NOT;
745 		self->movetype = MOVETYPE_PUSH;
746 		self->use = func_object_use;
747 		self->svflags |= SVF_NOCLIENT;
748 	}
749 
750 	if (self->spawnflags & 2)
751 		self->s.effects |= EF_ANIM_ALL;
752 	if (self->spawnflags & 4)
753 		self->s.effects |= EF_ANIM_ALLFAST;
754 
755 	self->clipmask = MASK_MONSTERSOLID;
756 
757 	gi.linkentity (self);
758 }
759 
760 
761 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
762 Any brush that you want to explode or break apart.  If you want an
763 ex0plosion, set dmg and it will do a radius explosion of that amount
764 at the center of the bursh.
765 
766 If targeted it will not be shootable.
767 
768 health defaults to 100.
769 
770 mass defaults to 75.  This determines how much debris is emitted when
771 it explodes.  You get one large chunk per 100 of mass (up to 8) and
772 one small chunk per 25 of mass (up to 16).  So 800 gives the most.
773 */
func_explosive_explode(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)774 void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
775 {
776 	vec3_t	origin;
777 	vec3_t	chunkorigin;
778 	vec3_t	size;
779 	int		count;
780 	int		mass;
781 
782 	// bmodel origins are (0 0 0), we need to adjust that here
783 	VectorScale (self->size, 0.5, size);
784 	VectorAdd (self->absmin, size, origin);
785 	VectorCopy (origin, self->s.origin);
786 
787 	self->takedamage = DAMAGE_NO;
788 
789 	if (self->dmg)
790 		T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
791 
792 	VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
793 	VectorNormalize (self->velocity);
794 	VectorScale (self->velocity, 150, self->velocity);
795 
796 	// start chunks towards the center
797 	VectorScale (size, 0.5, size);
798 
799 	mass = self->mass;
800 	if (!mass)
801 		mass = 75;
802 
803 	// big chunks
804 	if (mass >= 100)
805 	{
806 		count = mass / 100;
807 		if (count > 8)
808 			count = 8;
809 		while(count--)
810 		{
811 			chunkorigin[0] = origin[0] + crandom() * size[0];
812 			chunkorigin[1] = origin[1] + crandom() * size[1];
813 			chunkorigin[2] = origin[2] + crandom() * size[2];
814 			ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin);
815 		}
816 	}
817 
818 	// small chunks
819 	count = mass / 25;
820 	if (count > 16)
821 		count = 16;
822 	while(count--)
823 	{
824 		chunkorigin[0] = origin[0] + crandom() * size[0];
825 		chunkorigin[1] = origin[1] + crandom() * size[1];
826 		chunkorigin[2] = origin[2] + crandom() * size[2];
827 		ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin);
828 	}
829 
830 	G_UseTargets (self, attacker);
831 
832 	if (self->dmg)
833 		BecomeExplosion1 (self);
834 	else
835 		G_FreeEdict (self);
836 }
837 
func_explosive_use(edict_t * self,edict_t * other,edict_t * activator)838 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
839 {
840 	func_explosive_explode (self, self, other, self->health, vec3_origin);
841 }
842 
func_explosive_spawn(edict_t * self,edict_t * other,edict_t * activator)843 void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator)
844 {
845 	self->solid = SOLID_BSP;
846 	self->svflags &= ~SVF_NOCLIENT;
847 	self->use = NULL;
848 	KillBox (self);
849 	gi.linkentity (self);
850 }
851 
SP_func_explosive(edict_t * self)852 void SP_func_explosive (edict_t *self)
853 {
854 	if (deathmatch->value)
855 	{	// auto-remove for deathmatch
856 		G_FreeEdict (self);
857 		return;
858 	}
859 
860 	self->movetype = MOVETYPE_PUSH;
861 
862 	gi.modelindex ("models/objects/debris1/tris.md2");
863 	gi.modelindex ("models/objects/debris2/tris.md2");
864 
865 	gi.setmodel (self, self->model);
866 
867 	if (self->spawnflags & 1)
868 	{
869 		self->svflags |= SVF_NOCLIENT;
870 		self->solid = SOLID_NOT;
871 		self->use = func_explosive_spawn;
872 	}
873 	else
874 	{
875 		self->solid = SOLID_BSP;
876 		if (self->targetname)
877 			self->use = func_explosive_use;
878 	}
879 
880 	if (self->spawnflags & 2)
881 		self->s.effects |= EF_ANIM_ALL;
882 	if (self->spawnflags & 4)
883 		self->s.effects |= EF_ANIM_ALLFAST;
884 
885 	if (self->use != func_explosive_use)
886 	{
887 		if (!self->health)
888 			self->health = 100;
889 		self->die = func_explosive_explode;
890 		self->takedamage = DAMAGE_YES;
891 	}
892 
893 	gi.linkentity (self);
894 }
895 
896 
897 /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
898 Large exploding box.  You can override its mass (100),
899 health (80), and dmg (150).
900 */
901 
barrel_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)902 void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
903 
904 {
905 	float	ratio;
906 	vec3_t	v;
907 
908 	if ((!other->groundentity) || (other->groundentity == self))
909 		return;
910 
911 	ratio = (float)other->mass / (float)self->mass;
912 	VectorSubtract (self->s.origin, other->s.origin, v);
913 	M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
914 }
915 
barrel_explode(edict_t * self)916 void barrel_explode (edict_t *self)
917 {
918 	vec3_t	org;
919 	float	spd;
920 	vec3_t	save;
921 
922 	T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL);
923 
924 	VectorCopy (self->s.origin, save);
925 	VectorMA (self->absmin, 0.5, self->size, self->s.origin);
926 
927 	// a few big chunks
928 	spd = 1.5 * (float)self->dmg / 200.0;
929 	org[0] = self->s.origin[0] + crandom() * self->size[0];
930 	org[1] = self->s.origin[1] + crandom() * self->size[1];
931 	org[2] = self->s.origin[2] + crandom() * self->size[2];
932 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
933 	org[0] = self->s.origin[0] + crandom() * self->size[0];
934 	org[1] = self->s.origin[1] + crandom() * self->size[1];
935 	org[2] = self->s.origin[2] + crandom() * self->size[2];
936 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
937 
938 	// bottom corners
939 	spd = 1.75 * (float)self->dmg / 200.0;
940 	VectorCopy (self->absmin, org);
941 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
942 	VectorCopy (self->absmin, org);
943 	org[0] += self->size[0];
944 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
945 	VectorCopy (self->absmin, org);
946 	org[1] += self->size[1];
947 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
948 	VectorCopy (self->absmin, org);
949 	org[0] += self->size[0];
950 	org[1] += self->size[1];
951 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
952 
953 	// a bunch of little chunks
954 	spd = 2 * self->dmg / 200;
955 	org[0] = self->s.origin[0] + crandom() * self->size[0];
956 	org[1] = self->s.origin[1] + crandom() * self->size[1];
957 	org[2] = self->s.origin[2] + crandom() * self->size[2];
958 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
959 	org[0] = self->s.origin[0] + crandom() * self->size[0];
960 	org[1] = self->s.origin[1] + crandom() * self->size[1];
961 	org[2] = self->s.origin[2] + crandom() * self->size[2];
962 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
963 	org[0] = self->s.origin[0] + crandom() * self->size[0];
964 	org[1] = self->s.origin[1] + crandom() * self->size[1];
965 	org[2] = self->s.origin[2] + crandom() * self->size[2];
966 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
967 	org[0] = self->s.origin[0] + crandom() * self->size[0];
968 	org[1] = self->s.origin[1] + crandom() * self->size[1];
969 	org[2] = self->s.origin[2] + crandom() * self->size[2];
970 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
971 	org[0] = self->s.origin[0] + crandom() * self->size[0];
972 	org[1] = self->s.origin[1] + crandom() * self->size[1];
973 	org[2] = self->s.origin[2] + crandom() * self->size[2];
974 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
975 	org[0] = self->s.origin[0] + crandom() * self->size[0];
976 	org[1] = self->s.origin[1] + crandom() * self->size[1];
977 	org[2] = self->s.origin[2] + crandom() * self->size[2];
978 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
979 	org[0] = self->s.origin[0] + crandom() * self->size[0];
980 	org[1] = self->s.origin[1] + crandom() * self->size[1];
981 	org[2] = self->s.origin[2] + crandom() * self->size[2];
982 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
983 	org[0] = self->s.origin[0] + crandom() * self->size[0];
984 	org[1] = self->s.origin[1] + crandom() * self->size[1];
985 	org[2] = self->s.origin[2] + crandom() * self->size[2];
986 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
987 
988 	VectorCopy (save, self->s.origin);
989 	if (self->groundentity)
990 		BecomeExplosion2 (self);
991 	else
992 		BecomeExplosion1 (self);
993 }
994 
barrel_delay(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)995 void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
996 {
997 	self->takedamage = DAMAGE_NO;
998 	self->nextthink = level.time + 2 * FRAMETIME;
999 	self->think = barrel_explode;
1000 	self->activator = attacker;
1001 }
1002 
SP_misc_explobox(edict_t * self)1003 void SP_misc_explobox (edict_t *self)
1004 {
1005 	if (deathmatch->value)
1006 	{	// auto-remove for deathmatch
1007 		G_FreeEdict (self);
1008 		return;
1009 	}
1010 
1011 	gi.modelindex ("models/objects/debris1/tris.md2");
1012 	gi.modelindex ("models/objects/debris2/tris.md2");
1013 	gi.modelindex ("models/objects/debris3/tris.md2");
1014 
1015 	self->solid = SOLID_BBOX;
1016 	self->movetype = MOVETYPE_STEP;
1017 
1018 	self->model = "models/objects/barrels/tris.md2";
1019 	self->s.modelindex = gi.modelindex (self->model);
1020 	VectorSet (self->mins, -16, -16, 0);
1021 	VectorSet (self->maxs, 16, 16, 40);
1022 
1023 	if (!self->mass)
1024 		self->mass = 400;
1025 	if (!self->health)
1026 		self->health = 10;
1027 	if (!self->dmg)
1028 		self->dmg = 150;
1029 
1030 	self->die = barrel_delay;
1031 	self->takedamage = DAMAGE_YES;
1032 	self->monsterinfo.aiflags = AI_NOSTEP;
1033 
1034 	self->touch = barrel_touch;
1035 
1036 	self->think = M_droptofloor;
1037 	self->nextthink = level.time + 2 * FRAMETIME;
1038 
1039 	gi.linkentity (self);
1040 }
1041 
1042 
1043 //
1044 // miscellaneous specialty items
1045 //
1046 
1047 /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8)
1048 */
1049 
misc_blackhole_use(edict_t * ent,edict_t * other,edict_t * activator)1050 void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator)
1051 {
1052 	/*
1053 	gi.WriteByte (svc_temp_entity);
1054 	gi.WriteByte (TE_BOSSTPORT);
1055 	gi.WritePosition (ent->s.origin);
1056 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1057 	*/
1058 	G_FreeEdict (ent);
1059 }
1060 
misc_blackhole_think(edict_t * self)1061 void misc_blackhole_think (edict_t *self)
1062 {
1063 	if (++self->s.frame < 19)
1064 		self->nextthink = level.time + FRAMETIME;
1065 	else
1066 	{
1067 		self->s.frame = 0;
1068 		self->nextthink = level.time + FRAMETIME;
1069 	}
1070 }
1071 
SP_misc_blackhole(edict_t * ent)1072 void SP_misc_blackhole (edict_t *ent)
1073 {
1074 	ent->movetype = MOVETYPE_NONE;
1075 	ent->solid = SOLID_NOT;
1076 	VectorSet (ent->mins, -64, -64, 0);
1077 	VectorSet (ent->maxs, 64, 64, 8);
1078 	ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2");
1079 	ent->s.renderfx = RF_TRANSLUCENT;
1080 	ent->use = misc_blackhole_use;
1081 	ent->think = misc_blackhole_think;
1082 	ent->nextthink = level.time + 2 * FRAMETIME;
1083 	gi.linkentity (ent);
1084 }
1085 
1086 /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32)
1087 */
1088 
misc_eastertank_think(edict_t * self)1089 void misc_eastertank_think (edict_t *self)
1090 {
1091 	if (++self->s.frame < 293)
1092 		self->nextthink = level.time + FRAMETIME;
1093 	else
1094 	{
1095 		self->s.frame = 254;
1096 		self->nextthink = level.time + FRAMETIME;
1097 	}
1098 }
1099 
SP_misc_eastertank(edict_t * ent)1100 void SP_misc_eastertank (edict_t *ent)
1101 {
1102 	ent->movetype = MOVETYPE_NONE;
1103 	ent->solid = SOLID_BBOX;
1104 	VectorSet (ent->mins, -32, -32, -16);
1105 	VectorSet (ent->maxs, 32, 32, 32);
1106 	ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
1107 	ent->s.frame = 254;
1108 	ent->think = misc_eastertank_think;
1109 	ent->nextthink = level.time + 2 * FRAMETIME;
1110 	gi.linkentity (ent);
1111 }
1112 
1113 /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
1114 */
1115 
1116 
misc_easterchick_think(edict_t * self)1117 void misc_easterchick_think (edict_t *self)
1118 {
1119 	if (++self->s.frame < 247)
1120 		self->nextthink = level.time + FRAMETIME;
1121 	else
1122 	{
1123 		self->s.frame = 208;
1124 		self->nextthink = level.time + FRAMETIME;
1125 	}
1126 }
1127 
SP_misc_easterchick(edict_t * ent)1128 void SP_misc_easterchick (edict_t *ent)
1129 {
1130 	ent->movetype = MOVETYPE_NONE;
1131 	ent->solid = SOLID_BBOX;
1132 	VectorSet (ent->mins, -32, -32, 0);
1133 	VectorSet (ent->maxs, 32, 32, 32);
1134 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1135 	ent->s.frame = 208;
1136 	ent->think = misc_easterchick_think;
1137 	ent->nextthink = level.time + 2 * FRAMETIME;
1138 	gi.linkentity (ent);
1139 }
1140 
1141 /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
1142 */
1143 
1144 
misc_easterchick2_think(edict_t * self)1145 void misc_easterchick2_think (edict_t *self)
1146 {
1147 	if (++self->s.frame < 287)
1148 		self->nextthink = level.time + FRAMETIME;
1149 	else
1150 	{
1151 		self->s.frame = 248;
1152 		self->nextthink = level.time + FRAMETIME;
1153 	}
1154 }
1155 
SP_misc_easterchick2(edict_t * ent)1156 void SP_misc_easterchick2 (edict_t *ent)
1157 {
1158 	ent->movetype = MOVETYPE_NONE;
1159 	ent->solid = SOLID_BBOX;
1160 	VectorSet (ent->mins, -32, -32, 0);
1161 	VectorSet (ent->maxs, 32, 32, 32);
1162 	ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1163 	ent->s.frame = 248;
1164 	ent->think = misc_easterchick2_think;
1165 	ent->nextthink = level.time + 2 * FRAMETIME;
1166 	gi.linkentity (ent);
1167 }
1168 
1169 
1170 /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
1171 Not really a monster, this is the Tank Commander's decapitated body.
1172 There should be a item_commander_head that has this as it's target.
1173 */
1174 
commander_body_think(edict_t * self)1175 void commander_body_think (edict_t *self)
1176 {
1177 	if (++self->s.frame < 24)
1178 		self->nextthink = level.time + FRAMETIME;
1179 	else
1180 		self->nextthink = 0;
1181 
1182 	if (self->s.frame == 22)
1183 		gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0);
1184 }
1185 
commander_body_use(edict_t * self,edict_t * other,edict_t * activator)1186 void commander_body_use (edict_t *self, edict_t *other, edict_t *activator)
1187 {
1188 	self->think = commander_body_think;
1189 	self->nextthink = level.time + FRAMETIME;
1190 	gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0);
1191 }
1192 
commander_body_drop(edict_t * self)1193 void commander_body_drop (edict_t *self)
1194 {
1195 	self->movetype = MOVETYPE_TOSS;
1196 	self->s.origin[2] += 2;
1197 }
1198 
SP_monster_commander_body(edict_t * self)1199 void SP_monster_commander_body (edict_t *self)
1200 {
1201 	self->movetype = MOVETYPE_NONE;
1202 	self->solid = SOLID_BBOX;
1203 	self->model = "models/monsters/commandr/tris.md2";
1204 	self->s.modelindex = gi.modelindex (self->model);
1205 	VectorSet (self->mins, -32, -32, 0);
1206 	VectorSet (self->maxs, 32, 32, 48);
1207 	self->use = commander_body_use;
1208 	self->takedamage = DAMAGE_YES;
1209 	self->flags = FL_GODMODE;
1210 	self->s.renderfx |= RF_FRAMELERP;
1211 	gi.linkentity (self);
1212 
1213 	gi.soundindex ("tank/thud.wav");
1214 	gi.soundindex ("tank/pain.wav");
1215 
1216 	self->think = commander_body_drop;
1217 	self->nextthink = level.time + 5 * FRAMETIME;
1218 }
1219 
1220 
1221 /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
1222 The origin is the bottom of the banner.
1223 The banner is 128 tall.
1224 */
misc_banner_think(edict_t * ent)1225 void misc_banner_think (edict_t *ent)
1226 {
1227 	ent->s.frame = (ent->s.frame + 1) % 16;
1228 	ent->nextthink = level.time + FRAMETIME;
1229 }
1230 
SP_misc_banner(edict_t * ent)1231 void SP_misc_banner (edict_t *ent)
1232 {
1233 	ent->movetype = MOVETYPE_NONE;
1234 	ent->solid = SOLID_NOT;
1235 	ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
1236 	ent->s.frame = rand() % 16;
1237 	gi.linkentity (ent);
1238 
1239 	ent->think = misc_banner_think;
1240 	ent->nextthink = level.time + FRAMETIME;
1241 }
1242 
1243 /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
1244 This is the dead player model. Comes in 6 exciting different poses!
1245 */
misc_deadsoldier_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1246 void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1247 {
1248 	int		n;
1249 
1250 	if (self->health > -80)
1251 		return;
1252 
1253 	gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1254 	for (n= 0; n < 4; n++)
1255 		ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1256 	ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
1257 }
1258 
SP_misc_deadsoldier(edict_t * ent)1259 void SP_misc_deadsoldier (edict_t *ent)
1260 {
1261 	if (deathmatch->value)
1262 	{	// auto-remove for deathmatch
1263 		G_FreeEdict (ent);
1264 		return;
1265 	}
1266 
1267 	ent->movetype = MOVETYPE_NONE;
1268 	ent->solid = SOLID_BBOX;
1269 	ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2");
1270 
1271 	// Defaults to frame 0
1272 	if (ent->spawnflags & 2)
1273 		ent->s.frame = 1;
1274 	else if (ent->spawnflags & 4)
1275 		ent->s.frame = 2;
1276 	else if (ent->spawnflags & 8)
1277 		ent->s.frame = 3;
1278 	else if (ent->spawnflags & 16)
1279 		ent->s.frame = 4;
1280 	else if (ent->spawnflags & 32)
1281 		ent->s.frame = 5;
1282 	else
1283 		ent->s.frame = 0;
1284 
1285 	VectorSet (ent->mins, -16, -16, 0);
1286 	VectorSet (ent->maxs, 16, 16, 16);
1287 	ent->deadflag = DEAD_DEAD;
1288 	ent->takedamage = DAMAGE_YES;
1289 	ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER;
1290 	ent->die = misc_deadsoldier_die;
1291 	ent->monsterinfo.aiflags |= AI_GOOD_GUY;
1292 
1293 	gi.linkentity (ent);
1294 }
1295 
1296 /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
1297 This is the Viper for the flyby bombing.
1298 It is trigger_spawned, so you must have something use it for it to show up.
1299 There must be a path for it to follow once it is activated.
1300 
1301 "speed"		How fast the Viper should fly
1302 */
1303 
1304 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1305 extern void func_train_find (edict_t *self);
1306 
misc_viper_use(edict_t * self,edict_t * other,edict_t * activator)1307 void misc_viper_use  (edict_t *self, edict_t *other, edict_t *activator)
1308 {
1309 	self->svflags &= ~SVF_NOCLIENT;
1310 	self->use = train_use;
1311 	train_use (self, other, activator);
1312 }
1313 
SP_misc_viper(edict_t * ent)1314 void SP_misc_viper (edict_t *ent)
1315 {
1316 	if (!ent->target)
1317 	{
1318 		gi.dprintf ("misc_viper without a target at %s\n", vtos(ent->absmin));
1319 		G_FreeEdict (ent);
1320 		return;
1321 	}
1322 
1323 	if (!ent->speed)
1324 		ent->speed = 300;
1325 
1326 	ent->movetype = MOVETYPE_PUSH;
1327 	ent->solid = SOLID_NOT;
1328 	ent->s.modelindex = gi.modelindex ("models/ships/viper/tris.md2");
1329 	VectorSet (ent->mins, -16, -16, 0);
1330 	VectorSet (ent->maxs, 16, 16, 32);
1331 
1332 	ent->think = func_train_find;
1333 	ent->nextthink = level.time + FRAMETIME;
1334 	ent->use = misc_viper_use;
1335 	ent->svflags |= SVF_NOCLIENT;
1336 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1337 
1338 	gi.linkentity (ent);
1339 }
1340 
1341 
1342 /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72)
1343 This is a large stationary viper as seen in Paul's intro
1344 */
SP_misc_bigviper(edict_t * ent)1345 void SP_misc_bigviper (edict_t *ent)
1346 {
1347 	ent->movetype = MOVETYPE_NONE;
1348 	ent->solid = SOLID_BBOX;
1349 	VectorSet (ent->mins, -176, -120, -24);
1350 	VectorSet (ent->maxs, 176, 120, 72);
1351 	ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2");
1352 	gi.linkentity (ent);
1353 }
1354 
1355 
1356 /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
1357 "dmg"	how much boom should the bomb make?
1358 */
misc_viper_bomb_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1359 void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1360 {
1361 	G_UseTargets (self, self->activator);
1362 
1363 	self->s.origin[2] = self->absmin[2] + 1;
1364 	T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB);
1365 	BecomeExplosion2 (self);
1366 }
1367 
misc_viper_bomb_prethink(edict_t * self)1368 void misc_viper_bomb_prethink (edict_t *self)
1369 {
1370 	vec3_t	v;
1371 	float	diff;
1372 
1373 	self->groundentity = NULL;
1374 
1375 	diff = self->timestamp - level.time;
1376 	if (diff < -1.0)
1377 		diff = -1.0;
1378 
1379 	VectorScale (self->moveinfo.dir, 1.0 + diff, v);
1380 	v[2] = diff;
1381 
1382 	diff = self->s.angles[2];
1383 	vectoangles (v, self->s.angles);
1384 	self->s.angles[2] = diff + 10;
1385 }
1386 
misc_viper_bomb_use(edict_t * self,edict_t * other,edict_t * activator)1387 void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator)
1388 {
1389 	edict_t	*viper;
1390 
1391 	self->solid = SOLID_BBOX;
1392 	self->svflags &= ~SVF_NOCLIENT;
1393 	self->s.effects |= EF_ROCKET;
1394 	self->use = NULL;
1395 	self->movetype = MOVETYPE_TOSS;
1396 	self->prethink = misc_viper_bomb_prethink;
1397 	self->touch = misc_viper_bomb_touch;
1398 	self->activator = activator;
1399 
1400 	viper = G_Find (NULL, FOFS(classname), "misc_viper");
1401 	VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
1402 
1403 	self->timestamp = level.time;
1404 	VectorCopy (viper->moveinfo.dir, self->moveinfo.dir);
1405 }
1406 
SP_misc_viper_bomb(edict_t * self)1407 void SP_misc_viper_bomb (edict_t *self)
1408 {
1409 	self->movetype = MOVETYPE_NONE;
1410 	self->solid = SOLID_NOT;
1411 	VectorSet (self->mins, -8, -8, -8);
1412 	VectorSet (self->maxs, 8, 8, 8);
1413 
1414 	self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2");
1415 
1416 	if (!self->dmg)
1417 		self->dmg = 1000;
1418 
1419 	self->use = misc_viper_bomb_use;
1420 	self->svflags |= SVF_NOCLIENT;
1421 
1422 	gi.linkentity (self);
1423 }
1424 
1425 
1426 /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
1427 This is a Storgg ship for the flybys.
1428 It is trigger_spawned, so you must have something use it for it to show up.
1429 There must be a path for it to follow once it is activated.
1430 
1431 "speed"		How fast it should fly
1432 */
1433 
1434 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1435 extern void func_train_find (edict_t *self);
1436 
misc_strogg_ship_use(edict_t * self,edict_t * other,edict_t * activator)1437 void misc_strogg_ship_use  (edict_t *self, edict_t *other, edict_t *activator)
1438 {
1439 	self->svflags &= ~SVF_NOCLIENT;
1440 	self->use = train_use;
1441 	train_use (self, other, activator);
1442 }
1443 
SP_misc_strogg_ship(edict_t * ent)1444 void SP_misc_strogg_ship (edict_t *ent)
1445 {
1446 	if (!ent->target)
1447 	{
1448 		gi.dprintf ("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
1449 		G_FreeEdict (ent);
1450 		return;
1451 	}
1452 
1453 	if (!ent->speed)
1454 		ent->speed = 300;
1455 
1456 	ent->movetype = MOVETYPE_PUSH;
1457 	ent->solid = SOLID_NOT;
1458 	ent->s.modelindex = gi.modelindex ("models/ships/strogg1/tris.md2");
1459 	VectorSet (ent->mins, -16, -16, 0);
1460 	VectorSet (ent->maxs, 16, 16, 32);
1461 
1462 	ent->think = func_train_find;
1463 	ent->nextthink = level.time + FRAMETIME;
1464 	ent->use = misc_strogg_ship_use;
1465 	ent->svflags |= SVF_NOCLIENT;
1466 	ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1467 
1468 	gi.linkentity (ent);
1469 }
1470 
1471 
1472 /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
1473 */
misc_satellite_dish_think(edict_t * self)1474 void misc_satellite_dish_think (edict_t *self)
1475 {
1476 	self->s.frame++;
1477 	if (self->s.frame < 38)
1478 		self->nextthink = level.time + FRAMETIME;
1479 }
1480 
misc_satellite_dish_use(edict_t * self,edict_t * other,edict_t * activator)1481 void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator)
1482 {
1483 	self->s.frame = 0;
1484 	self->think = misc_satellite_dish_think;
1485 	self->nextthink = level.time + FRAMETIME;
1486 }
1487 
SP_misc_satellite_dish(edict_t * ent)1488 void SP_misc_satellite_dish (edict_t *ent)
1489 {
1490 	ent->movetype = MOVETYPE_NONE;
1491 	ent->solid = SOLID_BBOX;
1492 	VectorSet (ent->mins, -64, -64, 0);
1493 	VectorSet (ent->maxs, 64, 64, 128);
1494 	ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2");
1495 	ent->use = misc_satellite_dish_use;
1496 	gi.linkentity (ent);
1497 }
1498 
1499 
1500 /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
1501 */
SP_light_mine1(edict_t * ent)1502 void SP_light_mine1 (edict_t *ent)
1503 {
1504 	ent->movetype = MOVETYPE_NONE;
1505 	ent->solid = SOLID_BBOX;
1506 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2");
1507 	gi.linkentity (ent);
1508 }
1509 
1510 
1511 /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
1512 */
SP_light_mine2(edict_t * ent)1513 void SP_light_mine2 (edict_t *ent)
1514 {
1515 	ent->movetype = MOVETYPE_NONE;
1516 	ent->solid = SOLID_BBOX;
1517 	ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2");
1518 	gi.linkentity (ent);
1519 }
1520 
1521 
1522 /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
1523 Intended for use with the target_spawner
1524 */
SP_misc_gib_arm(edict_t * ent)1525 void SP_misc_gib_arm (edict_t *ent)
1526 {
1527 	gi.setmodel (ent, "models/objects/gibs/arm/tris.md2");
1528 	ent->solid = SOLID_NOT;
1529 	ent->s.effects |= EF_GIB;
1530 	ent->takedamage = DAMAGE_YES;
1531 	ent->die = gib_die;
1532 	ent->movetype = MOVETYPE_TOSS;
1533 	ent->svflags |= SVF_MONSTER;
1534 	ent->deadflag = DEAD_DEAD;
1535 	ent->avelocity[0] = random()*200;
1536 	ent->avelocity[1] = random()*200;
1537 	ent->avelocity[2] = random()*200;
1538 	ent->think = G_FreeEdict;
1539 	ent->nextthink = level.time + 30;
1540 	gi.linkentity (ent);
1541 }
1542 
1543 /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
1544 Intended for use with the target_spawner
1545 */
SP_misc_gib_leg(edict_t * ent)1546 void SP_misc_gib_leg (edict_t *ent)
1547 {
1548 	gi.setmodel (ent, "models/objects/gibs/leg/tris.md2");
1549 	ent->solid = SOLID_NOT;
1550 	ent->s.effects |= EF_GIB;
1551 	ent->takedamage = DAMAGE_YES;
1552 	ent->die = gib_die;
1553 	ent->movetype = MOVETYPE_TOSS;
1554 	ent->svflags |= SVF_MONSTER;
1555 	ent->deadflag = DEAD_DEAD;
1556 	ent->avelocity[0] = random()*200;
1557 	ent->avelocity[1] = random()*200;
1558 	ent->avelocity[2] = random()*200;
1559 	ent->think = G_FreeEdict;
1560 	ent->nextthink = level.time + 30;
1561 	gi.linkentity (ent);
1562 }
1563 
1564 /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
1565 Intended for use with the target_spawner
1566 */
SP_misc_gib_head(edict_t * ent)1567 void SP_misc_gib_head (edict_t *ent)
1568 {
1569 	gi.setmodel (ent, "models/objects/gibs/head/tris.md2");
1570 	ent->solid = SOLID_NOT;
1571 	ent->s.effects |= EF_GIB;
1572 	ent->takedamage = DAMAGE_YES;
1573 	ent->die = gib_die;
1574 	ent->movetype = MOVETYPE_TOSS;
1575 	ent->svflags |= SVF_MONSTER;
1576 	ent->deadflag = DEAD_DEAD;
1577 	ent->avelocity[0] = random()*200;
1578 	ent->avelocity[1] = random()*200;
1579 	ent->avelocity[2] = random()*200;
1580 	ent->think = G_FreeEdict;
1581 	ent->nextthink = level.time + 30;
1582 	gi.linkentity (ent);
1583 }
1584 
1585 //=====================================================
1586 
1587 /*QUAKED target_character (0 0 1) ?
1588 used with target_string (must be on same "team")
1589 "count" is position in the string (starts at 1)
1590 */
1591 
SP_target_character(edict_t * self)1592 void SP_target_character (edict_t *self)
1593 {
1594 	self->movetype = MOVETYPE_PUSH;
1595 	gi.setmodel (self, self->model);
1596 	self->solid = SOLID_BSP;
1597 	self->s.frame = 12;
1598 	gi.linkentity (self);
1599 	return;
1600 }
1601 
1602 
1603 /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
1604 */
1605 
target_string_use(edict_t * self,edict_t * other,edict_t * activator)1606 void target_string_use (edict_t *self, edict_t *other, edict_t *activator)
1607 {
1608 	edict_t *e;
1609 	int		n, l;
1610 	char	c;
1611 
1612 	l = strlen(self->message);
1613 	for (e = self->teammaster; e; e = e->teamchain)
1614 	{
1615 		if (!e->count)
1616 			continue;
1617 		n = e->count - 1;
1618 		if (n > l)
1619 		{
1620 			e->s.frame = 12;
1621 			continue;
1622 		}
1623 
1624 		c = self->message[n];
1625 		if (c >= '0' && c <= '9')
1626 			e->s.frame = c - '0';
1627 		else if (c == '-')
1628 			e->s.frame = 10;
1629 		else if (c == ':')
1630 			e->s.frame = 11;
1631 		else
1632 			e->s.frame = 12;
1633 	}
1634 }
1635 
SP_target_string(edict_t * self)1636 void SP_target_string (edict_t *self)
1637 {
1638 	if (!self->message)
1639 		self->message = "";
1640 	self->use = target_string_use;
1641 }
1642 
1643 
1644 /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
1645 target a target_string with this
1646 
1647 The default is to be a time of day clock
1648 
1649 TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
1650 If START_OFF, this entity must be used before it starts
1651 
1652 "style"		0 "xx"
1653 			1 "xx:xx"
1654 			2 "xx:xx:xx"
1655 */
1656 
1657 #define	CLOCK_MESSAGE_SIZE	16
1658 
1659 // don't let field width of any clock messages change, or it
1660 // could cause an overwrite after a game load
1661 
func_clock_reset(edict_t * self)1662 static void func_clock_reset (edict_t *self)
1663 {
1664 	self->activator = NULL;
1665 	if (self->spawnflags & 1)
1666 	{
1667 		self->health = 0;
1668 		self->wait = self->count;
1669 	}
1670 	else if (self->spawnflags & 2)
1671 	{
1672 		self->health = self->count;
1673 		self->wait = 0;
1674 	}
1675 }
1676 
func_clock_format_countdown(edict_t * self)1677 static void func_clock_format_countdown (edict_t *self)
1678 {
1679 	if (self->style == 0)
1680 	{
1681 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
1682 		return;
1683 	}
1684 
1685 	if (self->style == 1)
1686 	{
1687 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
1688 		if (self->message[3] == ' ')
1689 			self->message[3] = '0';
1690 		return;
1691 	}
1692 
1693 	if (self->style == 2)
1694 	{
1695 		Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
1696 		if (self->message[3] == ' ')
1697 			self->message[3] = '0';
1698 		if (self->message[6] == ' ')
1699 			self->message[6] = '0';
1700 		return;
1701 	}
1702 }
1703 
func_clock_think(edict_t * self)1704 void func_clock_think (edict_t *self)
1705 {
1706 	if (!self->enemy)
1707 	{
1708 		self->enemy = G_Find (NULL, FOFS(targetname), self->target);
1709 		if (!self->enemy)
1710 			return;
1711 	}
1712 
1713 	if (self->spawnflags & 1)
1714 	{
1715 		func_clock_format_countdown (self);
1716 		self->health++;
1717 	}
1718 	else if (self->spawnflags & 2)
1719 	{
1720 		func_clock_format_countdown (self);
1721 		self->health--;
1722 	}
1723 	else
1724 	{
1725 		struct tm	*ltime;
1726 		time_t		gmtime;
1727 
1728 		time(&gmtime);
1729 		ltime = localtime(&gmtime);
1730 		Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
1731 		if (self->message[3] == ' ')
1732 			self->message[3] = '0';
1733 		if (self->message[6] == ' ')
1734 			self->message[6] = '0';
1735 	}
1736 
1737 	self->enemy->message = self->message;
1738 	self->enemy->use (self->enemy, self, self);
1739 
1740 	if (((self->spawnflags & 1) && (self->health > self->wait)) ||
1741 		((self->spawnflags & 2) && (self->health < self->wait)))
1742 	{
1743 		if (self->pathtarget)
1744 		{
1745 			char *savetarget;
1746 			char *savemessage;
1747 
1748 			savetarget = self->target;
1749 			savemessage = self->message;
1750 			self->target = self->pathtarget;
1751 			self->message = NULL;
1752 			G_UseTargets (self, self->activator);
1753 			self->target = savetarget;
1754 			self->message = savemessage;
1755 		}
1756 
1757 		if (!(self->spawnflags & 8))
1758 			return;
1759 
1760 		func_clock_reset (self);
1761 
1762 		if (self->spawnflags & 4)
1763 			return;
1764 	}
1765 
1766 	self->nextthink = level.time + 1;
1767 }
1768 
func_clock_use(edict_t * self,edict_t * other,edict_t * activator)1769 void func_clock_use (edict_t *self, edict_t *other, edict_t *activator)
1770 {
1771 	if (!(self->spawnflags & 8))
1772 		self->use = NULL;
1773 	if (self->activator)
1774 		return;
1775 	self->activator = activator;
1776 	self->think (self);
1777 }
1778 
SP_func_clock(edict_t * self)1779 void SP_func_clock (edict_t *self)
1780 {
1781 	if (!self->target)
1782 	{
1783 		gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
1784 		G_FreeEdict (self);
1785 		return;
1786 	}
1787 
1788 	if ((self->spawnflags & 2) && (!self->count))
1789 	{
1790 		gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
1791 		G_FreeEdict (self);
1792 		return;
1793 	}
1794 
1795 	if ((self->spawnflags & 1) && (!self->count))
1796 		self->count = 60*60;;
1797 
1798 	func_clock_reset (self);
1799 
1800 	self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL);
1801 
1802 	self->think = func_clock_think;
1803 
1804 	if (self->spawnflags & 4)
1805 		self->use = func_clock_use;
1806 	else
1807 		self->nextthink = level.time + 1;
1808 }
1809 
1810 //=================================================================================
1811 
teleporter_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1812 void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1813 {
1814 	edict_t		*dest;
1815 	int			i;
1816 
1817 	if (!other->client)
1818 		return;
1819 	dest = G_Find (NULL, FOFS(targetname), self->target);
1820 	if (!dest)
1821 	{
1822 		gi.dprintf ("Couldn't find destination\n");
1823 		return;
1824 	}
1825 
1826 //ZOID
1827 	CTFPlayerResetGrapple(other);
1828 //ZOID
1829 
1830 	// unlink to make sure it can't possibly interfere with KillBox
1831 	gi.unlinkentity (other);
1832 
1833 	VectorCopy (dest->s.origin, other->s.origin);
1834 	VectorCopy (dest->s.origin, other->s.old_origin);
1835 	other->s.origin[2] += 10;
1836 
1837 	// clear the velocity and hold them in place briefly
1838 	VectorClear (other->velocity);
1839 	other->client->ps.pmove.pm_time = 160>>3;		// hold time
1840 	other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
1841 
1842 	// draw the teleport splash at source and on the player
1843 	self->owner->s.event = EV_PLAYER_TELEPORT;
1844 	other->s.event = EV_PLAYER_TELEPORT;
1845 
1846 	// set angles
1847 	for (i=0 ; i<3 ; i++)
1848 		other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
1849 
1850 	VectorClear (other->s.angles);
1851 	VectorClear (other->client->ps.viewangles);
1852 	VectorClear (other->client->v_angle);
1853 
1854 	// kill anything at the destination
1855 	KillBox (other);
1856 
1857 	gi.linkentity (other);
1858 }
1859 
1860 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
1861 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
1862 */
SP_misc_teleporter(edict_t * ent)1863 void SP_misc_teleporter (edict_t *ent)
1864 {
1865 	edict_t		*trig;
1866 
1867 	if (!ent->target)
1868 	{
1869 		gi.dprintf ("teleporter without a target.\n");
1870 		G_FreeEdict (ent);
1871 		return;
1872 	}
1873 
1874 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
1875 	ent->s.skinnum = 1;
1876 	ent->s.effects = EF_TELEPORTER;
1877 	ent->s.sound = gi.soundindex ("world/amb10.wav");
1878 	ent->solid = SOLID_BBOX;
1879 
1880 	VectorSet (ent->mins, -32, -32, -24);
1881 	VectorSet (ent->maxs, 32, 32, -16);
1882 	gi.linkentity (ent);
1883 
1884 	trig = G_Spawn ();
1885 	trig->touch = teleporter_touch;
1886 	trig->solid = SOLID_TRIGGER;
1887 	trig->target = ent->target;
1888 	trig->owner = ent;
1889 	VectorCopy (ent->s.origin, trig->s.origin);
1890 	VectorSet (trig->mins, -8, -8, 8);
1891 	VectorSet (trig->maxs, 8, 8, 24);
1892 	gi.linkentity (trig);
1893 
1894 }
1895 
1896 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
1897 Point teleporters at these.
1898 */
SP_misc_teleporter_dest(edict_t * ent)1899 void SP_misc_teleporter_dest (edict_t *ent)
1900 {
1901 	gi.setmodel (ent, "models/objects/dmspot/tris.md2");
1902 	ent->s.skinnum = 0;
1903 	ent->solid = SOLID_BBOX;
1904 //	ent->s.effects |= EF_FLIES;
1905 	VectorSet (ent->mins, -32, -32, -24);
1906 	VectorSet (ent->maxs, 32, 32, -16);
1907 	gi.linkentity (ent);
1908 }
1909 
1910