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