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