1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // g_misc.c
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "g_local.h"
27 
28 
29 /*QUAKED func_group (0 0 0) ?
30 Used to group brushes together just for editor convenience.
31 */
32 
33 //=====================================================
34 
Use_Areaportal(edict_t * ent,edict_t * other,edict_t * activator)35 void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
36 {
37 	ent->count ^= 1;		// toggle state
38 //	gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
39 	gi.SetAreaPortalState (ent->style, ent->count);
40 }
41 
42 /*QUAKED func_areaportal (0 0 0) ?
43 
44 This is a non-visible object that divides the world into
45 areas that are seperated when this portal is not activated.
46 Usually enclosed in the middle of a door.
47 */
SP_func_areaportal(edict_t * ent)48 void SP_func_areaportal (edict_t *ent)
49 {
50 	ent->use = Use_Areaportal;
51 	ent->count = 0;		// always start closed;
52 }
53 
54 //=====================================================
55 
56 
57 /*
58 =================
59 Misc functions
60 =================
61 */
VelocityForDamage(int damage,vec3_t v)62 void VelocityForDamage (int damage, vec3_t v)
63 {
64 	v[0] = 100.0 * crandom();
65 	v[1] = 100.0 * crandom();
66 	v[2] = 200.0 + 100.0 * random();
67 
68 	if (damage < 50)
69 		VectorScale (v, 0.7, v);
70 	else
71 		VectorScale (v, 1.2, v);
72 }
73 
ClipGibVelocity(edict_t * ent)74 void ClipGibVelocity (edict_t *ent)
75 {
76 	if (ent->velocity[0] < -300)
77 		ent->velocity[0] = -300;
78 	else if (ent->velocity[0] > 300)
79 		ent->velocity[0] = 300;
80 	if (ent->velocity[1] < -300)
81 		ent->velocity[1] = -300;
82 	else if (ent->velocity[1] > 300)
83 		ent->velocity[1] = 300;
84 	if (ent->velocity[2] < 200)
85 		ent->velocity[2] = 200;	// always some upwards
86 	else if (ent->velocity[2] > 500)
87 		ent->velocity[2] = 500;
88 }
89 
90 
91 /*
92 =================
93 gibs
94 =================
95 */
gib_think(edict_t * self)96 void gib_think (edict_t *self)
97 {
98 	self->s.frame++;
99 	self->nextthink = level.time + FRAMETIME;
100 
101 	if (self->s.frame > 9)
102 	{
103 		self->think = G_FreeEdict;
104 		self->nextthink = level.time + 2 + random()*2;
105 	}
106 }
107 
gib_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)108 void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
109 {
110 	vec3_t	normal_angles, right;
111 
112 	if (!self->groundentity)
113 		return;
114 
115 	self->touch = NULL;
116 
117 	if (plane)
118 	{
119 		gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
120 
121 		vectoangles (plane->normal, normal_angles);
122 		AngleVectors (normal_angles, NULL, right, NULL);
123 		vectoangles (right, self->s.angles);
124 
125 		if (self->s.modelindex == sm_meat_index)
126 		{
127 			self->s.frame = 0;//++;
128 			self->think = gib_think;
129 			self->nextthink = level.time + FRAMETIME;
130 		}
131 	}
132 }
133 
gib_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)134 void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
135 {
136 	G_FreeEdict (self);
137 }
138 
ThrowGib(edict_t * self,char * gibname,int damage,int type,int effects)139 void ThrowGib (edict_t *self, char *gibname, int damage, int type, int effects)
140 {
141 	edict_t *gib;
142 	vec3_t	vd;
143 	vec3_t	origin;
144 	vec3_t	size;
145 	float	vscale;
146 
147 	gib = G_Spawn();
148 
149 	VectorScale (self->size, 1, size);
150 	VectorAdd (self->absmin, size, origin);
151 	gib->s.origin[0] = origin[0] + crandom() * size[0];
152 	gib->s.origin[1] = origin[1] + crandom() * size[1];
153 	gib->s.origin[2] = origin[2] + crandom() * size[2];
154 
155 	gi.setmodel (gib, gibname);
156 	gib->solid = SOLID_NOT;
157 	gib->s.effects = effects;
158 	gib->flags |= FL_NO_KNOCKBACK;
159 	gib->takedamage = DAMAGE_YES;
160 	gib->die = gib_die;
161 
162 	if (type == GIB_ORGANIC)
163 	{
164 		gib->movetype = MOVETYPE_BOUNCE;
165 		gib->touch = gib_touch;
166 		vscale = 0.2;
167 	}
168 	else
169 	{
170 		gib->movetype = MOVETYPE_BOUNCE;
171 		vscale = 1.0;
172 	}
173 
174 	VelocityForDamage (damage, vd);
175 	VectorMA (self->velocity, vscale, vd, gib->velocity);
176 	ClipGibVelocity (gib);
177 	gib->avelocity[0] = random()*600;
178 	gib->avelocity[1] = random()*600;
179 	gib->avelocity[2] = random()*600;
180 
181 	gib->think = G_FreeEdict;
182 	gib->nextthink = level.time + 2 + random()*2;
183 
184 	gi.linkentity (gib);
185 }
186 
ThrowClientHead(edict_t * self,int damage)187 void ThrowClientHead (edict_t *self, int damage)
188 {
189 	vec3_t	vd;
190 	char	*gibname;
191 
192 	gibname = "models/objects/gibs/sm_meat/tris.md2";
193 		self->s.skinnum = 0;
194 
195 	self->s.origin[2] += 32;
196 	self->s.frame = 0;
197 	gi.setmodel (self, gibname);
198 	VectorSet (self->mins, -16, -16, 0);
199 	VectorSet (self->maxs, 16, 16, 16);
200 
201 	self->takedamage = DAMAGE_NO;
202 	self->solid = SOLID_NOT;
203 	self->s.effects = EF_GIB;
204 	self->s.sound = 0;
205 	self->flags |= FL_NO_KNOCKBACK;
206 
207 	self->movetype = MOVETYPE_TOSS;
208 	VelocityForDamage (damage, vd);
209 	VectorAdd (self->velocity, vd, self->velocity);
210 
211 	if (self->client)	// bodies in the queue don't have a client anymore
212 	{
213 		self->client->anim_priority = ANIM_DEATH;
214 		self->client->anim_end = self->s.frame;
215 	}
216 	else
217 	{
218 		self->think = NULL;
219 		self->nextthink = 0;
220 	}
221 
222 	gi.linkentity (self);
223 }
224 
225 
226 /*
227 =================
228 debris
229 =================
230 */
debris_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)231 void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
232 {
233 	G_FreeEdict (self);
234 }
235 
ThrowDebris(edict_t * self,char * modelname,float speed,vec3_t origin)236 void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
237 {
238 	edict_t	*chunk;
239 	vec3_t	v;
240 
241 	chunk = G_Spawn();
242 	VectorCopy (origin, chunk->s.origin);
243 	gi.setmodel (chunk, modelname);
244 	v[0] = 100 * crandom();
245 	v[1] = 100 * crandom();
246 	v[2] = 100 + 100 * crandom();
247 	VectorMA (self->velocity, speed, v, chunk->velocity);
248 	chunk->movetype = MOVETYPE_BOUNCE;
249 	chunk->solid = SOLID_NOT;
250 	chunk->avelocity[0] = random()*600;
251 	chunk->avelocity[1] = random()*600;
252 	chunk->avelocity[2] = random()*600;
253 	chunk->s.effects |= EF_ROCKET;
254 	chunk->think = G_FreeEdict;
255 	chunk->nextthink = level.time + 5 + random()*5;
256 	chunk->s.frame = 0;
257 	chunk->flags = 0;
258 	chunk->classname = "debris";
259 	chunk->takedamage = DAMAGE_YES;
260 	chunk->die = debris_die;
261 	gi.linkentity (chunk);
262 }
263 
ThrowDebris2(edict_t * self,char * modelname,float speed,vec3_t origin)264 void ThrowDebris2 (edict_t *self, char *modelname, float speed, vec3_t origin)
265 {
266 	edict_t	*chunk;
267 	vec3_t	v;
268 
269 	chunk = G_Spawn();
270 	VectorCopy (origin, chunk->s.origin);
271 	gi.setmodel (chunk, modelname);
272 	v[0] = 100 * crandom();
273 	v[1] = 100 * crandom();
274 	v[2] = 100 + 100 * crandom();
275 	VectorMA (self->velocity, speed, v, chunk->velocity);
276 	chunk->movetype = MOVETYPE_BOUNCE;
277 	chunk->solid = SOLID_NOT;
278 	chunk->avelocity[0] = random()*600;
279 	chunk->avelocity[1] = random()*600;
280 	chunk->avelocity[2] = random()*600;
281 	chunk->think = G_FreeEdict;
282 	chunk->nextthink = level.time + 5 + random()*5;
283 	chunk->s.frame = 0;
284 	chunk->flags = 0;
285 	chunk->classname = "debris";
286 	chunk->takedamage = DAMAGE_YES;
287 	chunk->die = debris_die;
288 	gi.linkentity (chunk);
289 }
290 
BecomeExplosion1(edict_t * self)291 void BecomeExplosion1 (edict_t *self)
292 {
293 	gi.WriteByte (svc_temp_entity);
294 	gi.WriteByte (TE_EXPLOSION1);
295 	gi.WritePosition (self->s.origin);
296 	gi.multicast (self->s.origin, MULTICAST_PVS);
297 
298 	G_FreeEdict (self);
299 }
300 
301 
BecomeExplosion2(edict_t * self)302 void BecomeExplosion2 (edict_t *self)
303 {
304 	gi.WriteByte (svc_temp_entity);
305 	gi.WriteByte (TE_EXPLOSION2);
306 	gi.WritePosition (self->s.origin);
307 	gi.multicast (self->s.origin, MULTICAST_PVS);
308 
309 	G_FreeEdict (self);
310 }
311 
312 
313 /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
314 Target: next path corner
315 Pathtarget: gets used when an entity that has
316 	this path_corner targeted touches it
317 */
318 
path_corner_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)319 void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
320 {
321 	vec3_t		v;
322 	edict_t		*next;
323 
324 	if (other->movetarget != self)
325 		return;
326 
327 	if (other->enemy)
328 		return;
329 
330 	if (self->pathtarget)
331 	{
332 		char *savetarget;
333 
334 		savetarget = self->target;
335 		self->target = self->pathtarget;
336 		G_UseTargets (self, other);
337 		self->target = savetarget;
338 	}
339 
340 	if (self->target)
341 		next = G_PickTarget(self->target);
342 	else
343 		next = NULL;
344 
345 	if ((next) && (next->spawnflags & 1))
346 	{
347 		VectorCopy (next->s.origin, v);
348 		v[2] += next->mins[2];
349 		v[2] -= other->mins[2];
350 		VectorCopy (v, other->s.origin);
351 		next = G_PickTarget(next->target);
352 		other->s.event = EV_OTHER_TELEPORT;
353 	}
354 
355 	other->goalentity = other->movetarget = next;
356 
357 	if (self->wait)
358 	{
359 		other->monsterinfo.pausetime = level.time + self->wait;
360 		other->monsterinfo.stand (other);
361 		return;
362 	}
363 
364 	if (!other->movetarget)
365 	{
366 		other->monsterinfo.pausetime = level.time + 100000000;
367 		other->monsterinfo.stand (other);
368 	}
369 	else
370 	{
371 		VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
372 		other->ideal_yaw = vectoyaw (v);
373 	}
374 }
375 
SP_path_corner(edict_t * self)376 void SP_path_corner (edict_t *self)
377 {
378 	if (!self->targetname)
379 	{
380 		gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
381 		G_FreeEdict (self);
382 		return;
383 	}
384 
385 	self->solid = SOLID_TRIGGER;
386 	self->touch = path_corner_touch;
387 	VectorSet (self->mins, -8, -8, -8);
388 	VectorSet (self->maxs, 8, 8, 8);
389 	self->svflags |= SVF_NOCLIENT;
390 	gi.linkentity (self);
391 }
392 
393 
394 /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
395 Makes this the target of a monster and it will head here
396 when first activated before going after the activator.  If
397 hold is selected, it will stay here.
398 */
point_combat_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)399 void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
400 {
401 	edict_t	*activator;
402 
403 	if (other->movetarget != self)
404 		return;
405 
406 	if (self->target)
407 	{
408 		other->target = self->target;
409 		other->goalentity = other->movetarget = G_PickTarget(other->target);
410 		if (!other->goalentity)
411 		{
412 			gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
413 			other->movetarget = self;
414 		}
415 		self->target = NULL;
416 	}
417 	else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
418 	{
419 		other->monsterinfo.pausetime = level.time + 100000000;
420 		other->monsterinfo.aiflags |= AI_STAND_GROUND;
421 		other->monsterinfo.stand (other);
422 	}
423 
424 	if (other->movetarget == self)
425 	{
426 		other->target = NULL;
427 		other->movetarget = NULL;
428 		other->goalentity = other->enemy;
429 		other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
430 	}
431 
432 	if (self->pathtarget)
433 	{
434 		char *savetarget;
435 
436 		savetarget = self->target;
437 		self->target = self->pathtarget;
438 		if (other->enemy && other->enemy->client)
439 			activator = other->enemy;
440 		else if (other->oldenemy && other->oldenemy->client)
441 			activator = other->oldenemy;
442 		else if (other->activator && other->activator->client)
443 			activator = other->activator;
444 		else
445 			activator = other;
446 		G_UseTargets (self, activator);
447 		self->target = savetarget;
448 	}
449 }
450 
SP_point_combat(edict_t * self)451 void SP_point_combat (edict_t *self)
452 {
453 	if (deathmatch->value)
454 	{
455 		G_FreeEdict (self);
456 		return;
457 	}
458 	self->solid = SOLID_TRIGGER;
459 	self->touch = point_combat_touch;
460 	VectorSet (self->mins, -8, -8, -16);
461 	VectorSet (self->maxs, 8, 8, 16);
462 	self->svflags = SVF_NOCLIENT;
463 	gi.linkentity (self);
464 };
465 
466 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
467 Used as a positional target for spotlights, etc.
468 */
SP_info_null(edict_t * self)469 void SP_info_null (edict_t *self)
470 {
471 	G_FreeEdict (self);
472 };
473 
474 
475 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
476 Used as a positional target for lightning.
477 */
SP_info_notnull(edict_t * self)478 void SP_info_notnull (edict_t *self)
479 {
480 	VectorCopy (self->s.origin, self->absmin);
481 	VectorCopy (self->s.origin, self->absmax);
482 };
483 
484 
485 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
486 Non-displayed light.
487 Default light value is 300.
488 Default style is 0.
489 If targeted, will toggle between on and off.
490 Default _cone value is 10 (used to set size of light for spotlights)
491 */
492 
493 #define START_OFF	1
494 
light_use(edict_t * self,edict_t * other,edict_t * activator)495 static void light_use (edict_t *self, edict_t *other, edict_t *activator)
496 {
497 	if (self->spawnflags & START_OFF)
498 	{
499 		gi.configstring (CS_LIGHTS+self->style, "m");
500 		self->spawnflags &= ~START_OFF;
501 	}
502 	else
503 	{
504 		gi.configstring (CS_LIGHTS+self->style, "a");
505 		self->spawnflags |= START_OFF;
506 	}
507 }
508 
SP_light(edict_t * self)509 void SP_light (edict_t *self)
510 {
511 	// no targeted lights in deathmatch, because they cause global messages
512 	if (!self->targetname || deathmatch->value)
513 	{
514 		G_FreeEdict (self);
515 		return;
516 	}
517 
518 	if (self->style >= 32)
519 	{
520 		self->use = light_use;
521 		if (self->spawnflags & START_OFF)
522 			gi.configstring (CS_LIGHTS+self->style, "a");
523 		else
524 			gi.configstring (CS_LIGHTS+self->style, "m");
525 	}
526 }
527 
528 
529 /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
530 This is just a solid wall if not inhibited
531 
532 TRIGGER_SPAWN	the wall will not be present until triggered
533 				it will then blink in to existance; it will
534 				kill anything that was in it's way
535 
536 TOGGLE			only valid for TRIGGER_SPAWN walls
537 				this allows the wall to be turned on and off
538 
539 START_ON		only valid for TRIGGER_SPAWN walls
540 				the wall will initially be present
541 */
542 
func_wall_use(edict_t * self,edict_t * other,edict_t * activator)543 void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
544 {
545 	if (self->solid == SOLID_NOT)
546 	{
547 		self->solid = SOLID_BSP;
548 		self->svflags &= ~SVF_NOCLIENT;
549 		KillBox (self);
550 	}
551 	else
552 	{
553 		self->solid = SOLID_NOT;
554 		self->svflags |= SVF_NOCLIENT;
555 	}
556 	gi.linkentity (self);
557 
558 	if (!(self->spawnflags & 2))
559 		self->use = NULL;
560 }
561 
SP_func_wall(edict_t * self)562 void SP_func_wall (edict_t *self)
563 {
564 	self->movetype = MOVETYPE_PUSH;
565 	gi.setmodel (self, self->model);
566 
567 	if (self->spawnflags & 8)
568 		self->s.effects |= EF_ANIM_ALL;
569 	if (self->spawnflags & 16)
570 		self->s.effects |= EF_ANIM_ALLFAST;
571 
572 	// just a wall
573 	if ((self->spawnflags & 7) == 0)
574 	{
575 		self->solid = SOLID_BSP;
576 		gi.linkentity (self);
577 		return;
578 	}
579 
580 	// it must be TRIGGER_SPAWN
581 	if (!(self->spawnflags & 1))
582 	{
583 //		gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
584 		self->spawnflags |= 1;
585 	}
586 
587 	// yell if the spawnflags are odd
588 	if (self->spawnflags & 4)
589 	{
590 		if (!(self->spawnflags & 2))
591 		{
592 			gi.dprintf("func_wall START_ON without TOGGLE\n");
593 			self->spawnflags |= 2;
594 		}
595 	}
596 
597 	self->use = func_wall_use;
598 	if (self->spawnflags & 4)
599 	{
600 		self->solid = SOLID_BSP;
601 	}
602 	else
603 	{
604 		self->solid = SOLID_NOT;
605 		self->svflags |= SVF_NOCLIENT;
606 	}
607 	gi.linkentity (self);
608 }
609 
610 
611 /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
612 This is solid bmodel that will fall if it's support it removed.
613 */
614 
func_object_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)615 void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
616 {
617 	// only squash thing we fall on top of
618 	if (!plane)
619 		return;
620 	if (plane->normal[2] < 1.0)
621 		return;
622 	if (other->takedamage == DAMAGE_NO)
623 		return;
624 	T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
625 }
626 
func_object_release(edict_t * self)627 void func_object_release (edict_t *self)
628 {
629 	self->movetype = MOVETYPE_TOSS;
630 	self->touch = func_object_touch;
631 }
632 
func_object_use(edict_t * self,edict_t * other,edict_t * activator)633 void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
634 {
635 	self->solid = SOLID_BSP;
636 	self->svflags &= ~SVF_NOCLIENT;
637 	self->use = NULL;
638 	KillBox (self);
639 	func_object_release (self);
640 }
641 
SP_func_object(edict_t * self)642 void SP_func_object (edict_t *self)
643 {
644 	gi.setmodel (self, self->model);
645 
646 	self->mins[0] += 1;
647 	self->mins[1] += 1;
648 	self->mins[2] += 1;
649 	self->maxs[0] -= 1;
650 	self->maxs[1] -= 1;
651 	self->maxs[2] -= 1;
652 
653 	if (!self->dmg)
654 		self->dmg = 100;
655 
656 	if (self->spawnflags == 0)
657 	{
658 		self->solid = SOLID_BSP;
659 		self->movetype = MOVETYPE_PUSH;
660 		self->think = func_object_release;
661 		self->nextthink = level.time + 2 * FRAMETIME;
662 	}
663 	else
664 	{
665 		self->solid = SOLID_NOT;
666 		self->movetype = MOVETYPE_PUSH;
667 		self->use = func_object_use;
668 		self->svflags |= SVF_NOCLIENT;
669 	}
670 
671 	if (self->spawnflags & 2)
672 		self->s.effects |= EF_ANIM_ALL;
673 	if (self->spawnflags & 4)
674 		self->s.effects |= EF_ANIM_ALLFAST;
675 
676 	self->clipmask = MASK_MONSTERSOLID;
677 
678 	gi.linkentity (self);
679 }
680 
681 
682 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
683 Any brush that you want to explode or break apart.  If you want an
684 ex0plosion, set dmg and it will do a radius explosion of that amount
685 at the center of the bursh.
686 
687 If targeted it will not be shootable.
688 
689 health defaults to 100.
690 
691 mass defaults to 75.  This determines how much debris is emitted when
692 it explodes.  You get one large chunk per 100 of mass (up to 8) and
693 one small chunk per 25 of mass (up to 16).  So 800 gives the most.
694 */
func_explosive_explode(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)695 void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
696 {
697 	vec3_t	origin;
698 	vec3_t	chunkorigin;
699 	vec3_t	size;
700 	int		count;
701 	int		mass;
702 
703 	// bmodel origins are (0 0 0), we need to adjust that here
704 	VectorScale (self->size, 0.5, size);
705 	VectorAdd (self->absmin, size, origin);
706 	VectorCopy (origin, self->s.origin);
707 
708 	self->takedamage = DAMAGE_NO;
709 
710 	if (self->dmg)
711 		T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE, -1);
712 
713 	VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
714 	VectorNormalize (self->velocity);
715 	VectorScale (self->velocity, 150, self->velocity);
716 
717 	// start chunks towards the center
718 	VectorScale (size, 0.5, size);
719 
720 	mass = self->mass;
721 	if (!mass)
722 		mass = 75;
723 
724 	// big chunks
725 	if (mass >= 100)
726 	{
727 		count = mass / 100;
728 		if (count > 8)
729 			count = 8;
730 		while(count--)
731 		{
732 			chunkorigin[0] = origin[0] + crandom() * size[0];
733 			chunkorigin[1] = origin[1] + crandom() * size[1];
734 			chunkorigin[2] = origin[2] + crandom() * size[2];
735 			ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin);
736 		}
737 	}
738 
739 	// small chunks
740 	count = mass / 25;
741 	if (count > 16)
742 		count = 16;
743 	while(count--)
744 	{
745 		chunkorigin[0] = origin[0] + crandom() * size[0];
746 		chunkorigin[1] = origin[1] + crandom() * size[1];
747 		chunkorigin[2] = origin[2] + crandom() * size[2];
748 		ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin);
749 	}
750 
751 	G_UseTargets (self, attacker);
752 
753 	if (self->dmg)
754 		BecomeExplosion1 (self);
755 	else
756 		G_FreeEdict (self);
757 }
758 
func_explosive_use(edict_t * self,edict_t * other,edict_t * activator)759 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
760 {
761 	func_explosive_explode (self, self, other, self->health, vec3_origin);
762 }
763 
func_explosive_spawn(edict_t * self,edict_t * other,edict_t * activator)764 void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator)
765 {
766 	self->solid = SOLID_BSP;
767 	self->svflags &= ~SVF_NOCLIENT;
768 	self->use = NULL;
769 	KillBox (self);
770 	gi.linkentity (self);
771 }
772 
SP_func_explosive(edict_t * self)773 void SP_func_explosive (edict_t *self)
774 {
775 	self->movetype = MOVETYPE_PUSH;
776 
777 	gi.modelindex ("models/objects/debris1/tris.md2");
778 	gi.modelindex ("models/objects/debris2/tris.md2");
779 
780 	gi.setmodel (self, self->model);
781 
782 	if (self->spawnflags & 1)
783 	{
784 		self->svflags |= SVF_NOCLIENT;
785 		self->solid = SOLID_NOT;
786 		self->use = func_explosive_spawn;
787 	}
788 	else
789 	{
790 		self->solid = SOLID_BSP;
791 		if (self->targetname)
792 			self->use = func_explosive_use;
793 	}
794 
795 	if (self->spawnflags & 2)
796 		self->s.effects |= EF_ANIM_ALL;
797 	if (self->spawnflags & 4)
798 		self->s.effects |= EF_ANIM_ALLFAST;
799 
800 	if (self->use != func_explosive_use)
801 	{
802 		if (!self->health)
803 			self->health = 100;
804 		self->die = func_explosive_explode;
805 		self->takedamage = DAMAGE_YES;
806 	}
807 
808 	gi.linkentity (self);
809 }
810 
811 
812 /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
813 Large exploding box.  You can override its mass (100),
814 health (80), and dmg (150).
815 */
816 
barrel_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)817 void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
818 
819 {
820 	float	ratio;
821 	vec3_t	v;
822 
823 	if ((!other->groundentity) || (other->groundentity == self))
824 		return;
825 
826 	ratio = (float)other->mass / (float)self->mass;
827 	VectorSubtract (self->s.origin, other->s.origin, v);
828 	M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
829 }
830 
barrel_explode(edict_t * self)831 void barrel_explode (edict_t *self)
832 {
833 	vec3_t	org;
834 	float	spd;
835 	vec3_t	save, size;
836 
837 	T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL, -1);
838 
839 	VectorCopy (self->s.origin, save);
840 	VectorMA (self->absmin, 0.5, self->size, self->s.origin);
841 	VectorScale (self->size, 0.5, size);
842 
843 	// a few big chunks
844 	spd = 1.5 * (float)self->dmg / 200.0;
845 	org[0] = self->s.origin[0] + crandom() * size[0];
846 	org[1] = self->s.origin[1] + crandom() * size[1];
847 	org[2] = self->s.origin[2] + crandom() * size[2];
848 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
849 	org[0] = self->s.origin[0] + crandom() * size[0];
850 	org[1] = self->s.origin[1] + crandom() * size[1];
851 	org[2] = self->s.origin[2] + crandom() * size[2];
852 	ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
853 
854 	// bottom corners
855 	spd = 1.75 * (float)self->dmg / 200.0;
856 	VectorCopy (self->absmin, org);
857 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
858 	VectorCopy (self->absmin, org);
859 	org[0] += self->size[0];
860 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
861 	VectorCopy (self->absmin, org);
862 	org[1] += self->size[1];
863 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
864 	VectorCopy (self->absmin, org);
865 	org[0] += self->size[0];
866 	org[1] += self->size[1];
867 	ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
868 
869 	// a bunch of little chunks
870 	spd = 2 * self->dmg / 200;
871 	org[0] = self->s.origin[0] + crandom() * size[0];
872 	org[1] = self->s.origin[1] + crandom() * size[1];
873 	org[2] = self->s.origin[2] + crandom() * size[2];
874 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
875 	org[0] = self->s.origin[0] + crandom() * size[0];
876 	org[1] = self->s.origin[1] + crandom() * size[1];
877 	org[2] = self->s.origin[2] + crandom() * size[2];
878 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
879 	org[0] = self->s.origin[0] + crandom() * size[0];
880 	org[1] = self->s.origin[1] + crandom() * size[1];
881 	org[2] = self->s.origin[2] + crandom() * size[2];
882 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
883 	org[0] = self->s.origin[0] + crandom() * size[0];
884 	org[1] = self->s.origin[1] + crandom() * size[1];
885 	org[2] = self->s.origin[2] + crandom() * size[2];
886 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
887 	org[0] = self->s.origin[0] + crandom() * size[0];
888 	org[1] = self->s.origin[1] + crandom() * size[1];
889 	org[2] = self->s.origin[2] + crandom() * size[2];
890 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
891 	org[0] = self->s.origin[0] + crandom() * size[0];
892 	org[1] = self->s.origin[1] + crandom() * size[1];
893 	org[2] = self->s.origin[2] + crandom() * size[2];
894 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
895 	org[0] = self->s.origin[0] + crandom() * size[0];
896 	org[1] = self->s.origin[1] + crandom() * size[1];
897 	org[2] = self->s.origin[2] + crandom() * size[2];
898 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
899 	org[0] = self->s.origin[0] + crandom() * size[0];
900 	org[1] = self->s.origin[1] + crandom() * size[1];
901 	org[2] = self->s.origin[2] + crandom() * size[2];
902 	ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
903 
904 	VectorCopy (save, self->s.origin);
905 	if (self->groundentity)
906 		BecomeExplosion2 (self);
907 	else
908 		BecomeExplosion1 (self);
909 }
910 
barrel_delay(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)911 void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
912 {
913 	self->takedamage = DAMAGE_NO;
914 	self->nextthink = level.time + 2 * FRAMETIME;
915 	self->think = barrel_explode;
916 	self->activator = attacker;
917 }
918 
919 //=================================================================================
920 
teleporter_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)921 void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
922 {
923 	edict_t		*dest;
924 	int			i;
925 
926 	if (!other->client)
927 		return;
928 	dest = G_Find (NULL, FOFS(targetname), self->target);
929 	if (!dest)
930 	{
931 		gi.dprintf ("Couldn't find destination\n");
932 		return;
933 	}
934 
935 	//ZOID
936 	CTFPlayerResetGrapple(other);
937 	//ZOID
938 
939 	// unlink to make sure it can't possibly interfere with KillBox
940 	gi.unlinkentity (other);
941 
942 	VectorCopy (dest->s.origin, other->s.origin);
943 	VectorCopy (dest->s.origin, other->s.old_origin);
944 	other->s.origin[2] += 10;
945 
946 	// clear the velocity and hold them in place briefly
947 	VectorClear (other->velocity);
948 	other->client->ps.pmove.pm_time = 160>>3;		// hold time
949 	other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
950 
951 	// draw the teleport splash at source and on the player
952 	self->owner->s.event = EV_PLAYER_TELEPORT;
953 	other->s.event = EV_PLAYER_TELEPORT;
954 
955 	// set angles
956 	for (i=0 ; i<3 ; i++)
957 	{
958 		other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
959 	}
960 
961 	VectorClear (other->s.angles);
962 	VectorClear (other->client->ps.viewangles);
963 	VectorClear (other->client->v_angle);
964 
965 	// kill anything at the destination
966 	KillBox (other);
967 
968 	gi.linkentity (other);
969 }
970 
971 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
972 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
973 */
SP_misc_teleporter(edict_t * ent)974 void SP_misc_teleporter (edict_t *ent)
975 {
976 	edict_t		*trig;
977 
978 	if (!ent->target)
979 	{
980 		gi.dprintf ("teleporter without a target.\n");
981 		G_FreeEdict (ent);
982 		return;
983 	}
984 
985 	// The actual misc_teleporter is converted into a cosmetic object for the
986 	// glowing effect, while a new entity is spawned to act as the functioning
987 	// teleporter.
988 	gi.setmodel (ent, "models/objects/blank/tris.md2");
989 	ent->s.skinnum = 1;
990 	ent->s.effects = EF_TELEPORTER;
991 	ent->solid     = SOLID_BBOX;
992 
993 	VectorSet (ent->mins, -32, -32, -24);
994 	VectorSet (ent->maxs, 32, 32, -16);
995 	gi.linkentity (ent);
996 
997 	trig = G_Spawn ();
998 	trig->touch = teleporter_touch;
999 	trig->solid = SOLID_TRIGGER;
1000 	trig->target = ent->target;
1001 	trig->owner = ent;
1002 	VectorCopy (ent->s.origin, trig->s.origin);
1003 	VectorSet (trig->mins, -8, -8, 8);
1004 	VectorSet (trig->maxs, 8, 8, 24);
1005 	gi.linkentity (trig);
1006 
1007 }
1008 
1009 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
1010 Point teleporters at these.
1011 */
SP_misc_teleporter_dest(edict_t * ent)1012 void SP_misc_teleporter_dest (edict_t *ent)
1013 {
1014 	ent->s.modelindex = 0;
1015 	ent->s.skinnum = 0;
1016 	ent->solid = SOLID_NOT;
1017 	VectorSet (ent->mins, -32, -32, -24);
1018 	VectorSet (ent->maxs, 32, 32, -16);
1019 	gi.linkentity (ent);
1020 }
1021 
misc_mapmodel_think(edict_t * ent)1022 void misc_mapmodel_think (edict_t *ent)
1023 {
1024 	if(ent->spawnflags & 2)
1025 		ent->s.frame = (ent->s.frame + 1) % 39;
1026 	else
1027 		ent->s.frame = (ent->s.frame + 1) % 24;
1028 	ent->nextthink = level.time + FRAMETIME;
1029 }
SP_misc_mapmodel(edict_t * ent)1030 void SP_misc_mapmodel (edict_t *ent) //random .md2 map models
1031 {
1032 	gi.setmodel (ent, ent->model);
1033 
1034 	ent->solid = SOLID_NOT; //will need clipping brushes around it
1035 
1036 	//disable shadows(also disables per-pixel dynamic lighting unless minlight set
1037 	if(ent->spawnflags & 1)
1038 		ent->s.renderfx = RF_NOSHADOWS;
1039 
1040 	if(ent->spawnflags & 16)
1041 		ent->s.renderfx = RF_TRANSLUCENT;
1042 
1043 	if(ent->spawnflags & 32) { //animated mesh
1044 		if(ent->spawnflags & 128)
1045 			ent->s.frame = 0;
1046 		else
1047 			ent->s.frame = rand()%24;
1048 		ent->think = misc_mapmodel_think;
1049 		ent->nextthink = level.time + FRAMETIME;
1050 	}
1051 	else //static mesh
1052 		ent->s.frame = 0;
1053 
1054 	//allow dynamic per-pixel lighting
1055 	if(ent->spawnflags & 64)
1056 		ent->s.renderfx |= RF_MINLIGHT;
1057 
1058 	gi.linkentity (ent);
1059 }
1060 
watersplash_think(edict_t * ent)1061 void watersplash_think (edict_t *ent)
1062 {
1063 	vec3_t up;
1064 
1065 	up[0] = 0;
1066 	up[1] = 0;
1067 	up[2] = 1;
1068 
1069 	//write effect
1070 	gi.WriteByte (svc_temp_entity);
1071 	gi.WriteByte (TE_SPLASH);
1072 	gi.WriteByte (8);
1073 	gi.WritePosition (ent->s.origin);
1074 	gi.WriteDir (up);
1075 	gi.WriteByte (SPLASH_BLUE_WATER); //we should allow spawnflags to change this
1076 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1077 
1078 	ent->nextthink = level.time + 1.0;
1079 }
1080 
SP_misc_watersplash(edict_t * ent)1081 void SP_misc_watersplash (edict_t *ent)
1082 {
1083 	gi.setmodel(ent, NULL);
1084 	ent->solid = SOLID_NOT;
1085 	ent->movetype = MOVETYPE_NONE;
1086 	ent->takedamage = DAMAGE_NO;
1087 
1088 	ent->think = watersplash_think;
1089 	ent->nextthink = level.time + 0.5 + random();
1090 
1091 	gi.linkentity (ent);
1092 }
1093 
misc_electroflash_think(edict_t * ent)1094 void misc_electroflash_think (edict_t *ent)
1095 {
1096 	gi.WriteByte (svc_muzzleflash);
1097 	gi.WriteShort (ent-g_edicts);
1098 	gi.WriteByte (MZ_RAILGUN);
1099 	gi.multicast (ent->s.origin, MULTICAST_PVS);
1100 
1101 	ent->nextthink = level.time + 0.05 + random();
1102 }
1103 
SP_misc_electroflash(edict_t * ent)1104 void SP_misc_electroflash (edict_t *ent) //random electrical pulses
1105 {
1106 	gi.setmodel (ent, NULL);
1107 
1108 	ent->solid = SOLID_NOT;
1109 	ent->movetype = MOVETYPE_NONE;
1110 	ent->takedamage = DAMAGE_NO;
1111 	ent->s.sound = gi.soundindex("world/electricity.wav");
1112 	ent->think = misc_electroflash_think;
1113 	ent->nextthink = level.time + 0.05 + random();
1114 
1115 	gi.linkentity (ent);
1116 }
1117 
1118 //Team Core Assault
1119 
spidervolts(edict_t * self)1120 void spidervolts (edict_t *self)
1121 {
1122 
1123 	vec3_t	start;
1124 	vec3_t	end;
1125 	// int		damage = 30;
1126 	int		x;
1127 	int		aim;
1128 
1129 	VectorCopy (self->s.origin, start);
1130 	VectorCopy (start, end);
1131 	start[2] = start[2] + 128;
1132 	for(x = 0; x<20; x++)
1133 	{
1134 
1135 		if(random() < .5)
1136 			aim = -1000;
1137 		else
1138 			aim = 1000;
1139 		end[0] = end[0] + random()*aim;
1140 		if(random() < .5)
1141 			aim = -1000;
1142 		else
1143 			aim = 1000;
1144 		end[1] = end[1] + random()*aim;
1145 		if(random() < .5)
1146 			aim = 0;
1147 		else
1148 			aim = 1000;
1149 		end[2] = end[2] + random()*aim;
1150 
1151 
1152 		gi.WriteByte (svc_temp_entity);
1153 		gi.WriteByte (TE_LIGHTNING);
1154 		gi.WritePosition (start);
1155 		gi.WritePosition (end);
1156 		gi.multicast (start, MULTICAST_PHS);
1157 
1158 		T_RadiusDamage(self, self, 300, NULL, 800, MOD_R_SPLASH, -1);
1159 
1160 	}
1161 	gi.sound (self, CHAN_AUTO, gi.soundindex("weapons/electroball.wav"), 1, ATTN_NONE, 0);
1162 }
misc_spiderpod_think(edict_t * ent)1163 void misc_spiderpod_think (edict_t *ent)
1164 {
1165 	ent->s.frame = (ent->s.frame + 1) % 13;
1166 
1167 	//fire random bursts of electricicy
1168 	if(ent->s.frame == 10)
1169 		if(random() > 0.7)
1170 			spidervolts(ent);
1171 
1172 	ent->nextthink = level.time + FRAMETIME;
1173 }
SP_misc_spiderpod(edict_t * ent)1174 void SP_misc_spiderpod (edict_t *ent)
1175 {
1176 	ent->movetype = MOVETYPE_NONE;
1177 	ent->solid = SOLID_BBOX;
1178 
1179 	ent->s.modelindex = gi.modelindex ("models/misc/spiderpod/tris.md2");
1180 
1181 	ent->s.renderfx = (RF_FULLBRIGHT | RF_GLOW | RF_NOSHADOWS);
1182 	VectorSet (ent->mins, -64, -64, 0);
1183 	VectorSet (ent->maxs, 64, 64, 128);
1184 	ent->think = misc_spiderpod_think;
1185 	ent->nextthink = level.time + FRAMETIME;
1186 
1187 	gi.linkentity (ent);
1188 	M_droptofloor (ent);
1189 }
1190 
1191 //items for Team Core Assault mode
rednode_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1192 void rednode_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1193 {
1194 
1195 	if(other->dmteam == NO_TEAM || other->movetype == MOVETYPE_FLYMISSILE)
1196 		return; //do not allow projectiles to turn anything off
1197 
1198 	//if off, and red player, turn on
1199 	if(!ent->powered && other->client && other->dmteam == RED_TEAM) {
1200 		ent->powered = true;
1201 		red_team_score++;
1202 		if(other->client)
1203 			other->client->resp.score+=2;
1204 		//play sound, print message
1205 		gi.sound (other, CHAN_AUTO, gi.soundindex("misc/redpnenabled.wav"), 1, ATTN_NONE, 0);
1206 		safe_centerprintf(other, "Red Powernode Enabled!\n");
1207 	}
1208 
1209 	//if on, and blue player, turn off
1210 	if(ent->powered && other->client && other->dmteam == BLUE_TEAM) {
1211 		ent->powered = false;
1212 		red_team_score--;
1213 		if(other->client)
1214 			other->client->resp.score+=5;
1215 		if(red_team_score == 1) {
1216 			gi.sound (other, CHAN_AUTO, gi.soundindex("misc/redvulnerable.wav"), 1, ATTN_NONE, 0);
1217 			safe_centerprintf(other, "Red Spider Node Vulnerable!");
1218 		}
1219 		else {
1220 			gi.sound (other, CHAN_AUTO, gi.soundindex("misc/redpndisabled.wav"), 1, ATTN_NONE, 0);
1221 			safe_centerprintf(other, "Red Powernode Disabled!\n");
1222 		}
1223 	}
1224 
1225 	//if off, and blue player, nothing
1226 	//if on, and red player, nothing
1227 }
rednode_think(edict_t * ent)1228 void rednode_think (edict_t *ent)
1229 {
1230 	vec3_t start, end;
1231 
1232 	if(ent->powered){
1233 
1234 		VectorCopy(ent->s.origin, start);
1235 		start[2] -= 24;
1236 		VectorCopy(ent->s.origin, end);
1237 		end[2] += 256;
1238 
1239 		//draw a beam into the sky
1240 		gi.WriteByte (svc_temp_entity);
1241 		gi.WriteByte (TE_REDLASER);
1242 		gi.WritePosition (start);
1243 		gi.WritePosition (end);
1244 		gi.multicast (start, MULTICAST_PHS);
1245 	}
1246 
1247 	ent->nextthink = level.time + FRAMETIME;
1248 }
SP_misc_rednode(edict_t * ent)1249 void SP_misc_rednode (edict_t *ent)
1250 {
1251 	if (!tca->integer)
1252 	{
1253 		G_FreeEdict (ent);
1254 		return;
1255 	}
1256 
1257 	ent->movetype = MOVETYPE_NONE;
1258 	ent->solid = SOLID_BBOX;
1259 	ent->takedamage = DAMAGE_NO;
1260 	ent->s.modelindex = gi.modelindex ("models/objects/dmspot/tris.md2");
1261 
1262 	ent->s.renderfx = (RF_FULLBRIGHT | RF_GLOW | RF_NOSHADOWS);
1263 	VectorSet (ent->mins, -32, -32, -24);
1264 	VectorSet (ent->maxs, 32, 32, -8);
1265 
1266 	ent->s.frame = 0;
1267 	ent->powered = true; //start on
1268 
1269 	ent->touch = rednode_touch;
1270 	ent->think = rednode_think;
1271 	ent->nextthink = level.time + FRAMETIME;
1272 
1273 	gi.linkentity (ent);
1274 	M_droptofloor (ent);
1275 }
bluenode_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1276 void bluenode_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1277 {
1278 
1279 	if(other->dmteam == NO_TEAM || other->movetype == MOVETYPE_FLYMISSILE)
1280 		return; //do not allow projectiles to turn anything off
1281 
1282 	//if off, and blue player, turn on
1283 	if(!ent->powered && other->client && other->dmteam == BLUE_TEAM) {
1284 		ent->powered = true;
1285 		blue_team_score++;
1286 		if(other->client)
1287 			other->client->resp.score+=2;
1288 		gi.sound (other, CHAN_AUTO, gi.soundindex("misc/bluepnenabled.wav"), 1, ATTN_NONE, 0);
1289 		safe_centerprintf(other, "Blue Powernode Enabled!\n");
1290 	}
1291 
1292 	//if on, and red player, turn off
1293 	if(ent->powered && other->client && other->dmteam == RED_TEAM) {
1294 		ent->powered = false;
1295 		blue_team_score--;
1296 		if(other->client)
1297 			other->client->resp.score+=5;
1298 		if(blue_team_score == 1) {
1299 			gi.sound (other, CHAN_AUTO, gi.soundindex("misc/bluevulnerable.wav"), 1, ATTN_NONE, 0);
1300 			safe_centerprintf(other, "Blue Spider Node Vulnerable!\n");
1301 		}
1302 		else {
1303 			gi.sound (other, CHAN_AUTO, gi.soundindex("misc/bluepndisabled.wav"), 1, ATTN_NONE, 0);
1304 			safe_centerprintf(other, "Blue Powernode Disabled!\n");
1305 		}
1306 	}
1307 
1308 }
bluenode_think(edict_t * ent)1309 void bluenode_think (edict_t *ent)
1310 {
1311 	vec3_t start, end;
1312 
1313 	if(ent->powered){
1314 
1315 		VectorCopy(ent->s.origin, start);
1316 		start[2] -= 24;
1317 		VectorCopy(ent->s.origin, end);
1318 		end[2] += 256;
1319 
1320 		//draw a beam into the sky
1321 		gi.WriteByte (svc_temp_entity);
1322 		gi.WriteByte (TE_BLASTERBEAM);
1323 		gi.WritePosition (start);
1324 		gi.WritePosition (end);
1325 		gi.multicast (start, MULTICAST_PHS);
1326 	}
1327 
1328 	ent->nextthink = level.time + FRAMETIME;
1329 }
SP_misc_bluenode(edict_t * ent)1330 void SP_misc_bluenode (edict_t *ent)
1331 {
1332 	if (!tca->integer)
1333 	{
1334 		G_FreeEdict (ent);
1335 		return;
1336 	}
1337 
1338 	ent->movetype = MOVETYPE_NONE;
1339 	ent->solid = SOLID_BBOX;
1340 	ent->takedamage = DAMAGE_NO;
1341 
1342 	ent->s.modelindex = gi.modelindex ("models/objects/dmspot/tris.md2");
1343 
1344 	ent->s.renderfx = (RF_FULLBRIGHT | RF_GLOW | RF_NOSHADOWS);
1345 	VectorSet (ent->mins, -32, -32, -24);
1346 	VectorSet (ent->maxs, 32, 32, -8);
1347 
1348 	ent->s.frame = 0;
1349 	ent->powered = true; //start on
1350 
1351 	gi.linkentity (ent);
1352 	ent->touch = bluenode_touch;
1353 	ent->think = bluenode_think;
1354 	ent->nextthink = level.time + FRAMETIME;
1355 
1356 	M_droptofloor (ent);
1357 }
redspidernode_think(edict_t * ent)1358 void redspidernode_think (edict_t *ent)
1359 {
1360 	//just sits there pulsing
1361 	if(red_team_score < 2) //now it can take damage
1362 		ent->takedamage = DAMAGE_YES;
1363 	else
1364 		ent->takedamage = DAMAGE_NO;
1365 
1366 	ent->s.frame = (ent->s.frame + 1) % 13;
1367 	ent->nextthink = level.time + FRAMETIME;
1368 }
red_roundend(edict_t * ent)1369 void red_roundend(edict_t *ent)
1370 {
1371 	red_team_score = 0;
1372 	blue_team_matches++;
1373 }
redspidernode_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1374 void redspidernode_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1375 {
1376 	self->takedamage = DAMAGE_NO;
1377 	self->activator = attacker;
1378 
1379 	gi.WriteByte (svc_temp_entity);
1380 	gi.WriteByte (TE_BFG_BIGEXPLOSION);
1381 	gi.WritePosition (self->s.origin);
1382 	gi.multicast (self->s.origin, MULTICAST_PHS);
1383 
1384 	if(attacker->dmteam == BLUE_TEAM && attacker->client)
1385 		attacker->client->resp.score+=50;
1386 
1387 	gi.sound (self, CHAN_AUTO, gi.soundindex("players/martiancyborg/death1.wav"), 1, ATTN_NONE, 0);
1388 	self->think = red_roundend;
1389 	self->nextthink = level.time + 2;
1390 }
SP_misc_redspidernode(edict_t * ent)1391 void SP_misc_redspidernode (edict_t *ent)
1392 {
1393 	if (!tca->integer)
1394 	{
1395 		G_FreeEdict (ent);
1396 		return;
1397 	}
1398 
1399 	ent->movetype = MOVETYPE_NONE;
1400 	ent->solid = SOLID_BBOX;
1401 	ent->takedamage = DAMAGE_NO;
1402 
1403 	ent->s.modelindex = gi.modelindex ("models/misc/spiderpod/tris.md2");
1404 	ent->s.modelindex3 = gi.modelindex ("models/misc/spiderpod/helmet.md2");
1405 
1406 	ent->s.renderfx = (RF_FULLBRIGHT | RF_GLOW);
1407 	VectorSet (ent->mins, -64, -64, 0);
1408 	VectorSet (ent->maxs, 64, 64, 128);
1409 	ent->health = 600;
1410 	ent->die = redspidernode_die;
1411 	ent->think = redspidernode_think;
1412 	ent->nextthink = level.time + FRAMETIME;
1413 
1414 	gi.linkentity (ent);
1415 	M_droptofloor (ent);
1416 }
bluespidernode_think(edict_t * ent)1417 void bluespidernode_think (edict_t *ent)
1418 {
1419 	//just sits there pulsing
1420 	if(blue_team_score < 2) //now it can take damage
1421 		ent->takedamage = DAMAGE_YES;
1422 	else
1423 		ent->takedamage = DAMAGE_NO;
1424 
1425 	ent->s.frame = (ent->s.frame + 1) % 13;
1426 	ent->nextthink = level.time + FRAMETIME;
1427 }
blue_roundend(edict_t * self)1428 void blue_roundend(edict_t *self)
1429 {
1430 	blue_team_score = 0;
1431 	red_team_matches++;
1432 }
bluespidernode_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1433 void bluespidernode_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1434 {
1435 	self->takedamage = DAMAGE_NO;
1436 	self->activator = attacker;
1437 
1438 	gi.WriteByte (svc_temp_entity);
1439 	gi.WriteByte (TE_BFG_BIGEXPLOSION);
1440 	gi.WritePosition (self->s.origin);
1441 	gi.multicast (self->s.origin, MULTICAST_PHS);
1442 
1443 	if(attacker->dmteam == RED_TEAM && attacker->client)
1444 		attacker->client->resp.score+=50;
1445 
1446 	gi.sound (self, CHAN_AUTO, gi.soundindex("players/martiancyborg/death1.wav"), 1, ATTN_NONE, 0);
1447 	self->think = blue_roundend;
1448 	self->nextthink = level.time + 2;
1449 }
SP_misc_bluespidernode(edict_t * ent)1450 void SP_misc_bluespidernode (edict_t *ent)
1451 {
1452 	if (!tca->integer)
1453 	{
1454 		G_FreeEdict (ent);
1455 		return;
1456 	}
1457 
1458 	ent->movetype = MOVETYPE_NONE;
1459 	ent->solid = SOLID_BBOX;
1460 	ent->takedamage = DAMAGE_NO;
1461 
1462 	ent->s.modelindex = gi.modelindex ("models/misc/spiderpod/tris.md2");
1463 	ent->s.modelindex3 = gi.modelindex ("models/misc/spiderpod/helmet.md2");
1464 
1465 	ent->s.renderfx = (RF_FULLBRIGHT | RF_GLOW);
1466 	VectorSet (ent->mins, -64, -64, 0);
1467 	VectorSet (ent->maxs, 64, 64, 128);
1468 	ent->health = 600;
1469 	ent->die = bluespidernode_die;
1470 	ent->think = bluespidernode_think;
1471 	ent->nextthink = level.time + FRAMETIME;
1472 
1473 	gi.linkentity (ent);
1474 	M_droptofloor (ent);
1475 }
1476 
1477 //Tactical base items
1478 //Rules:  When a computer is destroyed, that base's turrents/deathrays will behave eratically, even firing on it's own team on occasion.  Laser barriers shut off.
1479 //When a powersource is destroyed, the backup generators(if not already destroyed) for the computer and ammo generator turn on(generator models will animate, emit sound).  Ammo will generate from depot at half speed.
1480 //Laser barriers shut off, turrets and deathrays are weak.
1481 //When an ammo depot is destroyed, ammo stops being produced.
1482 //When all three are disabled, the other team wins.
1483 
1484 //lasers
misc_laser_think(edict_t * self)1485 void misc_laser_think (edict_t *self)
1486 {
1487 	edict_t *ent;
1488 	edict_t	*ignore;
1489 	vec3_t	start;
1490 	vec3_t	end;
1491 	trace_t	tr;
1492 	vec3_t	point;
1493 	vec3_t	last_movedir;
1494 	int		count;
1495 
1496 	if(self->spawnflags & 1)
1497 	{
1498 		if(!tacticalScore.humanComputer || !tacticalScore.humanPowerSource)
1499 		{
1500 			self->nextthink = 0;
1501 			return;
1502 		}
1503 	}
1504 	else if(!tacticalScore.alienComputer || !tacticalScore.alienPowerSource)
1505 	{
1506 		self->nextthink = 0;
1507 		return;
1508 	}
1509 
1510 	count = 8;
1511 
1512 	if (!self->enemy)
1513 	{
1514 		if (self->target)
1515 		{
1516 			ent = G_Find (NULL, FOFS(targetname), self->target);
1517 			if (!ent)
1518 				gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
1519 			self->enemy = ent;
1520 		}
1521 		else
1522 		{
1523 			G_SetMovedir (self->s.angles, self->movedir);
1524 		}
1525 	}
1526 	else
1527 	{
1528 		VectorCopy (self->movedir, last_movedir);
1529 		VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
1530 		VectorSubtract (point, self->s.origin, self->movedir);
1531 		VectorNormalize (self->movedir);
1532 	}
1533 
1534 	ignore = self;
1535 	VectorCopy (self->s.origin, start);
1536 	VectorCopy (self->enemy->s.origin, end);
1537 	while(1)
1538 	{
1539 		tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
1540 		gi.WriteByte (svc_temp_entity);
1541 		if (self->spawnflags & 1)
1542 			gi.WriteByte (TE_REDLASER);
1543 		else
1544 			gi.WriteByte (TE_LASERBEAM);
1545 		gi.WritePosition (start);
1546 		gi.WritePosition (end);
1547 		gi.multicast (start, MULTICAST_PHS);
1548 
1549 		if (!tr.ent)
1550 			break;
1551 
1552 		//don't hurt anyone on the same team as the laser - spawnflag 1 = human
1553 		if(self->spawnflags & 1)
1554 		{
1555 			if(tr.ent->ctype == 1)
1556 				break;
1557 		}
1558 		else if(!tr.ent->ctype)
1559 			break;
1560 
1561 		// hurt it if we can
1562 		if (tr.ent->takedamage)
1563 			T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
1564 
1565 		// if we hit something that's not a player, we're done
1566 		if (!tr.ent->client)
1567 		{
1568 			break;
1569 		}
1570 
1571 		ignore = tr.ent;
1572 		VectorCopy (tr.endpos, start);
1573 	}
1574 
1575 	VectorCopy (tr.endpos, self->s.old_origin);
1576 
1577 	self->nextthink = level.time + FRAMETIME;
1578 }
1579 
misc_laser_start(edict_t * self)1580 void misc_laser_start (edict_t *self)
1581 {
1582 
1583 	self->think = misc_laser_think;
1584 }
1585 
laser_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1586 void laser_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1587 {
1588 	self->takedamage = DAMAGE_NO;
1589 	self->activator = attacker;
1590 
1591 	gi.WriteByte (svc_temp_entity);
1592 	if(self->spawnflags & 1)
1593 	{
1594 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1595 	}
1596 	else
1597 	{
1598 		gi.WriteByte (TE_BFG_BIGEXPLOSION);
1599 	}
1600 	gi.WritePosition (self->s.origin);
1601 	gi.multicast (self->s.origin, MULTICAST_PHS);
1602 
1603 	gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
1604 
1605 	G_FreeEdict (self);
1606 }
1607 
SP_misc_laser(edict_t * ent)1608 void SP_misc_laser (edict_t *ent)
1609 {
1610 	if (!g_tactical->integer)
1611 	{
1612 		G_FreeEdict (ent);
1613 		return;
1614 	}
1615 
1616 	ent->movetype = MOVETYPE_NONE;
1617 	ent->solid = SOLID_BBOX;
1618 	ent->takedamage = DAMAGE_YES;
1619 	ent->health = 1000;
1620 	ent->die = laser_die;
1621 
1622 	ent->dmg = 500; //cause severe damage, especially if multiples(most cases)
1623 
1624 	if(ent->spawnflags & 1)
1625 		ent->s.modelindex = gi.modelindex ("models/tactical/human_laser.iqm");
1626 	else
1627 		ent->s.modelindex = gi.modelindex ("models/tactical/alien_laser.iqm");
1628 
1629 	VectorSet (ent->mins, -16, -16, -16);
1630 	VectorSet (ent->maxs, 16, 16, 16);
1631 
1632 	// let everything else get spawned before we start firing
1633 	ent->think = misc_laser_think;
1634 	ent->nextthink = level.time + 1;
1635 
1636 	gi.linkentity (ent);
1637 }
1638 
1639 
1640 //computers
computer_think(edict_t * ent)1641 void computer_think (edict_t *ent)
1642 {
1643 	if(ent->classname == "alien computer")
1644 		tacticalScore.alienComputerHealth = ent->health/15;
1645 	else
1646 		tacticalScore.humanComputerHealth = ent->health/15;
1647 
1648 	ent->s.frame = (ent->s.frame + 1) % 24;
1649 	ent->nextthink = level.time + FRAMETIME;
1650 }
1651 
computer_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1652 void computer_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1653 {
1654 	edict_t *cl_ent;
1655 	int i;
1656 
1657 	self->takedamage = DAMAGE_NO;
1658 	self->activator = attacker;
1659 
1660 	gi.WriteByte (svc_temp_entity);
1661 	if(self->classname == "alien computer")
1662 	{
1663 		tacticalScore.alienComputer = false;
1664 		tacticalScore.alienComputerHealth = 0;
1665 		gi.WriteByte (TE_BFG_BIGEXPLOSION);
1666 	}
1667 	else
1668 	{
1669 		tacticalScore.humanComputer = false;
1670 		tacticalScore.humanComputerHealth = 0;
1671 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1672 	}
1673 	gi.WritePosition (self->s.origin);
1674 	gi.multicast (self->s.origin, MULTICAST_PHS);
1675 
1676 	if(self->classname == "alien computer")
1677 	{
1678 		for (i=0 ; i<g_maxclients->value ; i++)
1679 		{
1680 			cl_ent = g_edicts + 1 + i;
1681 			if (!cl_ent->inuse || cl_ent->is_bot)
1682 				continue;
1683 			safe_centerprintf(cl_ent, "Alien Central Computer has been destroyed!");
1684 		}
1685 	}
1686 	else
1687 	{
1688 		for (i=0 ; i<g_maxclients->value ; i++)
1689 		{
1690 			cl_ent = g_edicts + 1 + i;
1691 			if (!cl_ent->inuse || cl_ent->is_bot)
1692 				continue;
1693 			safe_centerprintf(cl_ent, "Human Central Computer has been destroyed!");
1694 		}
1695 	}
1696 
1697 	gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
1698 
1699 	G_FreeEdict (self);
1700 }
1701 
SP_misc_aliencomputer(edict_t * ent)1702 void SP_misc_aliencomputer (edict_t *ent)
1703 {
1704 	if (!g_tactical->integer)
1705 	{
1706 		G_FreeEdict (ent);
1707 		return;
1708 	}
1709 
1710 	ent->movetype = MOVETYPE_NONE;
1711 	ent->solid = SOLID_BBOX;
1712 	ent->takedamage = DAMAGE_YES;
1713 
1714 	ent->s.modelindex = gi.modelindex ("models/tactical/alien_computer.iqm");
1715 
1716 	VectorSet (ent->mins, -64, -64, 0);
1717 	VectorSet (ent->maxs, 64, 64, 64);
1718 	ent->health = 1500;
1719 	ent->die = computer_die;
1720 	ent->think = computer_think;
1721 	ent->nextthink = level.time + FRAMETIME;
1722 	ent->classname = "alien computer";
1723 
1724 	gi.linkentity (ent);
1725 	M_droptofloor (ent);
1726 }
1727 
SP_misc_humancomputer(edict_t * ent)1728 void SP_misc_humancomputer (edict_t *ent)
1729 {
1730 	if (!g_tactical->integer)
1731 	{
1732 		G_FreeEdict (ent);
1733 		return;
1734 	}
1735 
1736 	ent->movetype = MOVETYPE_NONE;
1737 	ent->solid = SOLID_BBOX;
1738 	ent->takedamage = DAMAGE_YES;
1739 
1740 	ent->s.modelindex = gi.modelindex ("models/tactical/human_computer.iqm");
1741 
1742 	VectorSet (ent->mins, -64, -64, 0);
1743 	VectorSet (ent->maxs, 64, 64, 64);
1744 	ent->health = 1500;
1745 	ent->die = computer_die;
1746 	ent->think = computer_think;
1747 	ent->nextthink = level.time + FRAMETIME;
1748 	ent->classname = "human computer";
1749 
1750 	gi.linkentity (ent);
1751 	M_droptofloor (ent);
1752 }
1753 
1754 //power sources
powersrc_think(edict_t * ent)1755 void powersrc_think (edict_t *ent)
1756 {
1757 	if(ent->classname == "alien powersrc")
1758 		tacticalScore.alienPowerSourceHealth = ent->health/15;
1759 	else
1760 		tacticalScore.humanPowerSourceHealth = ent->health/15;
1761 	ent->s.frame = (ent->s.frame + 1) % 24;
1762 	ent->nextthink = level.time + FRAMETIME;
1763 }
1764 
powersrc_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1765 void powersrc_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1766 {
1767 	edict_t *cl_ent;
1768 	int i;
1769 
1770 	self->takedamage = DAMAGE_NO;
1771 	self->activator = attacker;
1772 
1773 	gi.WriteByte (svc_temp_entity);
1774 	if(self->classname == "alien powersrc")
1775 	{
1776 		tacticalScore.alienPowerSource = false;
1777 		tacticalScore.alienPowerSourceHealth = 0;
1778 		gi.WriteByte (TE_BFG_BIGEXPLOSION);
1779 	}
1780 	else
1781 	{
1782 		tacticalScore.humanPowerSource = false;
1783 		tacticalScore.humanPowerSourceHealth = 0;
1784 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1785 	}
1786 	gi.WritePosition (self->s.origin);
1787 	gi.multicast (self->s.origin, MULTICAST_PHS);
1788 
1789 	if(self->classname == "alien powersrc")
1790 	{
1791 		for (i=0 ; i<g_maxclients->value ; i++)
1792 		{
1793 			cl_ent = g_edicts + 1 + i;
1794 			if (!cl_ent->inuse || cl_ent->is_bot)
1795 				continue;
1796 			safe_centerprintf(cl_ent, "Alien Power Source has been destroyed!");
1797 		}
1798 	}
1799 	else
1800 	{
1801 		for (i=0 ; i<g_maxclients->value ; i++)
1802 		{
1803 			cl_ent = g_edicts + 1 + i;
1804 			if (!cl_ent->inuse || cl_ent->is_bot)
1805 				continue;
1806 			safe_centerprintf(cl_ent, "Human Power Source has been destroyed!");
1807 		}
1808 	}
1809 
1810 	gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
1811 
1812 	G_FreeEdict (self);
1813 }
1814 
SP_misc_alienpowersrc(edict_t * ent)1815 void SP_misc_alienpowersrc (edict_t *ent)
1816 {
1817 	if (!g_tactical->integer)
1818 	{
1819 		G_FreeEdict (ent);
1820 		return;
1821 	}
1822 
1823 	ent->movetype = MOVETYPE_NONE;
1824 	ent->solid = SOLID_BBOX;
1825 	ent->takedamage = DAMAGE_YES;
1826 
1827 	ent->s.modelindex = gi.modelindex ("models/tactical/alien_powersrc.iqm");
1828 
1829 	VectorSet (ent->mins, -64, -64, 0);
1830 	VectorSet (ent->maxs, 64, 64, 72);
1831 	ent->health = 1500;
1832 	ent->die = powersrc_die;
1833 	ent->think = powersrc_think;
1834 	ent->nextthink = level.time + FRAMETIME;
1835 	ent->classname = "alien powersrc";
1836 
1837 	gi.linkentity (ent);
1838 	M_droptofloor (ent);
1839 }
1840 
SP_misc_humanpowersrc(edict_t * ent)1841 void SP_misc_humanpowersrc (edict_t *ent)
1842 {
1843 	if (!g_tactical->integer)
1844 	{
1845 		G_FreeEdict (ent);
1846 		return;
1847 	}
1848 
1849 	ent->movetype = MOVETYPE_NONE;
1850 	ent->solid = SOLID_BBOX;
1851 	ent->takedamage = DAMAGE_YES;
1852 
1853 	ent->s.modelindex = gi.modelindex ("models/tactical/human_powersrc.iqm");
1854 
1855 	VectorSet (ent->mins, -32, -32, 0);
1856 	VectorSet (ent->maxs, 32, 32, 72);
1857 	ent->health = 1500;
1858 	ent->die = powersrc_die;
1859 	ent->think = powersrc_think;
1860 	ent->nextthink = level.time + FRAMETIME;
1861 	ent->classname = "human powersrc";
1862 
1863 	gi.linkentity (ent);
1864 	M_droptofloor (ent);
1865 }
1866 
1867 //ammo depots
ammodepot_think(edict_t * ent)1868 void ammodepot_think (edict_t *ent)
1869 {
1870 	if(ent->classname == "alien ammodepot")
1871 		tacticalScore.alienAmmoDepotHealth = ent->health/15;
1872 	else
1873 		tacticalScore.humanAmmoDepotHealth = ent->health/15;
1874 	ent->nextthink = level.time + FRAMETIME;
1875 }
1876 
ammodepot_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1877 void ammodepot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1878 {
1879 	edict_t	*cl_ent;
1880 	int i;
1881 
1882 	self->takedamage = DAMAGE_NO;
1883 	self->activator = attacker;
1884 
1885 	gi.WriteByte (svc_temp_entity);
1886 	if(self->classname == "alien ammodepot")
1887 	{
1888 		tacticalScore.alienAmmoDepot = false;
1889 		tacticalScore.alienAmmoDepotHealth = 0;
1890 		gi.WriteByte (TE_BFG_BIGEXPLOSION);
1891 	}
1892 	else
1893 	{
1894 		tacticalScore.humanAmmoDepot = false;
1895 		tacticalScore.humanAmmoDepotHealth = 0;
1896 		gi.WriteByte (TE_ROCKET_EXPLOSION);
1897 	}
1898 	gi.WritePosition (self->s.origin);
1899 	gi.multicast (self->s.origin, MULTICAST_PHS);
1900 
1901 	if(self->classname == "alien ammodepot")
1902 	{
1903 		for (i=0 ; i<g_maxclients->value ; i++)
1904 		{
1905 			cl_ent = g_edicts + 1 + i;
1906 			if (!cl_ent->inuse || cl_ent->is_bot)
1907 				continue;
1908 			safe_centerprintf(cl_ent, "Alien Ammo Depot has been destroyed!");
1909 		}
1910 	}
1911 	else
1912 	{
1913 		for (i=0 ; i<g_maxclients->value ; i++)
1914 		{
1915 			cl_ent = g_edicts + 1 + i;
1916 			if (!cl_ent->inuse || cl_ent->is_bot)
1917 				continue;
1918 			safe_centerprintf(cl_ent, "Human Ammo Depot has been destroyed!");
1919 		}
1920 	}
1921 
1922 	gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
1923 
1924 	G_FreeEdict (self);
1925 }
1926 
SP_misc_alienammodepot(edict_t * ent)1927 void SP_misc_alienammodepot (edict_t *ent)
1928 {
1929 	if (!g_tactical->integer)
1930 	{
1931 		G_FreeEdict (ent);
1932 		return;
1933 	}
1934 
1935 	ent->movetype = MOVETYPE_NONE;
1936 	ent->solid = SOLID_BBOX;
1937 	ent->takedamage = DAMAGE_YES;
1938 
1939 	ent->s.modelindex = gi.modelindex ("models/tactical/ammopad.md2");
1940 
1941 	VectorSet (ent->mins, -32, -32, 0);
1942 	VectorSet (ent->maxs, 32, 32, 16);
1943 	ent->health = 1500;
1944 	ent->die = ammodepot_die;
1945 	ent->think = ammodepot_think;
1946 	ent->nextthink = level.time + FRAMETIME;
1947 	ent->classname = "alien ammodepot";
1948 
1949 	gi.linkentity (ent);
1950 	M_droptofloor (ent);
1951 }
1952 
SP_misc_humanammodepot(edict_t * ent)1953 void SP_misc_humanammodepot (edict_t *ent)
1954 {
1955 	if (!g_tactical->integer)
1956 	{
1957 		G_FreeEdict (ent);
1958 		return;
1959 	}
1960 
1961 	ent->movetype = MOVETYPE_NONE;
1962 	ent->solid = SOLID_BBOX;
1963 	ent->takedamage = DAMAGE_YES;
1964 
1965 	ent->s.modelindex = gi.modelindex ("maps/meshes/flagpad.md2");
1966 
1967 	VectorSet (ent->mins, -32, -32, 0);
1968 	VectorSet (ent->maxs, 32, 32, 16);
1969 	ent->health = 1500;
1970 	ent->die = ammodepot_die;
1971 	ent->think = ammodepot_think;
1972 	ent->nextthink = level.time + FRAMETIME;
1973 	ent->classname = "human ammodepot";
1974 
1975 	gi.linkentity (ent);
1976 	M_droptofloor (ent);
1977 }
1978 
1979 //Backup generators
backupgen_think(edict_t * ent)1980 void backupgen_think (edict_t *ent)
1981 {
1982 	if(ent->classname == "alien backupgen")
1983 	{
1984 		if(!tacticalScore.alienPowerSource)
1985 		{
1986 			//animate
1987 			ent->s.frame = (ent->s.frame + 1) % 24;
1988 			gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/reject.wav"), 1, ATTN_STATIC, 0);
1989 		}
1990 	}
1991 	else
1992 	{
1993 		if(!tacticalScore.humanPowerSource)
1994 		{
1995 			ent->s.frame = (ent->s.frame + 1) % 24;
1996 			gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/reject.wav"), 1, ATTN_STATIC, 0);
1997 		}
1998 	}
1999 	ent->nextthink = level.time + FRAMETIME;
2000 }
2001 
backupgen_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)2002 void backupgen_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
2003 {
2004 	edict_t *cl_ent;
2005 	int i;
2006 
2007 	self->takedamage = DAMAGE_NO;
2008 	self->activator = attacker;
2009 
2010 	gi.WriteByte (svc_temp_entity);
2011 	if(self->classname == "alien backupgen")
2012 	{
2013 		tacticalScore.alienBackupGen = false;
2014 		gi.WriteByte (TE_BFG_BIGEXPLOSION);
2015 	}
2016 	else
2017 	{
2018 		tacticalScore.humanBackupGen = false;
2019 		gi.WriteByte (TE_ROCKET_EXPLOSION);
2020 	}
2021 	gi.WritePosition (self->s.origin);
2022 	gi.multicast (self->s.origin, MULTICAST_PHS);
2023 
2024 	if(self->classname == "alien backupgen")
2025 	{
2026 		for (i=0 ; i<g_maxclients->value ; i++)
2027 		{
2028 			cl_ent = g_edicts + 1 + i;
2029 			if (!cl_ent->inuse || cl_ent->is_bot)
2030 				continue;
2031 			safe_centerprintf(cl_ent, "Alien Backup Generator has been destroyed!");
2032 		}
2033 	}
2034 	else
2035 	{
2036 		for (i=0 ; i<g_maxclients->value ; i++)
2037 		{
2038 			cl_ent = g_edicts + 1 + i;
2039 			if (!cl_ent->inuse || cl_ent->is_bot)
2040 				continue;
2041 			safe_centerprintf(cl_ent, "Human Backup Generator has been destroyed!");
2042 		}
2043 	}
2044 
2045 	gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
2046 
2047 	G_FreeEdict (self);
2048 }
2049 
SP_misc_alienbackupgen(edict_t * ent)2050 void SP_misc_alienbackupgen (edict_t *ent)
2051 {
2052 	if (!g_tactical->integer)
2053 	{
2054 		G_FreeEdict (ent);
2055 		return;
2056 	}
2057 
2058 	ent->movetype = MOVETYPE_NONE;
2059 	ent->solid = SOLID_BBOX;
2060 	ent->takedamage = DAMAGE_YES;
2061 
2062 	ent->s.modelindex = gi.modelindex ("models/tactical/alien_backupgen.iqm");
2063 
2064 	VectorSet (ent->mins, -24, -24, 0);
2065 	VectorSet (ent->maxs, 24, 24, 48);
2066 	ent->health = 300;
2067 	ent->die = backupgen_die;
2068 	ent->think = backupgen_think;
2069 	ent->nextthink = level.time + FRAMETIME;
2070 	ent->classname = "alien backupgen";
2071 
2072 	gi.linkentity (ent);
2073 	M_droptofloor (ent);
2074 }
2075 
SP_misc_humanbackupgen(edict_t * ent)2076 void SP_misc_humanbackupgen (edict_t *ent)
2077 {
2078 	if (!g_tactical->integer)
2079 	{
2080 		G_FreeEdict (ent);
2081 		return;
2082 	}
2083 
2084 	ent->movetype = MOVETYPE_NONE;
2085 	ent->solid = SOLID_BBOX;
2086 	ent->takedamage = DAMAGE_YES;
2087 
2088 	ent->s.modelindex = gi.modelindex ("models/tactical/human_backupgen.iqm");
2089 
2090 	VectorSet (ent->mins, -24, -24, 0);
2091 	VectorSet (ent->maxs, 24, 24, 48);
2092 	ent->health = 300;
2093 	ent->die = backupgen_die;
2094 	ent->think = backupgen_think;
2095 	ent->nextthink = level.time + FRAMETIME;
2096 	ent->classname = "human backupgen";
2097 
2098 	gi.linkentity (ent);
2099 	M_droptofloor (ent);
2100 }
2101