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