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