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