1 // g_misc.c
2
3 #include "g_local.h"
4
5 //Time befor gib etc starts to fade away...
6 #define FADE 3
7
8 /*QUAKED func_group (0 0 0) ?
9 Used to group brushes together just for editor convenience.
10 */
11
12 //=====================================================
13
Use_Areaportal(edict_t * ent,edict_t * other,edict_t * activator)14 void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
15 {
16 ent->count ^= 1; // toggle state
17 // gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
18 gi.SetAreaPortalState (ent->style, ent->count);
19 }
20
21 /*QUAKED func_areaportal (0 0 0) ?
22
23 This is a non-visible object that divides the world into
24 areas that are seperated when this portal is not activated.
25 Usually enclosed in the middle of a door.
26 */
SP_func_areaportal(edict_t * ent)27 void SP_func_areaportal (edict_t *ent)
28 {
29 ent->use = Use_Areaportal;
30 ent->count = 0; // always start closed;
31 }
32
33 //=====================================================
34
35
36 /*
37 =================
38 Misc functions
39 =================
40 */
VelocityForDamage(int damage,vec3_t v)41 void VelocityForDamage (int damage, vec3_t v)
42 {
43 v[0] = 100.0 * crandom();
44 v[1] = 100.0 * crandom();
45 v[2] = 200.0 + 100.0 * random();
46
47 if (damage < 50)
48 VectorScale (v, 0.7, v);
49 else
50 VectorScale (v, 1.2, v);
51 }
52
ClipGibVelocity(edict_t * ent)53 void ClipGibVelocity (edict_t *ent)
54 {
55 if (ent->velocity[0] < -300)
56 ent->velocity[0] = -300;
57 else if (ent->velocity[0] > 300)
58 ent->velocity[0] = 300;
59 if (ent->velocity[1] < -300)
60 ent->velocity[1] = -300;
61 else if (ent->velocity[1] > 300)
62 ent->velocity[1] = 300;
63 if (ent->velocity[2] < 200)
64 ent->velocity[2] = 200; // always some upwards
65 else if (ent->velocity[2] > 500)
66 ent->velocity[2] = 500;
67 }
68
69
70 /*
71 =================
72 gibs
73 =================
74 */
gib_think(edict_t * self)75 void gib_think (edict_t *self)
76 {
77 self->s.frame++;
78 self->nextthink = level.time + FRAMETIME;
79
80 if (self->s.frame == 10)
81 {
82 self->think = G_FreeEdict;
83 self->nextthink = level.time + 8 + random()*10;
84 }
85 }
86
gib_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)87 void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
88 {
89 vec3_t normal_angles, right;
90
91 if (!self->groundentity)
92 return;
93
94 self->touch = NULL;
95
96 if (plane)
97 {
98 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0);
99
100 vectoangles (plane->normal, normal_angles);
101 AngleVectors (normal_angles, NULL, right, NULL);
102 vectoangles (right, self->s.angles);
103
104 if (self->s.modelindex == sm_meat_index)
105 {
106 self->s.frame++;
107 self->think = gib_think;
108 self->nextthink = level.time + FRAMETIME;
109 }
110 }
111 }
112
gib_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)113 void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
114 {
115 FadeDieSink (self);
116 }
117
ThrowGib(edict_t * self,char * gibname,int damage,int type)118 void ThrowGib (edict_t *self, char *gibname, int damage, int type)
119 {
120 edict_t *gib;
121 vec3_t vd;
122 vec3_t origin;
123 vec3_t size;
124 float vscale;
125
126 gib = G_Spawn();
127
128 gib->s.renderfx |= RF_IR_VISIBLE;
129
130 VectorScale (self->size, 0.5, size);
131 VectorAdd (self->absmin, size, origin);
132 gib->s.origin[0] = origin[0] + crandom() * size[0];
133 gib->s.origin[1] = origin[1] + crandom() * size[1];
134 gib->s.origin[2] = origin[2] + crandom() * size[2];
135
136 gi.setmodel (gib, gibname);
137 gib->solid = SOLID_NOT;
138 gib->s.effects |= EF_GIB;
139 gib->flags |= FL_NO_KNOCKBACK;
140 gib->takedamage = DAMAGE_YES;
141 gib->die = gib_die;
142
143 if (type == GIB_ORGANIC)
144 {
145 gib->movetype = MOVETYPE_TOSS;
146 gib->touch = gib_touch;
147 vscale = 0.5;
148 }
149 else
150 {
151 gib->movetype = MOVETYPE_BOUNCE;
152 vscale = 1.0;
153 }
154
155 VelocityForDamage (damage, vd);
156 VectorMA (self->velocity, vscale, vd, gib->velocity);
157 ClipGibVelocity (gib);
158 gib->avelocity[0] = random()*600;
159 gib->avelocity[1] = random()*600;
160 gib->avelocity[2] = random()*600;
161
162 if (damage==0)
163 {
164 VectorClear(gib->avelocity);
165 VectorClear(gib->velocity);
166 VectorSet(gib->avelocity,crandom()*20-40, crandom()*20-40, 300);
167 VectorSet(gib->velocity, crandom()*20-40, crandom()*20-40, 300);
168 }
169
170 gib->think=FadeDieSink;
171 gib->nextthink=level.time+FADE+random()*3;
172 gib->floater=1;
173
174 gi.linkentity (gib);
175 }
176
ThrowGibHead(edict_t * self,char * gibname,int damage,int type)177 void ThrowGibHead (edict_t *self, char *gibname, int damage, int type)
178 {
179 edict_t *gib;
180 vec3_t vd;
181 vec3_t origin;
182 vec3_t size;
183 float vscale;
184
185 gib = G_Spawn();
186
187 gib->s.skinnum = 0;
188 gib->s.frame = 0;
189 VectorSet (gib->mins, -16, -16, 0);
190 VectorSet (gib->maxs, 16, 16, 16);
191
192 VectorScale (self->size, 0.5, size);
193 VectorAdd (self->absmin, size, origin);
194 gib->s.origin[0] = origin[0] + crandom() * size[0];
195 gib->s.origin[1] = origin[1] + crandom() * size[1];
196 gib->s.origin[2] = origin[2] + crandom() * size[2];
197
198 gi.setmodel (gib, gibname);
199 gib->solid = SOLID_NOT;
200 gib->s.effects |= EF_GIB;
201 gib->s.renderfx |= RF_IR_VISIBLE;
202 gib->flags |= FL_NO_KNOCKBACK;
203 gib->takedamage = DAMAGE_YES;
204 gib->die = gib_die;
205
206 if (type == GIB_ORGANIC)
207 {
208 gib->movetype = MOVETYPE_TOSS;
209 gib->touch = gib_touch;
210 vscale = 0.5;
211 }
212 else
213 {
214 gib->movetype = MOVETYPE_BOUNCE;
215 vscale = 1.0;
216 }
217
218 VelocityForDamage (damage, vd);
219 VectorMA (self->velocity, vscale, vd, gib->velocity);
220 ClipGibVelocity (gib);
221 gib->avelocity[0] = random()*600;
222 gib->avelocity[1] = random()*600;
223 gib->avelocity[2] = random()*600;
224
225 gib->think=FadeDieSink;
226 gib->nextthink=level.time+FADETIME+random()*3;
227 gib->floater=1;
228
229 gi.linkentity (gib);
230 }
231
ThrowHead(edict_t * self,char * gibname,int damage,int type)232 void ThrowHead (edict_t *self, char *gibname, int damage, int type)
233 {
234 vec3_t vd;
235 float vscale;
236
237 CleanUpEnt (self);
238
239 self->s.skinnum = 0;
240 self->s.frame = 0;
241 VectorClear (self->mins);
242 VectorClear (self->maxs);
243
244 VectorSet (self->mins, -16, -16, 0);
245 VectorSet (self->maxs, 16, 16, 16);
246
247 self->s.modelindex2 = 0;
248 gi.setmodel (self, gibname);
249 self->solid = SOLID_NOT;
250 self->s.effects |= EF_GIB;
251 self->s.effects &= ~EF_FLIES;
252 self->s.sound = 0;
253 self->flags |= FL_NO_KNOCKBACK;
254 self->svflags &= ~SVF_MONSTER;
255 self->takedamage = DAMAGE_YES;
256 self->die = gib_die;
257
258 if (self->shadow)
259 G_FreeEdict(self->shadow);
260
261 self->floater=1;
262
263 self->s.renderfx |= RF_IR_VISIBLE;
264
265 if (type == GIB_ORGANIC)
266 {
267 self->movetype = MOVETYPE_TOSS;
268 self->touch = gib_touch;
269 vscale = 0.5;
270 }
271 else
272 {
273 self->movetype = MOVETYPE_BOUNCE;
274 vscale = 1.0;
275 }
276
277 VelocityForDamage (damage, vd);
278 VectorMA (self->velocity, vscale, vd, self->velocity);
279 ClipGibVelocity (self);
280
281 self->avelocity[YAW] = crandom()*600;
282
283 self->think=FadeDieSink;
284 self->nextthink=level.time+FADETIME+random()*3;
285 self->floater=1;
286
287 if (!Q_stricmp("models/objects/gibs/sm_meat/tris.md2", gibname))
288 self->nextthink=level.time+FADE+random()*3;
289
290 gi.linkentity (self);
291 }
292
293
ThrowClientHead(edict_t * self,int damage)294 void ThrowClientHead (edict_t *self, int damage)
295 {
296 vec3_t vd;
297 char *gibname;
298
299 CleanUpEnt (self);
300 //*
301 if (rand()&1)
302 {
303 gibname = "models/objects/gibs/head2/tris.md2";
304 self->s.skinnum = 1; // second skin is player
305 }
306 else
307 { //*/
308 gibname = "models/objects/gibs/skull/tris.md2";
309 self->s.skinnum = 0;
310 //*
311 } //*/
312
313 self->s.origin[2] += 32;
314 self->s.frame = 0;
315 gi.setmodel (self, gibname);
316 VectorSet (self->mins, -16, -16, 0);
317 VectorSet (self->maxs, 16, 16, 16);
318
319 self->floater = 1;
320
321 self->takedamage = DAMAGE_NO;
322 self->solid = SOLID_NOT;
323 self->s.effects = EF_GIB;
324 self->s.sound = 0;
325 self->flags |= FL_NO_KNOCKBACK;
326 self->s.frame=0;
327
328 self->movetype = MOVETYPE_BOUNCE;
329 VelocityForDamage (damage, vd);
330 VectorAdd (self->velocity, vd, self->velocity);
331 /*
332 if (self->client) // bodies in the queue don't have a client anymore
333 {
334 self->client->anim_priority = ANIM_DEATH;
335 self->client->anim_end = self->s.frame;
336 }
337 else
338 {
339 self->think = NULL;
340 self->nextthink = 0;
341
342 } //*/
343
344 self->think=FadeDieSink;
345 self->nextthink=level.time+FADETIME+random()*3;
346 self->floater=1;
347
348 gi.linkentity (self);
349 }
350
351
ThrowClientHeadNew(edict_t * self)352 edict_t * ThrowClientHeadNew (edict_t *self)
353 {
354 edict_t *head;
355 vec3_t vd;
356 char *gibname;
357
358
359 head = G_Spawn();
360 //*
361 if (rand()&1)
362 {
363 gibname = "models/objects/gibs/head2/tris.md2";
364 head->s.skinnum = 1; // second skin is player
365 }
366 else
367 { //*/
368 gibname = "models/objects/gibs/skull/tris.md2";
369 head->s.skinnum = 0;
370 //*
371 } //*/
372
373 head->s.origin[2] += 32;
374 head->s.frame = 0;
375 gi.setmodel (head, gibname);
376 VectorCopy(self->s.origin, head->s.origin);
377 VectorSet (head->mins, -20, -20, 0);
378 VectorSet (head->maxs, 20, 20, 20);
379
380 head->floater = 1;
381
382 head->takedamage = DAMAGE_NO;
383 head->solid = SOLID_NOT;
384 head->s.effects = EF_GIB;
385 head->s.sound = 0;
386 head->flags |= FL_NO_KNOCKBACK;
387 head->s.frame=0;
388
389 head->movetype = MOVETYPE_BOUNCE;
390
391 head->think=FadeDieSink;
392 head->nextthink=level.time+FADETIME+random()*3;
393 head->floater=1;
394
395 VectorClear(head->avelocity);
396 VectorClear(head->velocity);
397 VectorSet(head->avelocity,crandom()*20-40, crandom()*20-40, 300);
398 VectorSet(head->velocity, crandom()*20-40, crandom()*20-40, 300);
399
400 if (self->client)
401 head->PlayerDeadName = self->client->pers.netname;
402 else
403 head->PlayerDeadName = self->PlayerDeadName;
404
405 return head;
406 }
407
408 /*
409 =================
410 lens flares for lights etc
411 =================
412 */
413
FlareThink(edict_t * flare)414 void FlareThink (edict_t *flare)
415 {
416 int dot;
417 vec3_t forward, dir;
418
419 flare->nextthink = level.time+FRAMETIME;
420 if (!SPClient)
421 return;
422
423 AngleVectors (SPClient->client->v_angle, forward, NULL, NULL);
424 VectorSubtract(flare->s.origin, SPClient->s.origin, dir); VectorNormalize(dir);
425
426 dot = 100 * DotProduct(dir, forward);
427
428 if (dot>95)
429 flare->s.frame = 2;
430 else if (dot>85)
431 flare->s.frame = 1;
432 else
433 flare->s.frame = 0;
434 }
435
SP_LensFlare(edict_t * flare)436 void SP_LensFlare (edict_t *flare)
437 {
438 if (sv_serversideonly->value)
439 {
440 G_FreeEdict(flare);
441 return;
442 }
443
444 gi.setmodel (flare, "sprites/s_lensflare.sp2");
445
446 flare->s.frame = 0;
447
448 flare->s.effects = 0;
449 flare->s.renderfx = RF_TRANSLUCENT;
450 flare->solid = SOLID_NOT;
451 flare->svflags = SVF_DEADMONSTER;
452 flare->clipmask = 0;
453 flare->takedamage = DAMAGE_NO;
454 flare->movetype = MOVETYPE_NONE;
455
456 if (!deathmatch->value && !coop->value)
457 {
458 flare->think = FlareThink;
459 flare->nextthink = level.time+FRAMETIME;
460 }
461
462 flare->classname = "lensflare";
463
464 gi.linkentity (flare);
465 }
466
SunFlareThink(edict_t * flare)467 void SunFlareThink (edict_t *flare)
468 {
469 int dot, frametarg;
470 vec3_t forward, dir;
471
472 flare->nextthink = level.time+FRAMETIME;
473 if (!SPClient)
474 return;
475
476 AngleVectors (SPClient->client->v_angle, forward, NULL, NULL);
477 VectorSubtract(flare->s.origin, SPClient->s.origin, dir); VectorNormalize(dir);
478
479 dot = 100 * DotProduct(dir, forward);
480
481 if (dot>95)
482 frametarg = 4;
483 else if (dot>90)
484 frametarg = 3;
485 else if (dot>85)
486 frametarg = 2;
487 else if (dot>80)
488 frametarg = 1;
489 else
490 frametarg = 0;
491
492 if (flare->s.frame<frametarg)
493 flare->s.frame++;
494 else if (flare->s.frame>frametarg)
495 flare->s.frame--;
496
497 if (flare->chain)
498 {
499 flare->chain->s.frame = flare->s.frame;
500 if (flare->chain->s.frame>0)
501 flare->chain->s.frame--;
502 }
503 }
504
SunFlareChild(edict_t * master)505 edict_t *SunFlareChild (edict_t *master)
506 {
507 edict_t *flare;
508
509 flare = G_Spawn();
510
511 flare->s.modelindex = master->s.modelindex;
512 VectorCopy(master->s.origin, flare->s.origin);
513 flare->s.frame = 0;
514 flare->s.effects = 0;
515 flare->s.renderfx = RF_TRANSLUCENT;
516 flare->solid = SOLID_NOT;
517 flare->svflags = SVF_DEADMONSTER;
518 flare->clipmask = 0;
519 flare->takedamage = DAMAGE_NO;
520 flare->movetype = MOVETYPE_NONE;
521
522 gi.linkentity (flare);
523
524 return flare;
525 }
526
SP_SunFlare(edict_t * flare)527 void SP_SunFlare (edict_t *flare)
528 {
529 if (deathmatch->value || coop->value || sv_serversideonly->value)
530 {
531 G_FreeEdict(flare);
532 return;
533 }
534
535 gi.setmodel (flare, "sprites/s_sunflare.sp2");
536
537 flare->s.frame = 0;
538
539 flare->s.effects = 0;
540 flare->s.renderfx = RF_TRANSLUCENT;
541 flare->solid = SOLID_NOT;
542 flare->svflags = SVF_DEADMONSTER;
543 flare->clipmask = 0;
544 flare->takedamage = DAMAGE_NO;
545 flare->movetype = MOVETYPE_NONE;
546 flare->think = SunFlareThink;
547 flare->nextthink = level.time+FRAMETIME;
548
549 flare->chain = SunFlareChild(flare);
550
551 flare->classname = "sunflare";
552
553 gi.linkentity (flare);
554 }
555
556 /*
557 =================
558 debris
559 =================
560 */
561
LinkToBSP(edict_t * chunk,edict_t * other)562 void LinkToBSP(edict_t *chunk, edict_t *other)
563 {
564 VectorSubtract(chunk->s.origin, other->s.origin, chunk->move_origin);
565
566 //chunk->move_origin will be used to calc angle offset for rotating brushes
567
568 chunk->enemy = other;
569 chunk->movetype = MOVETYPE_MOVEWITH;
570 }
571
debris_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)572 void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
573 {
574 FadeDieSink (self);
575 }
576
ThrowDebris(edict_t * self,char * modelname,float speed,vec3_t origin)577 void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
578 {
579 edict_t *chunk;
580 vec3_t v;
581
582 chunk = G_Spawn();
583 VectorCopy (origin, chunk->s.origin);
584 gi.setmodel (chunk, modelname);
585 v[0] = 100 * crandom();
586 v[1] = 100 * crandom();
587 v[2] = 100 + 100 * crandom();
588 VectorMA (self->velocity, speed, v, chunk->velocity);
589 chunk->movetype = MOVETYPE_BOUNCE;
590 chunk->solid = SOLID_NOT;
591 chunk->avelocity[0] = random()*600;
592 chunk->avelocity[1] = random()*600;
593 chunk->avelocity[2] = random()*600;
594 chunk->think = G_FreeEdict;
595 chunk->nextthink = level.time + 2 + random()*2;
596 chunk->s.frame = 0;
597 chunk->flags = 0;
598 chunk->classname = "debris";
599 chunk->takedamage = DAMAGE_YES;
600 chunk->die = debris_die;
601 chunk->think=FadeDieSink;
602 chunk->nextthink=level.time+FADE+random()*3;
603 chunk->floater=1;
604
605 gi.linkentity (chunk);
606 }
607
sizeByType(meansodeath)608 int sizeByType(meansodeath)
609 {
610 int mod = (meansodeath & ~MOD_FRIENDLY_FIRE & ~MOD_HEAD);
611 switch (mod)
612 {
613 case MOD_BLASTER:
614 case MOD_BLASTER2:
615 case MOD_SHOTGUN:
616 case MOD_SSHOTGUN:
617 case MOD_MACHINEGUN:
618 case MOD_CHAINGUN2:
619 return 0;
620 break;
621 case MOD_CHAINGUN:
622 case MOD_SHOTGUN2:
623 case MOD_SSHOTGUN2:
624 return 1;
625 break;
626 case MOD_RAILGUN:
627 return 2;
628 break;
629 default:
630 return 3;
631 }
632 }
633
BulletMarkThink(edict_t * self)634 void BulletMarkThink (edict_t *self)
635 {
636 int i=0;
637 edict_t *findMark=NULL;
638
639 if (!self)
640 return;
641
642 while (findMark!=self)
643 {
644 findMark=bulletptr[i];
645 i++;
646 if (i>bulletmarks)
647 break;
648 }
649 for (i=i-1; i <= bulletmarks;i++)
650 bulletptr[i] = bulletptr[i+1];
651 bulletptr[bulletmarks]=NULL;
652 bulletmarks--;
653
654 G_FreeEdict(self);
655 }
656
findradiusbhole(edict_t * from,vec3_t org,float rad)657 edict_t *findradiusbhole (edict_t *from, vec3_t org, float rad)
658 {
659 vec3_t eorg;
660 int j;
661
662 if (!from)
663 from = g_edicts;
664 else
665 from++;
666 for ( ; from < &g_edicts[globals.num_edicts]; from++)
667 {
668 if (!from->inuse)
669 continue;
670 for (j=0 ; j<3 ; j++)
671 eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
672 if (VectorLength(eorg) > rad)
673 continue;
674 return from;
675 }
676
677 return NULL;
678 }
679
FindBulletMarkRadius(vec3_t start,int skin,int radius)680 qboolean FindBulletMarkRadius (vec3_t start, int skin, int radius)
681 {
682 edict_t *ent = NULL;
683
684 while ((ent = findradiusbhole(ent, start, radius)) != NULL)
685 {
686 if (!Q_stricmp (ent->classname, "bullethole") && ent->s.skinnum == skin)
687 return true;
688 }
689 return false;
690 }
691
692
vectoanglenormaled(vec3_t value1,float angleyaw,vec3_t angles)693 void vectoanglenormaled (vec3_t value1, float angleyaw, vec3_t angles)
694 {
695 float forward, yaw, pitch;
696
697 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
698 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
699 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
700
701 if (pitch < 0)
702 pitch += 360;
703
704 angles[PITCH] = -pitch;
705 angles[YAW] = yaw;
706 angles[ROLL] = yaw - angleyaw;
707 }
708
FootPrint(edict_t * ent,vec3_t start,vec3_t dir,int type,edict_t * other)709 void FootPrint (edict_t *ent, vec3_t start, vec3_t dir, int type, edict_t *other)
710 {
711 edict_t *chunk;
712
713 if (sv_serversideonly->value)
714 return;
715
716 if ((int)sv_bulletmarks->value<=0)
717 return;
718
719 if (FindBulletMarkRadius(start, type, 4))
720 return;
721
722 chunk = G_Spawn();
723
724 if (sv_bulletmarks->value <= bulletmarks)
725 BulletMarkThink(bulletptr[0]);
726
727 VectorCopy (start, chunk->s.origin);
728 vectoanglenormaled (dir, ent->s.angles[1], chunk->s.angles);
729
730 gi.setmodel (chunk, "models/objects/footprint/tris.md2");
731 chunk->s.frame = 0;
732 chunk->movetype = MOVETYPE_FLYMISSILE;
733 chunk->solid = SOLID_NOT;
734 chunk->think = BulletMarkThink;
735 chunk->nextthink = level.time + 30 + random()*10;
736 chunk->flags = 0;
737 chunk->classname = "bullethole";
738 chunk->takedamage = DAMAGE_NO;
739
740 chunk->s.skinnum = type;
741 chunk->s.renderfx = RF_TRANSLUCENT;
742
743 chunk->s.origin[2]+=0.1;
744
745 if (other && other->solid == SOLID_BSP && other->movetype == MOVETYPE_PUSH)
746 LinkToBSP(chunk, other);
747
748 gi.linkentity (chunk);
749 bulletptr[bulletmarks] = chunk;
750 bulletmarks++;
751 }
752
BulletMark(edict_t * self,trace_t * tr,int mod)753 void BulletMark (edict_t *self, trace_t *tr, int mod)
754 {
755 int size = sizeByType(mod);
756 edict_t *chunk;
757
758 if ((int)sv_bulletmarks->value<=0)
759 return;
760
761 if (FindBulletMarkRadius(tr->endpos, size, 1))
762 return;
763
764 chunk = G_Spawn();
765
766 if (sv_bulletmarks->value <= bulletmarks)
767 BulletMarkThink(bulletptr[0]);
768
769 VectorCopy (tr->endpos, chunk->s.origin);
770 vectoanglenormaled (tr->plane.normal, (30 * (int)(random()*12)), chunk->s.angles);
771
772 gi.setmodel (chunk, "models/objects/hole/tris.md2");
773 chunk->s.frame = 0;
774 chunk->movetype = MOVETYPE_NONE;
775 chunk->solid = SOLID_NOT;
776 chunk->think = BulletMarkThink;
777 chunk->nextthink = level.time + 30 + random()*10;
778 chunk->flags = 0;
779 chunk->classname = "bullethole";
780 chunk->takedamage = DAMAGE_NO;
781 chunk->s.skinnum = size;
782 chunk->s.renderfx = RF_TRANSLUCENT;
783
784 if (tr && tr->ent && tr->ent->solid == SOLID_BSP && tr->ent->movetype == MOVETYPE_PUSH)
785 LinkToBSP(chunk, tr->ent);
786
787 chunk->s.origin[2]+=0.1;
788
789 gi.linkentity (chunk);
790 bulletptr[bulletmarks] = chunk;
791 bulletmarks++;
792 }
793
BulletMarkSlow(edict_t * self,vec3_t dir,edict_t * other)794 void BulletMarkSlow (edict_t *self, vec3_t dir, edict_t *other)
795 {
796 int size = sizeByType(self->mod);
797 edict_t *chunk;
798
799 gi.WriteByte (svc_temp_entity);
800 gi.WriteByte (self->timer);
801 gi.WritePosition (self->s.origin);
802 gi.WriteDir (dir);
803 gi.multicast (self->s.origin, MULTICAST_PVS);
804
805 if ((int)sv_bulletmarks->value<=0)
806 return;
807
808 if (FindBulletMarkRadius(self->s.origin, size, 1))
809 return;
810
811 if (sv_bulletmarks->value <= bulletmarks)
812 BulletMarkThink(bulletptr[0]);
813
814 chunk = G_Spawn();
815
816 VectorCopy (self->s.origin, chunk->s.origin);
817 vectoanglenormaled (dir, (30 * (int)(random()*12)), chunk->s.angles);
818
819 if (!sv_serversideonly->value)
820 gi.setmodel (chunk, "models/objects/hole/tris.md2");
821 else
822 gi.setmodel (chunk, "models/objects/flash/tris.md2");
823 chunk->s.frame = 0;
824 chunk->movetype = MOVETYPE_NONE;
825 chunk->solid = SOLID_NOT;
826 chunk->think = BulletMarkThink;
827 chunk->nextthink = level.time + 30 + random()*10;
828 chunk->flags = 0;
829 chunk->classname = "bullethole";
830 chunk->takedamage = DAMAGE_NO;
831 chunk->s.skinnum = size;
832
833 if (other && other->solid == SOLID_BSP && other->movetype == MOVETYPE_PUSH)
834 LinkToBSP(chunk, other);
835
836 chunk->s.origin[2]+=0.1;
837
838 chunk->s.renderfx = RF_TRANSLUCENT;
839 gi.linkentity (chunk);
840 bulletptr[bulletmarks] = chunk;
841 bulletmarks++;
842 }
843
ExplodeMark(edict_t * self,vec3_t origin,float time)844 void ExplodeMark (edict_t *self, vec3_t origin, float time)
845 {
846 edict_t *chunk;
847 chunk = G_Spawn();
848 VectorCopy (origin, chunk->s.origin);
849
850 gi.setmodel (chunk, "models/objects/flash/tris.md2");
851 chunk->s.frame = 0;
852 chunk->s.renderfx = RF_BEAM;
853 chunk->s.effects = EF_TRACKER; //EF_TRACKER;
854 chunk->movetype = MOVETYPE_NONE;
855 chunk->solid = SOLID_NOT;
856 chunk->think = G_FreeEdict;
857 chunk->nextthink = time;
858 chunk->flags = 0;
859 chunk->classname = "debris";
860 chunk->takedamage = DAMAGE_NO;
861 gi.linkentity (chunk);
862 }
863
BecomeExplosion1(edict_t * self)864 void BecomeExplosion1 (edict_t *self)
865 {
866 if (sv_sprite_explosions->value)
867 {
868 self->s.origin[2]+=30;
869 sprite_explosion (self->s.origin, 0, NULL);
870 }
871 else
872 {
873 gi.WriteByte (svc_temp_entity);
874 gi.WriteByte (TE_EXPLOSION1);
875 gi.WritePosition (self->s.origin);
876 gi.multicast (self->s.origin, MULTICAST_PVS);
877 }
878
879 G_FreeEdict (self);
880 }
881
882
BecomeExplosion2(edict_t * self)883 void BecomeExplosion2 (edict_t *self)
884 {
885 if (sv_sprite_explosions->value)
886 {
887 self->s.origin[2]+=50;
888 sprite_explosion (self->s.origin, 1, NULL);
889 }
890 else
891 {
892 gi.WriteByte (svc_temp_entity);
893 gi.WriteByte (TE_EXPLOSION2);
894 gi.WritePosition (self->s.origin);
895 gi.multicast (self->s.origin, MULTICAST_PVS);
896 }
897
898 G_FreeEdict (self);
899 }
900
901
902 /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
903 Target: next path corner
904 Pathtarget: gets used when an entity that has
905 this path_corner targeted touches it
906 */
907
path_corner_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)908 void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
909 {
910 vec3_t v;
911 edict_t *next;
912
913 if (other->movetarget != self)
914 return;
915
916 if (other->enemy)
917 return;
918
919 if (self->pathtarget)
920 {
921 char *savetarget;
922
923 savetarget = self->target;
924 self->target = self->pathtarget;
925 G_UseTargets (self, other);
926 self->target = savetarget;
927 }
928
929 if (self->target)
930 next = G_PickTarget(self->target);
931 else
932 next = NULL;
933
934 if ((next) && (next->spawnflags & 1))
935 {
936 VectorCopy (next->s.origin, v);
937 v[2] += next->mins[2];
938 v[2] -= other->mins[2];
939 VectorCopy (v, other->s.origin);
940 next = G_PickTarget(next->target);
941 other->s.event = EV_OTHER_TELEPORT;
942 }
943
944 other->goalentity = other->movetarget = next;
945
946 if (self->wait)
947 {
948 other->monsterinfo.pausetime = level.time + self->wait;
949 other->monsterinfo.stand (other);
950 return;
951 }
952
953 if (!other->movetarget)
954 {
955 other->monsterinfo.pausetime = level.time + 100000000;
956 other->monsterinfo.stand (other);
957 }
958 else
959 {
960 VectorSubtract (other->goalentity->s.origin, other->s.origin, v);
961 other->ideal_yaw = vectoyaw (v);
962 }
963 }
964
SP_path_corner(edict_t * self)965 void SP_path_corner (edict_t *self)
966 {
967 if (!self->targetname)
968 {
969 gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
970 G_FreeEdict (self);
971 return;
972 }
973
974 self->solid = SOLID_TRIGGER;
975 self->touch = path_corner_touch;
976 VectorSet (self->mins, -8, -8, -8);
977 VectorSet (self->maxs, 8, 8, 8);
978 self->svflags |= SVF_NOCLIENT;
979 gi.linkentity (self);
980 }
981
982
983 /*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
984 Makes this the target of a monster and it will head here
985 when first activated before going after the activator. If
986 hold is selected, it will stay here.
987 */
point_combat_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)988 void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
989 {
990 edict_t *activator;
991
992 if (other->movetarget != self)
993 return;
994
995 if (self->target)
996 {
997 other->target = self->target;
998 other->goalentity = other->movetarget = G_PickTarget(other->target);
999 if (!other->goalentity)
1000 {
1001 gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
1002 other->movetarget = self;
1003 }
1004 self->target = NULL;
1005 }
1006 else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY)))
1007 {
1008 other->monsterinfo.pausetime = level.time + 100000000;
1009 other->monsterinfo.aiflags |= AI_STAND_GROUND;
1010 other->monsterinfo.stand (other);
1011 }
1012
1013 if (other->movetarget == self)
1014 {
1015 other->target = NULL;
1016 other->movetarget = NULL;
1017 other->goalentity = other->enemy;
1018 other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
1019 }
1020
1021 if (self->pathtarget)
1022 {
1023 char *savetarget;
1024
1025 savetarget = self->target;
1026 self->target = self->pathtarget;
1027 if (other->enemy && other->enemy->client)
1028 activator = other->enemy;
1029 else if (other->oldenemy && other->oldenemy->client)
1030 activator = other->oldenemy;
1031 else if (other->activator && other->activator->client)
1032 activator = other->activator;
1033 else
1034 activator = other;
1035 G_UseTargets (self, activator);
1036 self->target = savetarget;
1037 }
1038 }
1039
SP_point_combat(edict_t * self)1040 void SP_point_combat (edict_t *self)
1041 {
1042 if (deathmatch->value)
1043 {
1044 G_FreeEdict (self);
1045 return;
1046 }
1047 self->solid = SOLID_TRIGGER;
1048 self->touch = point_combat_touch;
1049 VectorSet (self->mins, -8, -8, -16);
1050 VectorSet (self->maxs, 8, 8, 16);
1051 self->svflags = SVF_NOCLIENT;
1052 gi.linkentity (self);
1053 };
1054
1055
1056 /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
1057 Just for the debugging level. Don't use
1058 */
TH_viewthing(edict_t * ent)1059 void TH_viewthing(edict_t *ent)
1060 {
1061 ent->s.frame = (ent->s.frame + 1) % 7;
1062 ent->nextthink = level.time + FRAMETIME;
1063 }
1064
SP_viewthing(edict_t * ent)1065 void SP_viewthing(edict_t *ent)
1066 {
1067 gi.dprintf ("viewthing spawned\n");
1068
1069 ent->movetype = MOVETYPE_NONE;
1070 ent->solid = SOLID_BBOX;
1071 ent->s.renderfx = RF_FRAMELERP;
1072 VectorSet (ent->mins, -16, -16, -24);
1073 VectorSet (ent->maxs, 16, 16, 32);
1074 ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
1075 gi.linkentity (ent);
1076 ent->nextthink = level.time + 0.5;
1077 ent->think = TH_viewthing;
1078 return;
1079 }
1080
1081
1082 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
1083 Used as a positional target for spotlights, etc.
1084 */
SP_info_null(edict_t * self)1085 void SP_info_null (edict_t *self)
1086 {
1087 G_FreeEdict (self);
1088 };
1089
1090
1091 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
1092 Used as a positional target for lightning.
1093 */
SP_info_notnull(edict_t * self)1094 void SP_info_notnull (edict_t *self)
1095 {
1096 VectorCopy (self->s.origin, self->absmin);
1097 VectorCopy (self->s.origin, self->absmax);
1098 };
1099
1100
1101 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
1102 Non-displayed light.
1103 Default light value is 300.
1104 Default style is 0.
1105 If targeted, will toggle between on and off.
1106 Default _cone value is 10 (used to set size of light for spotlights)
1107 */
1108
1109 #define START_OFF 1
1110
light_use(edict_t * self,edict_t * other,edict_t * activator)1111 static void light_use (edict_t *self, edict_t *other, edict_t *activator)
1112 {
1113 if (self->spawnflags & START_OFF)
1114 {
1115 gi.configstring (CS_LIGHTS+self->style, "m");
1116 self->spawnflags &= ~START_OFF;
1117 }
1118 else
1119 {
1120 gi.configstring (CS_LIGHTS+self->style, "a");
1121 self->spawnflags |= START_OFF;
1122 }
1123 }
1124
SP_light(edict_t * self)1125 void SP_light (edict_t *self)
1126 {
1127 // no targeted lights in deathmatch, because they cause global messages
1128 if (!self->targetname || deathmatch->value)
1129 {
1130 G_FreeEdict (self);
1131 return;
1132 }
1133
1134 if (self->style >= 32)
1135 {
1136 self->use = light_use;
1137 if (self->spawnflags & START_OFF)
1138 gi.configstring (CS_LIGHTS+self->style, "a");
1139 else
1140 gi.configstring (CS_LIGHTS+self->style, "m");
1141 }
1142 }
1143
1144
1145 /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
1146 This is just a solid wall if not inhibited
1147
1148 TRIGGER_SPAWN the wall will not be present until triggered
1149 it will then blink in to existance; it will
1150 kill anything that was in it's way
1151
1152 TOGGLE only valid for TRIGGER_SPAWN walls
1153 this allows the wall to be turned on and off
1154
1155 START_ON only valid for TRIGGER_SPAWN walls
1156 the wall will initially be present
1157 */
1158
func_wall_use(edict_t * self,edict_t * other,edict_t * activator)1159 void func_wall_use (edict_t *self, edict_t *other, edict_t *activator)
1160 {
1161 if (self->solid == SOLID_NOT)
1162 {
1163 self->solid = SOLID_BSP;
1164 self->svflags &= ~SVF_NOCLIENT;
1165 KillBox (self);
1166 }
1167 else
1168 {
1169 self->solid = SOLID_NOT;
1170 self->svflags |= SVF_NOCLIENT;
1171 }
1172 gi.linkentity (self);
1173
1174 if (!(self->spawnflags & 2))
1175 self->use = NULL;
1176 }
1177
SP_func_wall(edict_t * self)1178 void SP_func_wall (edict_t *self)
1179 {
1180 self->movetype = MOVETYPE_PUSH;
1181 gi.setmodel (self, self->model);
1182
1183 if (self->spawnflags & 8)
1184 self->s.effects |= EF_ANIM_ALL;
1185 if (self->spawnflags & 16)
1186 self->s.effects |= EF_ANIM_ALLFAST;
1187
1188 // just a wall
1189 if ((self->spawnflags & 7) == 0)
1190 {
1191 self->solid = SOLID_BSP;
1192 gi.linkentity (self);
1193 return;
1194 }
1195
1196 // it must be TRIGGER_SPAWN
1197 if (!(self->spawnflags & 1))
1198 {
1199 // gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
1200 self->spawnflags |= 1;
1201 }
1202
1203 // yell if the spawnflags are odd
1204 if (self->spawnflags & 4)
1205 {
1206 if (!(self->spawnflags & 2))
1207 {
1208 gi.dprintf("func_wall START_ON without TOGGLE\n");
1209 self->spawnflags |= 2;
1210 }
1211 }
1212
1213 self->use = func_wall_use;
1214 if (self->spawnflags & 4)
1215 {
1216 self->solid = SOLID_BSP;
1217 }
1218 else
1219 {
1220 self->solid = SOLID_NOT;
1221 self->svflags |= SVF_NOCLIENT;
1222 }
1223 gi.linkentity (self);
1224 }
1225
1226
1227 /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
1228 This is solid bmodel that will fall if it's support it removed.
1229 */
1230
func_object_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1231 void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1232 {
1233 // only squash thing we fall on top of
1234 if (!plane)
1235 return;
1236 if (plane->normal[2] < 1.0)
1237 return;
1238 if (other->takedamage == DAMAGE_NO)
1239 return;
1240 T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1241 }
1242
func_object_release(edict_t * self)1243 void func_object_release (edict_t *self)
1244 {
1245 self->movetype = MOVETYPE_TOSS;
1246 self->touch = func_object_touch;
1247 }
1248
func_object_use(edict_t * self,edict_t * other,edict_t * activator)1249 void func_object_use (edict_t *self, edict_t *other, edict_t *activator)
1250 {
1251 self->solid = SOLID_BSP;
1252 self->svflags &= ~SVF_NOCLIENT;
1253 self->use = NULL;
1254 KillBox (self);
1255 func_object_release (self);
1256 }
1257
SP_func_object(edict_t * self)1258 void SP_func_object (edict_t *self)
1259 {
1260 gi.setmodel (self, self->model);
1261
1262 self->mins[0] += 1;
1263 self->mins[1] += 1;
1264 self->mins[2] += 1;
1265 self->maxs[0] -= 1;
1266 self->maxs[1] -= 1;
1267 self->maxs[2] -= 1;
1268
1269 if (!self->dmg)
1270 self->dmg = 100;
1271
1272 if (self->spawnflags == 0)
1273 {
1274 self->solid = SOLID_BSP;
1275 self->movetype = MOVETYPE_PUSH;
1276 self->think = func_object_release;
1277 self->nextthink = level.time + 2 * FRAMETIME;
1278 }
1279 else
1280 {
1281 self->solid = SOLID_NOT;
1282 self->movetype = MOVETYPE_PUSH;
1283 self->use = func_object_use;
1284 self->svflags |= SVF_NOCLIENT;
1285 }
1286
1287 if (self->spawnflags & 2)
1288 self->s.effects |= EF_ANIM_ALL;
1289 if (self->spawnflags & 4)
1290 self->s.effects |= EF_ANIM_ALLFAST;
1291
1292 self->clipmask = MASK_MONSTERSOLID;
1293
1294 gi.linkentity (self);
1295 }
1296
1297
1298 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
1299 Any brush that you want to explode or break apart. If you want an
1300 ex0plosion, set dmg and it will do a radius explosion of that amount
1301 at the center of the bursh.
1302
1303 If targeted it will not be shootable.
1304
1305 health defaults to 100.
1306
1307 mass defaults to 75. This determines how much debris is emitted when
1308 it explodes. You get one large chunk per 100 of mass (up to 8) and
1309 one small chunk per 25 of mass (up to 16). So 800 gives the most.
1310 */
func_explosive_explode(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1311 void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1312 {
1313 vec3_t origin;
1314 vec3_t chunkorigin;
1315 vec3_t size;
1316 int count;
1317 int mass;
1318
1319 // bmodel origins are (0 0 0), we need to adjust that here
1320 VectorScale (self->size, 0.5, size);
1321 VectorAdd (self->absmin, size, origin);
1322 VectorCopy (origin, self->s.origin);
1323
1324 self->takedamage = DAMAGE_NO;
1325
1326 if (self->dmg)
1327 T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
1328
1329 VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity);
1330 VectorNormalize (self->velocity);
1331 VectorScale (self->velocity, 150, self->velocity);
1332
1333 // start chunks towards the center
1334 VectorScale (size, 0.5, size);
1335
1336 mass = self->mass;
1337 if (!mass)
1338 mass = 75;
1339
1340 // big chunks
1341 if (mass >= 100)
1342 {
1343 count = mass / 100;
1344 if (count > 8)
1345 count = 8;
1346 while(count--)
1347 {
1348 chunkorigin[0] = origin[0] + crandom() * size[0];
1349 chunkorigin[1] = origin[1] + crandom() * size[1];
1350 chunkorigin[2] = origin[2] + crandom() * size[2];
1351 ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin);
1352 }
1353 }
1354
1355 // small chunks
1356 count = mass / 25;
1357 if (count > 16)
1358 count = 16;
1359 while(count--)
1360 {
1361 chunkorigin[0] = origin[0] + crandom() * size[0];
1362 chunkorigin[1] = origin[1] + crandom() * size[1];
1363 chunkorigin[2] = origin[2] + crandom() * size[2];
1364 ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin);
1365 }
1366
1367 G_UseTargets (self, attacker);
1368
1369 if (self->dmg)
1370 BecomeExplosion1 (self);
1371 else
1372 G_FreeEdict (self);
1373 }
1374
func_explosive_use(edict_t * self,edict_t * other,edict_t * activator)1375 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
1376 {
1377 func_explosive_explode (self, self, other, self->health, vec3_origin);
1378 }
1379
func_explosive_spawn(edict_t * self,edict_t * other,edict_t * activator)1380 void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator)
1381 {
1382 self->solid = SOLID_BSP;
1383 self->svflags &= ~SVF_NOCLIENT;
1384 self->use = NULL;
1385 KillBox (self);
1386 gi.linkentity (self);
1387 }
1388
SP_func_explosive(edict_t * self)1389 void SP_func_explosive (edict_t *self)
1390 {
1391 if (deathmatch->value)
1392 { // auto-remove for deathmatch
1393 G_FreeEdict (self);
1394 return;
1395 }
1396
1397 self->movetype = MOVETYPE_PUSH;
1398
1399 gi.modelindex ("models/objects/debris1/tris.md2");
1400 gi.modelindex ("models/objects/debris2/tris.md2");
1401
1402 gi.setmodel (self, self->model);
1403
1404 if (self->spawnflags & 1)
1405 {
1406 self->svflags |= SVF_NOCLIENT;
1407 self->solid = SOLID_NOT;
1408 self->use = func_explosive_spawn;
1409 }
1410 else
1411 {
1412 self->solid = SOLID_BSP;
1413 if (self->targetname)
1414 self->use = func_explosive_use;
1415 }
1416
1417 if (self->spawnflags & 2)
1418 self->s.effects |= EF_ANIM_ALL;
1419 if (self->spawnflags & 4)
1420 self->s.effects |= EF_ANIM_ALLFAST;
1421
1422 if (self->use != func_explosive_use)
1423 {
1424 if (!self->health)
1425 self->health = 100;
1426 self->die = func_explosive_explode;
1427 self->takedamage = DAMAGE_YES;
1428 }
1429
1430 gi.linkentity (self);
1431 }
1432
1433
1434 /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
1435 Large exploding box. You can override its mass (100),
1436 health (80), and dmg (150).
1437 */
1438
barrel_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1439 void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1440
1441 {
1442 float ratio;
1443 vec3_t v;
1444
1445 if ((!other->groundentity) || (other->groundentity == self))
1446 return;
1447
1448 ratio = (float)other->mass / (float)self->mass;
1449 VectorSubtract (self->s.origin, other->s.origin, v);
1450 M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME);
1451 }
1452
barrel_explode(edict_t * self)1453 void barrel_explode (edict_t *self)
1454 {
1455 vec3_t org;
1456 float spd;
1457 vec3_t save;
1458
1459 T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL);
1460
1461 VectorCopy (self->s.origin, save);
1462 VectorMA (self->absmin, 0.5, self->size, self->s.origin);
1463
1464 // a few big chunks
1465 spd = 1.5 * (float)self->dmg / 200.0;
1466 org[0] = self->s.origin[0] + crandom() * self->size[0];
1467 org[1] = self->s.origin[1] + crandom() * self->size[1];
1468 org[2] = self->s.origin[2] + crandom() * self->size[2];
1469 ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
1470 org[0] = self->s.origin[0] + crandom() * self->size[0];
1471 org[1] = self->s.origin[1] + crandom() * self->size[1];
1472 org[2] = self->s.origin[2] + crandom() * self->size[2];
1473 ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org);
1474
1475 // bottom corners
1476 spd = 1.75 * (float)self->dmg / 200.0;
1477 VectorCopy (self->absmin, org);
1478 ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1479 VectorCopy (self->absmin, org);
1480 org[0] += self->size[0];
1481 ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1482 VectorCopy (self->absmin, org);
1483 org[1] += self->size[1];
1484 ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1485 VectorCopy (self->absmin, org);
1486 org[0] += self->size[0];
1487 org[1] += self->size[1];
1488 ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org);
1489
1490 // a bunch of little chunks
1491 spd = 2 * self->dmg / 200;
1492 org[0] = self->s.origin[0] + crandom() * self->size[0];
1493 org[1] = self->s.origin[1] + crandom() * self->size[1];
1494 org[2] = self->s.origin[2] + crandom() * self->size[2];
1495 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1496 org[0] = self->s.origin[0] + crandom() * self->size[0];
1497 org[1] = self->s.origin[1] + crandom() * self->size[1];
1498 org[2] = self->s.origin[2] + crandom() * self->size[2];
1499 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1500 org[0] = self->s.origin[0] + crandom() * self->size[0];
1501 org[1] = self->s.origin[1] + crandom() * self->size[1];
1502 org[2] = self->s.origin[2] + crandom() * self->size[2];
1503 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1504 org[0] = self->s.origin[0] + crandom() * self->size[0];
1505 org[1] = self->s.origin[1] + crandom() * self->size[1];
1506 org[2] = self->s.origin[2] + crandom() * self->size[2];
1507 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1508 org[0] = self->s.origin[0] + crandom() * self->size[0];
1509 org[1] = self->s.origin[1] + crandom() * self->size[1];
1510 org[2] = self->s.origin[2] + crandom() * self->size[2];
1511 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1512 org[0] = self->s.origin[0] + crandom() * self->size[0];
1513 org[1] = self->s.origin[1] + crandom() * self->size[1];
1514 org[2] = self->s.origin[2] + crandom() * self->size[2];
1515 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1516 org[0] = self->s.origin[0] + crandom() * self->size[0];
1517 org[1] = self->s.origin[1] + crandom() * self->size[1];
1518 org[2] = self->s.origin[2] + crandom() * self->size[2];
1519 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1520 org[0] = self->s.origin[0] + crandom() * self->size[0];
1521 org[1] = self->s.origin[1] + crandom() * self->size[1];
1522 org[2] = self->s.origin[2] + crandom() * self->size[2];
1523 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
1524
1525 VectorCopy (save, self->s.origin);
1526 if (self->groundentity)
1527 BecomeExplosion2 (self);
1528 else
1529 BecomeExplosion1 (self);
1530 }
1531
barrel_delay(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1532 void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1533 {
1534 self->takedamage = DAMAGE_NO;
1535 self->nextthink = level.time + 2 * FRAMETIME;
1536 self->think = barrel_explode;
1537 self->activator = attacker;
1538 }
1539
SP_misc_explobox(edict_t * self)1540 void SP_misc_explobox (edict_t *self)
1541 {
1542 if (deathmatch->value)
1543 { // auto-remove for deathmatch
1544 G_FreeEdict (self);
1545 return;
1546 }
1547
1548 gi.modelindex ("models/objects/debris1/tris.md2");
1549 gi.modelindex ("models/objects/debris2/tris.md2");
1550 gi.modelindex ("models/objects/debris3/tris.md2");
1551
1552 self->solid = SOLID_BBOX;
1553 self->movetype = MOVETYPE_STEP;
1554
1555 self->model = "models/objects/barrels/tris.md2";
1556 self->s.modelindex = gi.modelindex (self->model);
1557 VectorSet (self->mins, -16, -16, 0);
1558 VectorSet (self->maxs, 16, 16, 40);
1559
1560 if (!self->mass)
1561 self->mass = 400;
1562 if (!self->health)
1563 self->health = 10;
1564 if (!self->dmg)
1565 self->dmg = 150;
1566
1567 self->die = barrel_delay;
1568 self->takedamage = DAMAGE_YES;
1569 self->monsterinfo.aiflags = AI_NOSTEP;
1570
1571 self->touch = barrel_touch;
1572
1573 self->think = M_droptofloor;
1574 self->nextthink = level.time + 2 * FRAMETIME;
1575
1576 gi.linkentity (self);
1577 }
1578
1579
1580 //
1581 // miscellaneous specialty items
1582 //
1583
1584 /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8)
1585 */
1586
misc_blackhole_use(edict_t * ent,edict_t * other,edict_t * activator)1587 void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator)
1588 {
1589 /*
1590 gi.WriteByte (svc_temp_entity);
1591 gi.WriteByte (TE_BOSSTPORT);
1592 gi.WritePosition (ent->s.origin);
1593 gi.multicast (ent->s.origin, MULTICAST_PVS);
1594 */
1595 G_FreeEdict (ent);
1596 }
1597
misc_blackhole_think(edict_t * self)1598 void misc_blackhole_think (edict_t *self)
1599 {
1600 if (++self->s.frame < 19)
1601 self->nextthink = level.time + FRAMETIME;
1602 else
1603 {
1604 self->s.frame = 0;
1605 self->nextthink = level.time + FRAMETIME;
1606 }
1607 }
1608
SP_misc_blackhole(edict_t * ent)1609 void SP_misc_blackhole (edict_t *ent)
1610 {
1611 ent->movetype = MOVETYPE_NONE;
1612 ent->solid = SOLID_NOT;
1613 VectorSet (ent->mins, -64, -64, 0);
1614 VectorSet (ent->maxs, 64, 64, 8);
1615 ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2");
1616 ent->s.renderfx = RF_TRANSLUCENT;
1617 ent->use = misc_blackhole_use;
1618 ent->think = misc_blackhole_think;
1619 ent->nextthink = level.time + 2 * FRAMETIME;
1620 gi.linkentity (ent);
1621 }
1622
1623 /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32)
1624 */
1625
misc_eastertank_think(edict_t * self)1626 void misc_eastertank_think (edict_t *self)
1627 {
1628 if (++self->s.frame < 293)
1629 self->nextthink = level.time + FRAMETIME;
1630 else
1631 {
1632 self->s.frame = 254;
1633 self->nextthink = level.time + FRAMETIME;
1634 }
1635 }
1636
SP_misc_eastertank(edict_t * ent)1637 void SP_misc_eastertank (edict_t *ent)
1638 {
1639 ent->movetype = MOVETYPE_NONE;
1640 ent->solid = SOLID_BBOX;
1641 VectorSet (ent->mins, -32, -32, -16);
1642 VectorSet (ent->maxs, 32, 32, 32);
1643 ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2");
1644 ent->s.frame = 254;
1645 ent->think = misc_eastertank_think;
1646 ent->nextthink = level.time + 2 * FRAMETIME;
1647 gi.linkentity (ent);
1648 }
1649
1650 /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
1651 */
1652
1653
misc_easterchick_think(edict_t * self)1654 void misc_easterchick_think (edict_t *self)
1655 {
1656 if (++self->s.frame < 247)
1657 self->nextthink = level.time + FRAMETIME;
1658 else
1659 {
1660 self->s.frame = 208;
1661 self->nextthink = level.time + FRAMETIME;
1662 }
1663 }
1664
SP_misc_easterchick(edict_t * ent)1665 void SP_misc_easterchick (edict_t *ent)
1666 {
1667 ent->movetype = MOVETYPE_NONE;
1668 ent->solid = SOLID_BBOX;
1669 VectorSet (ent->mins, -32, -32, 0);
1670 VectorSet (ent->maxs, 32, 32, 32);
1671 ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1672 ent->s.frame = 208;
1673 ent->think = misc_easterchick_think;
1674 ent->nextthink = level.time + 2 * FRAMETIME;
1675 gi.linkentity (ent);
1676 }
1677
1678 /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
1679 */
1680
1681
misc_easterchick2_think(edict_t * self)1682 void misc_easterchick2_think (edict_t *self)
1683 {
1684 if (++self->s.frame < 287)
1685 self->nextthink = level.time + FRAMETIME;
1686 else
1687 {
1688 self->s.frame = 248;
1689 self->nextthink = level.time + FRAMETIME;
1690 }
1691 }
1692
SP_misc_easterchick2(edict_t * ent)1693 void SP_misc_easterchick2 (edict_t *ent)
1694 {
1695 ent->movetype = MOVETYPE_NONE;
1696 ent->solid = SOLID_BBOX;
1697 VectorSet (ent->mins, -32, -32, 0);
1698 VectorSet (ent->maxs, 32, 32, 32);
1699 ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2");
1700 ent->s.frame = 248;
1701 ent->think = misc_easterchick2_think;
1702 ent->nextthink = level.time + 2 * FRAMETIME;
1703 gi.linkentity (ent);
1704 }
1705
1706
1707 /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
1708 Not really a monster, this is the Tank Commander's decapitated body.
1709 There should be a item_commander_head that has this as it's target.
1710 */
1711
commander_body_think(edict_t * self)1712 void commander_body_think (edict_t *self)
1713 {
1714 if (++self->s.frame < 24)
1715 self->nextthink = level.time + FRAMETIME;
1716 else
1717 self->nextthink = 0;
1718
1719 if (self->s.frame == 22)
1720 gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0);
1721 }
1722
commander_body_use(edict_t * self,edict_t * other,edict_t * activator)1723 void commander_body_use (edict_t *self, edict_t *other, edict_t *activator)
1724 {
1725 self->think = commander_body_think;
1726 self->nextthink = level.time + FRAMETIME;
1727 gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0);
1728 }
1729
commander_body_drop(edict_t * self)1730 void commander_body_drop (edict_t *self)
1731 {
1732 self->movetype = MOVETYPE_TOSS;
1733 self->s.origin[2] += 2;
1734 }
1735
SP_monster_commander_body(edict_t * self)1736 void SP_monster_commander_body (edict_t *self)
1737 {
1738 self->movetype = MOVETYPE_NONE;
1739 self->solid = SOLID_BBOX;
1740 self->model = "models/monsters/commandr/tris.md2";
1741 self->s.modelindex = gi.modelindex (self->model);
1742 VectorSet (self->mins, -32, -32, 0);
1743 VectorSet (self->maxs, 32, 32, 48);
1744 self->use = commander_body_use;
1745 self->takedamage = DAMAGE_YES;
1746 self->flags = FL_GODMODE;
1747 self->s.renderfx |= RF_FRAMELERP;
1748 gi.linkentity (self);
1749
1750 gi.soundindex ("tank/thud.wav");
1751 gi.soundindex ("tank/pain.wav");
1752
1753 self->think = commander_body_drop;
1754 self->nextthink = level.time + 5 * FRAMETIME;
1755 }
1756
1757
1758 /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
1759 The origin is the bottom of the banner.
1760 The banner is 128 tall.
1761 */
misc_banner_think(edict_t * ent)1762 void misc_banner_think (edict_t *ent)
1763 {
1764 ent->s.frame = (ent->s.frame + 1) % 16;
1765 ent->nextthink = level.time + FRAMETIME;
1766 }
1767
SP_misc_banner(edict_t * ent)1768 void SP_misc_banner (edict_t *ent)
1769 {
1770 ent->movetype = MOVETYPE_NONE;
1771 ent->solid = SOLID_NOT;
1772 ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2");
1773 ent->s.frame = rand() % 16;
1774 gi.linkentity (ent);
1775
1776 ent->think = misc_banner_think;
1777 ent->nextthink = level.time + FRAMETIME;
1778 }
1779
1780 /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
1781 This is the dead player model. Comes in 6 exciting different poses!
1782 */
misc_deadsoldier_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1783 void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1784 {
1785 int n;
1786
1787 if (self->health > -300)
1788 return;
1789
1790 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
1791 for (n= 0; n < 4; n++)
1792 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1793 ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
1794 }
1795
SP_misc_deadsoldier(edict_t * ent)1796 void SP_misc_deadsoldier (edict_t *ent)
1797 {
1798 if (deathmatch->value)
1799 { // auto-remove for deathmatch
1800 G_FreeEdict (ent);
1801 return;
1802 }
1803
1804 ent->movetype = MOVETYPE_NONE;
1805 ent->solid = SOLID_BBOX;
1806 ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2");
1807
1808 // Defaults to frame 0
1809 if (ent->spawnflags & 2)
1810 ent->s.frame = 1;
1811 else if (ent->spawnflags & 4)
1812 ent->s.frame = 2;
1813 else if (ent->spawnflags & 8)
1814 ent->s.frame = 3;
1815 else if (ent->spawnflags & 16)
1816 ent->s.frame = 4;
1817 else if (ent->spawnflags & 32)
1818 ent->s.frame = 5;
1819 else
1820 ent->s.frame = 0;
1821
1822 VectorSet (ent->mins, -16, -16, 0);
1823 VectorSet (ent->maxs, 16, 16, 16);
1824 ent->deadflag = DEAD_DEAD;
1825 ent->takedamage = DAMAGE_YES;
1826 ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER;
1827 ent->die = misc_deadsoldier_die;
1828 ent->monsterinfo.aiflags |= AI_GOOD_GUY;
1829
1830 ent->think=FadeDieSink;
1831 ent->nextthink=level.time+FADETIME+random()*FADETIME*2;
1832 ent->floater=1;
1833
1834 gi.linkentity (ent);
1835 }
1836
1837 /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
1838 This is the Viper for the flyby bombing.
1839 It is trigger_spawned, so you must have something use it for it to show up.
1840 There must be a path for it to follow once it is activated.
1841
1842 "speed" How fast the Viper should fly
1843 */
1844
1845 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1846 extern void func_train_find (edict_t *self);
1847
misc_viper_use(edict_t * self,edict_t * other,edict_t * activator)1848 void misc_viper_use (edict_t *self, edict_t *other, edict_t *activator)
1849 {
1850 self->svflags &= ~SVF_NOCLIENT;
1851 self->use = train_use;
1852 train_use (self, other, activator);
1853 }
1854
SP_misc_viper(edict_t * ent)1855 void SP_misc_viper (edict_t *ent)
1856 {
1857 if (!ent->target)
1858 {
1859 gi.dprintf ("misc_viper without a target at %s\n", vtos(ent->absmin));
1860 G_FreeEdict (ent);
1861 return;
1862 }
1863
1864 if (!ent->speed)
1865 ent->speed = 300;
1866
1867 ent->movetype = MOVETYPE_PUSH;
1868 ent->solid = SOLID_NOT;
1869 ent->s.modelindex = gi.modelindex ("models/ships/viper/tris.md2");
1870 VectorSet (ent->mins, -16, -16, 0);
1871 VectorSet (ent->maxs, 16, 16, 32);
1872
1873 ent->think = func_train_find;
1874 ent->nextthink = level.time + FRAMETIME;
1875 ent->use = misc_viper_use;
1876 ent->svflags |= SVF_NOCLIENT;
1877 ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1878
1879 gi.linkentity (ent);
1880 }
1881
1882
1883 /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72)
1884 This is a large stationary viper as seen in Paul's intro
1885 */
SP_misc_bigviper(edict_t * ent)1886 void SP_misc_bigviper (edict_t *ent)
1887 {
1888 ent->movetype = MOVETYPE_NONE;
1889 ent->solid = SOLID_BBOX;
1890 VectorSet (ent->mins, -176, -120, -24);
1891 VectorSet (ent->maxs, 176, 120, 72);
1892 ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2");
1893 gi.linkentity (ent);
1894 }
1895
1896
1897 /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
1898 "dmg" how much boom should the bomb make?
1899 */
misc_viper_bomb_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1900 void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1901 {
1902 G_UseTargets (self, self->activator);
1903
1904 self->s.origin[2] = self->absmin[2] + 1;
1905 T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB);
1906 BecomeExplosion2 (self);
1907 }
1908
misc_viper_bomb_prethink(edict_t * self)1909 void misc_viper_bomb_prethink (edict_t *self)
1910 {
1911 vec3_t v;
1912 float diff;
1913
1914 self->groundentity = NULL;
1915
1916 diff = self->timestamp - level.time;
1917 if (diff < -1.0)
1918 diff = -1.0;
1919
1920 VectorScale (self->moveinfo.dir, 1.0 + diff, v);
1921 v[2] = diff;
1922
1923 diff = self->s.angles[2];
1924 vectoangles (v, self->s.angles);
1925 self->s.angles[2] = diff + 10;
1926 }
1927
misc_viper_bomb_use(edict_t * self,edict_t * other,edict_t * activator)1928 void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator)
1929 {
1930 edict_t *viper;
1931
1932 self->solid = SOLID_BBOX;
1933 self->svflags &= ~SVF_NOCLIENT;
1934 self->s.effects |= EF_ROCKET;
1935 self->use = NULL;
1936 self->movetype = MOVETYPE_TOSS;
1937 self->prethink = misc_viper_bomb_prethink;
1938 self->touch = misc_viper_bomb_touch;
1939 self->activator = activator;
1940
1941 viper = G_Find (NULL, FOFS(classname), "misc_viper");
1942 VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
1943
1944 self->timestamp = level.time;
1945 VectorCopy (viper->moveinfo.dir, self->moveinfo.dir);
1946 }
1947
SP_misc_viper_bomb(edict_t * self)1948 void SP_misc_viper_bomb (edict_t *self)
1949 {
1950 self->movetype = MOVETYPE_NONE;
1951 self->solid = SOLID_NOT;
1952 VectorSet (self->mins, -8, -8, -8);
1953 VectorSet (self->maxs, 8, 8, 8);
1954
1955 self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2");
1956
1957 if (!self->dmg)
1958 self->dmg = 1000;
1959
1960 self->use = misc_viper_bomb_use;
1961 self->svflags |= SVF_NOCLIENT;
1962
1963 gi.linkentity (self);
1964 }
1965
1966
1967 /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
1968 This is a Storgg ship for the flybys.
1969 It is trigger_spawned, so you must have something use it for it to show up.
1970 There must be a path for it to follow once it is activated.
1971
1972 "speed" How fast it should fly
1973 */
1974
1975 extern void train_use (edict_t *self, edict_t *other, edict_t *activator);
1976 extern void func_train_find (edict_t *self);
1977
misc_strogg_ship_use(edict_t * self,edict_t * other,edict_t * activator)1978 void misc_strogg_ship_use (edict_t *self, edict_t *other, edict_t *activator)
1979 {
1980 self->svflags &= ~SVF_NOCLIENT;
1981 self->use = train_use;
1982 train_use (self, other, activator);
1983 }
1984
SP_misc_strogg_ship(edict_t * ent)1985 void SP_misc_strogg_ship (edict_t *ent)
1986 {
1987 if (!ent->target)
1988 {
1989 gi.dprintf ("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
1990 G_FreeEdict (ent);
1991 return;
1992 }
1993
1994 if (!ent->speed)
1995 ent->speed = 300;
1996
1997 ent->movetype = MOVETYPE_PUSH;
1998 ent->solid = SOLID_NOT;
1999 ent->s.modelindex = gi.modelindex ("models/ships/strogg1/tris.md2");
2000 VectorSet (ent->mins, -16, -16, 0);
2001 VectorSet (ent->maxs, 16, 16, 32);
2002
2003 ent->think = func_train_find;
2004 ent->nextthink = level.time + FRAMETIME;
2005 ent->use = misc_strogg_ship_use;
2006 ent->svflags |= SVF_NOCLIENT;
2007 ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
2008
2009 gi.linkentity (ent);
2010 }
2011
2012
2013 /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
2014 */
misc_satellite_dish_think(edict_t * self)2015 void misc_satellite_dish_think (edict_t *self)
2016 {
2017 self->s.frame++;
2018 if (self->s.frame < 38)
2019 self->nextthink = level.time + FRAMETIME;
2020 }
2021
misc_satellite_dish_use(edict_t * self,edict_t * other,edict_t * activator)2022 void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator)
2023 {
2024 self->s.frame = 0;
2025 self->think = misc_satellite_dish_think;
2026 self->nextthink = level.time + FRAMETIME;
2027 }
2028
SP_misc_satellite_dish(edict_t * ent)2029 void SP_misc_satellite_dish (edict_t *ent)
2030 {
2031 ent->movetype = MOVETYPE_NONE;
2032 ent->solid = SOLID_BBOX;
2033 VectorSet (ent->mins, -64, -64, 0);
2034 VectorSet (ent->maxs, 64, 64, 128);
2035 ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2");
2036 ent->use = misc_satellite_dish_use;
2037 gi.linkentity (ent);
2038 }
2039
2040
2041 /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
2042 */
2043
Mine_Light_Break(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)2044 static void Mine_Light_Break (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
2045 {
2046 int spd, i;
2047 vec3_t org;
2048
2049 if (self->s.modelindex2)
2050 {
2051 self->s.modelindex = self->s.modelindex2;
2052 self->s.frame = 0;
2053 }
2054 self->s.effects = 0;
2055 self->takedamage = DAMAGE_NO;
2056 self->die = NULL;
2057
2058 gi.sound(self, CHAN_AUTO, gi.soundindex("world/spark1.wav"), 1, ATTN_IDLE, 0);
2059 gi.sound(self, CHAN_VOICE, gi.soundindex("world/brkglas.wav"), 0.7, ATTN_IDLE, 0);
2060
2061 spd = 5+(crandom()*10);
2062 org[0] = self->s.origin[0] + crandom() * 10;
2063 org[1] = self->s.origin[1] + crandom() * 10;
2064 org[2] = self->s.origin[2] + crandom() * 10;
2065 ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org);
2066
2067 self->think = FadeDie;
2068 self->nextthink = level.time + 1;
2069 }
2070
SP_light_mine1(edict_t * ent)2071 void SP_light_mine1 (edict_t *ent)
2072 {
2073 ent->movetype = MOVETYPE_NONE;
2074 ent->solid = SOLID_BBOX;
2075 ent->s.effects = EF_PLASMA;
2076 ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2077
2078 if (!sv_serversideonly->value)
2079 {
2080 ent->s.modelindex2 = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2081 ent->s.modelindex = gi.modelindex ("sprites/s_lensflare.sp2");
2082
2083 if (!deathmatch->value && !coop->value)
2084 {
2085 ent->think = FlareThink;
2086 ent->nextthink = level.time+FRAMETIME;
2087 }
2088 }
2089
2090 VectorSet(ent->mins, -8, -8, -20);
2091 VectorSet(ent->maxs, 8, 8, 20);
2092 ent->health = 25;
2093 ent->die = Mine_Light_Break;
2094 ent->takedamage = DAMAGE_YES;
2095 ent->monsterinfo.aiflags = AI_NOSTEP;
2096
2097 gi.linkentity (ent);
2098 }
2099
2100
2101 /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
2102 */
SP_light_mine2(edict_t * ent)2103 void SP_light_mine2 (edict_t *ent)
2104 {
2105 ent->movetype = MOVETYPE_NONE;
2106 ent->solid = SOLID_BBOX;
2107 ent->s.effects = EF_PLASMA;
2108 ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2");
2109
2110 if (!sv_serversideonly->value)
2111 {
2112 ent->s.modelindex2 = gi.modelindex ("models/objects/minelite/light1/tris.md2");
2113 ent->s.modelindex = gi.modelindex ("sprites/s_lensflare.sp2");
2114
2115 if (!deathmatch->value && !coop->value)
2116 {
2117 ent->think = FlareThink;
2118 ent->nextthink = level.time+FRAMETIME;
2119 }
2120 }
2121
2122 VectorSet(ent->mins, -8, -8, -20);
2123 VectorSet(ent->maxs, 8, 8, 20);
2124 ent->health = 25;
2125 ent->die = Mine_Light_Break;
2126 ent->takedamage = DAMAGE_YES;
2127
2128 gi.linkentity (ent);
2129 }
2130
2131
2132 /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
2133 Intended for use with the target_spawner
2134 */
SP_misc_gib_arm(edict_t * ent)2135 void SP_misc_gib_arm (edict_t *ent)
2136 {
2137 gi.setmodel (ent, "models/objects/gibs/arm/tris.md2");
2138 ent->solid = SOLID_NOT;
2139 ent->s.effects |= EF_GIB;
2140 ent->takedamage = DAMAGE_YES;
2141 ent->die = gib_die;
2142 ent->movetype = MOVETYPE_TOSS;
2143 ent->svflags |= SVF_MONSTER;
2144 ent->deadflag = DEAD_DEAD;
2145 ent->avelocity[0] = random()*200;
2146 ent->avelocity[1] = random()*200;
2147 ent->avelocity[2] = random()*200;
2148 ent->think = G_FreeEdict;
2149 ent->nextthink = level.time + 30;
2150 gi.linkentity (ent);
2151 }
2152
2153 /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
2154 Intended for use with the target_spawner
2155 */
SP_misc_gib_leg(edict_t * ent)2156 void SP_misc_gib_leg (edict_t *ent)
2157 {
2158 gi.setmodel (ent, "models/objects/gibs/leg/tris.md2");
2159 ent->solid = SOLID_NOT;
2160 ent->s.effects |= EF_GIB;
2161 ent->takedamage = DAMAGE_YES;
2162 ent->die = gib_die;
2163 ent->movetype = MOVETYPE_TOSS;
2164 ent->svflags |= SVF_MONSTER;
2165 ent->deadflag = DEAD_DEAD;
2166 ent->avelocity[0] = random()*200;
2167 ent->avelocity[1] = random()*200;
2168 ent->avelocity[2] = random()*200;
2169 ent->think = G_FreeEdict;
2170 ent->nextthink = level.time + 30;
2171 gi.linkentity (ent);
2172 }
2173
2174 /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
2175 Intended for use with the target_spawner
2176 */
SP_misc_gib_head(edict_t * ent)2177 void SP_misc_gib_head (edict_t *ent)
2178 {
2179 gi.setmodel (ent, "models/objects/gibs/head/tris.md2");
2180 ent->solid = SOLID_NOT;
2181 ent->s.effects |= EF_GIB;
2182 ent->takedamage = DAMAGE_YES;
2183 ent->die = gib_die;
2184 ent->movetype = MOVETYPE_TOSS;
2185 ent->svflags |= SVF_MONSTER;
2186 ent->deadflag = DEAD_DEAD;
2187 ent->avelocity[0] = random()*200;
2188 ent->avelocity[1] = random()*200;
2189 ent->avelocity[2] = random()*200;
2190 ent->think = G_FreeEdict;
2191 ent->nextthink = level.time + 30;
2192 gi.linkentity (ent);
2193 }
2194
2195 //=====================================================
2196
2197 /*QUAKED target_character (0 0 1) ?
2198 used with target_string (must be on same "team")
2199 "count" is position in the string (starts at 1)
2200 */
2201
SP_target_character(edict_t * self)2202 void SP_target_character (edict_t *self)
2203 {
2204 self->movetype = MOVETYPE_PUSH;
2205 gi.setmodel (self, self->model);
2206 self->solid = SOLID_BSP;
2207 self->s.frame = 12;
2208 gi.linkentity (self);
2209 return;
2210 }
2211
2212
2213 /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
2214 */
2215
target_string_use(edict_t * self,edict_t * other,edict_t * activator)2216 void target_string_use (edict_t *self, edict_t *other, edict_t *activator)
2217 {
2218 edict_t *e;
2219 int n, l;
2220 char c;
2221
2222 l = strlen(self->message);
2223 for (e = self->teammaster; e; e = e->teamchain)
2224 {
2225 if (!e->count)
2226 continue;
2227 n = e->count - 1;
2228 if (n > l)
2229 {
2230 e->s.frame = 12;
2231 continue;
2232 }
2233
2234 c = self->message[n];
2235 if (c >= '0' && c <= '9')
2236 e->s.frame = c - '0';
2237 else if (c == '-')
2238 e->s.frame = 10;
2239 else if (c == ':')
2240 e->s.frame = 11;
2241 else
2242 e->s.frame = 12;
2243 }
2244 }
2245
SP_target_string(edict_t * self)2246 void SP_target_string (edict_t *self)
2247 {
2248 if (!self->message)
2249 self->message = "";
2250 self->use = target_string_use;
2251 }
2252
2253
2254 /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
2255 target a target_string with this
2256
2257 The default is to be a time of day clock
2258
2259 TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
2260 If START_OFF, this entity must be used before it starts
2261
2262 "style" 0 "xx"
2263 1 "xx:xx"
2264 2 "xx:xx:xx"
2265 */
2266
2267 #define CLOCK_MESSAGE_SIZE 16
2268
2269 // don't let field width of any clock messages change, or it
2270 // could cause an overwrite after a game load
2271
func_clock_reset(edict_t * self)2272 static void func_clock_reset (edict_t *self)
2273 {
2274 self->activator = NULL;
2275 if (self->spawnflags & 1)
2276 {
2277 self->health = 0;
2278 self->wait = self->count;
2279 }
2280 else if (self->spawnflags & 2)
2281 {
2282 self->health = self->count;
2283 self->wait = 0;
2284 }
2285 }
2286
func_clock_format_countdown(edict_t * self)2287 static void func_clock_format_countdown (edict_t *self)
2288 {
2289 if (self->style == 0)
2290 {
2291 Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
2292 return;
2293 }
2294
2295 if (self->style == 1)
2296 {
2297 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
2298 if (self->message[3] == ' ')
2299 self->message[3] = '0';
2300 return;
2301 }
2302
2303 if (self->style == 2)
2304 {
2305 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60);
2306 if (self->message[3] == ' ')
2307 self->message[3] = '0';
2308 if (self->message[6] == ' ')
2309 self->message[6] = '0';
2310 return;
2311 }
2312 }
2313
func_clock_think(edict_t * self)2314 void func_clock_think (edict_t *self)
2315 {
2316 if (!self->enemy)
2317 {
2318 self->enemy = G_Find (NULL, FOFS(targetname), self->target);
2319 if (!self->enemy)
2320 return;
2321 }
2322
2323 if (self->spawnflags & 1)
2324 {
2325 func_clock_format_countdown (self);
2326 self->health++;
2327 }
2328 else if (self->spawnflags & 2)
2329 {
2330 func_clock_format_countdown (self);
2331 self->health--;
2332 }
2333 else
2334 {
2335 struct tm *ltime;
2336 time_t gmtime;
2337
2338 time(&gmtime);
2339 ltime = localtime(&gmtime);
2340 Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
2341 if (self->message[3] == ' ')
2342 self->message[3] = '0';
2343 if (self->message[6] == ' ')
2344 self->message[6] = '0';
2345 }
2346
2347 self->enemy->message = self->message;
2348 self->enemy->use (self->enemy, self, self);
2349
2350 if (((self->spawnflags & 1) && (self->health > self->wait)) ||
2351 ((self->spawnflags & 2) && (self->health < self->wait)))
2352 {
2353 if (self->pathtarget)
2354 {
2355 char *savetarget;
2356 char *savemessage;
2357
2358 savetarget = self->target;
2359 savemessage = self->message;
2360 self->target = self->pathtarget;
2361 self->message = NULL;
2362 G_UseTargets (self, self->activator);
2363 self->target = savetarget;
2364 self->message = savemessage;
2365 }
2366
2367 if (!(self->spawnflags & 8))
2368 return;
2369
2370 func_clock_reset (self);
2371
2372 if (self->spawnflags & 4)
2373 return;
2374 }
2375
2376 self->nextthink = level.time + 1;
2377 }
2378
func_clock_use(edict_t * self,edict_t * other,edict_t * activator)2379 void func_clock_use (edict_t *self, edict_t *other, edict_t *activator)
2380 {
2381 if (!(self->spawnflags & 8))
2382 self->use = NULL;
2383 if (self->activator)
2384 return;
2385 self->activator = activator;
2386 self->think (self);
2387 }
2388
SP_func_clock(edict_t * self)2389 void SP_func_clock (edict_t *self)
2390 {
2391 if (!self->target)
2392 {
2393 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
2394 G_FreeEdict (self);
2395 return;
2396 }
2397
2398 if ((self->spawnflags & 2) && (!self->count))
2399 {
2400 gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
2401 G_FreeEdict (self);
2402 return;
2403 }
2404
2405 if ((self->spawnflags & 1) && (!self->count))
2406 self->count = 60*60;;
2407
2408 func_clock_reset (self);
2409
2410 self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL);
2411
2412 self->think = func_clock_think;
2413
2414 if (self->spawnflags & 4)
2415 self->use = func_clock_use;
2416 else
2417 self->nextthink = level.time + 1;
2418 }
2419
2420 //=================================================================================
2421
teleporter_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)2422 void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
2423 {
2424 edict_t *dest;
2425 int i;
2426
2427 if (!other->client)
2428 return;
2429 dest = G_Find (NULL, FOFS(targetname), self->target);
2430 if (!dest)
2431 {
2432 gi.dprintf ("Couldn't find destination\n");
2433 return;
2434 }
2435
2436 // unlink to make sure it can't possibly interfere with KillBox
2437 gi.unlinkentity (other);
2438
2439 VectorCopy (dest->s.origin, other->s.origin);
2440 VectorCopy (dest->s.origin, other->s.old_origin);
2441 other->s.origin[2] += 10;
2442
2443 // clear the velocity and hold them in place briefly
2444 VectorClear (other->velocity);
2445 other->client->ps.pmove.pm_time = 160>>3; // hold time
2446 other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
2447
2448 // draw the teleport splash at source and on the player
2449 self->owner->s.event = EV_PLAYER_TELEPORT;
2450 other->s.event = EV_PLAYER_TELEPORT;
2451
2452 // set angles
2453 for (i=0 ; i<3 ; i++)
2454 {
2455 other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
2456 }
2457
2458 VectorClear (other->s.angles);
2459 VectorClear (other->client->ps.viewangles);
2460 VectorClear (other->client->v_angle);
2461
2462 // kill anything at the destination
2463 KillBox (other);
2464
2465 gi.linkentity (other);
2466 }
2467
2468 /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
2469 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
2470 */
SP_misc_teleporter(edict_t * ent)2471 void SP_misc_teleporter (edict_t *ent)
2472 {
2473 edict_t *trig;
2474
2475 if (!ent->target)
2476 {
2477 gi.dprintf ("teleporter without a target.\n");
2478 G_FreeEdict (ent);
2479 return;
2480 }
2481
2482 gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2483 ent->s.skinnum = 1;
2484 ent->s.effects = EF_TELEPORTER;
2485 ent->s.sound = gi.soundindex ("world/amb10.wav");
2486 ent->solid = SOLID_BBOX;
2487
2488 VectorSet (ent->mins, -32, -32, -24);
2489 VectorSet (ent->maxs, 32, 32, -16);
2490 gi.linkentity (ent);
2491
2492 trig = G_Spawn ();
2493 trig->touch = teleporter_touch;
2494
2495 trig->solid = SOLID_TRIGGER;
2496
2497
2498 trig->target = ent->target;
2499 trig->owner = ent;
2500 VectorCopy (ent->s.origin, trig->s.origin);
2501 VectorSet (trig->mins, -8, -8, 8);
2502 VectorSet (trig->maxs, 8, 8, 24);
2503 gi.linkentity (trig);
2504
2505 }
2506
2507 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
2508 Point teleporters at these.
2509 */
2510
2511 //sv_spawner->value
2512
SP_misc_teleporter_dest(edict_t * ent)2513 void SP_misc_teleporter_dest (edict_t *ent)
2514 {
2515 gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2516 ent->s.skinnum = 0;
2517 ent->solid = SOLID_BBOX;
2518 // ent->s.effects |= EF_FLIES;
2519 VectorSet (ent->mins, -32, -32, -24);
2520 VectorSet (ent->maxs, 32, 32, -16);
2521 gi.linkentity (ent);
2522 }
2523
SP_misc_spawn_dest(edict_t * ent)2524 void SP_misc_spawn_dest (edict_t *ent)
2525 {
2526
2527 gi.setmodel (ent, "models/objects/dmspot/tris.md2");
2528 ent->s.skinnum = 1;
2529
2530 if (sv_spawner->value<2)
2531 ent->solid = SOLID_NOT;
2532
2533 if (sv_spawner->value==1)
2534 {
2535 ent->s.renderfx = RF_GLOW;
2536 ent->s.effects = EF_SPHERETRANS;
2537 }
2538 else if (sv_spawner->value==0)
2539 {
2540 ent->s.renderfx = RF_BEAM;
2541 }
2542 else
2543 ent->solid = SOLID_BBOX;
2544
2545 VectorSet (ent->mins, -32, -32, -24);
2546 VectorSet (ent->maxs, 32, 32, -16);
2547 gi.linkentity (ent);
2548 }