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