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