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