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