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