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