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