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