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