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