|
|
1.1 root 1: // g_sphere.c
2: // pmack
3: // april 1998
4:
5: // defender - actively finds and shoots at enemies
6: // hunter - waits until < 25% health and vore ball tracks person who hurt you
7: // vengeance - kills person who killed you.
8:
9: #include "g_local.h"
10:
11: #define DEFENDER_LIFESPAN 30
12: #define HUNTER_LIFESPAN 30
13: #define VENGEANCE_LIFESPAN 30
14: #define MINIMUM_FLY_TIME 15
15: //#define MINIMUM_FLY_TIME 30
16:
17: // FIXME - do we need to be calling ED_NewString at all?
18: extern char *ED_NewString (char *string);
19: void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker);
20:
21:
22: void defender_think (edict_t *self);
23: void hunter_think (edict_t *self);
24: void vengeance_think (edict_t *self);
25: void vengeance_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
26: void hunter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
27:
28: // *************************
29: // General Sphere Code
30: // *************************
31:
32: // =================
33: // =================
34: void sphere_think_explode (edict_t *self)
35: {
36: if(self->owner && self->owner->client)
37: {
38: self->owner->client->owned_sphere = NULL;
39: }
40: BecomeExplosion1 (self);
41: }
42:
43: // =================
44: // sphere_explode
45: // =================
46: void sphere_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
47: {
48: // if(self->owner && self->owner->client)
49: // gi.cprintf(self->owner, PRINT_HIGH, "Sphere timed out\n");
50: // gi.dprintf("player died, blowing up\n");
51: sphere_think_explode (self);
52: }
53:
54: // =================
55: // sphere_if_idle_die - if the sphere is not currently attacking, blow up.
56: // =================
57: void sphere_if_idle_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
58: {
59: if(!self->enemy)
60: {
61: // gi.dprintf("player died, blowing up\n");
62: sphere_think_explode(self);
63: }
64: }
65:
66: // *************************
67: // Sphere Movement
68: // *************************
69:
70: // =================
71: // =================
72: void sphere_fly (edict_t *self)
73: {
74: vec3_t dest;
75: vec3_t dir;
76:
77: if(level.time >= self->wait)
78: {
79: // gi.dprintf("fly: timed out\n");
80: sphere_think_explode(self);
81: return;
82: }
83:
84: VectorCopy (self->owner->s.origin, dest);
85: dest[2] = self->owner->absmax[2] + 4;
86:
87: if(level.time == (float)(int)level.time)
88: {
89: if(!visible(self, self->owner))
90: {
91: VectorCopy(dest, self->s.origin);
92: gi.linkentity(self);
93: return;
94: }
95: }
96:
97: VectorSubtract (dest, self->s.origin, dir);
98: VectorScale (dir, 5, self->velocity);
99: }
100:
101: // =================
102: // =================
103: void sphere_chase (edict_t *self, int stupidChase)
104: {
105: vec3_t dest;
106: vec3_t dir;
107: float dist;
108:
109: if(level.time >= self->wait || (self->enemy && self->enemy->health < 1))
110: {
111: sphere_think_explode(self);
112: return;
113: }
114:
115: VectorCopy (self->enemy->s.origin, dest);
116: if(self->enemy->client)
117: dest[2] += self->enemy->viewheight;
118:
119: if(visible(self, self->enemy) || stupidChase)
120: {
121: VectorSubtract (dest, self->s.origin, dir);
122: VectorNormalize (dir);
123: vectoangles2(dir, self->s.angles);
124: VectorScale (dir, 500, self->velocity);
125: VectorCopy(dest, self->monsterinfo.saved_goal);
126: }
127: else if (VectorCompare (self->monsterinfo.saved_goal, vec3_origin))
128: {
129: VectorClear (self->velocity);
130: }
131: else
132: {
133: VectorSubtract(self->monsterinfo.saved_goal, self->s.origin, dir);
134: dist = VectorNormalize(dir);
135: vectoangles2(dir, self->s.angles);
136: if(dist)
137: {
138: if(dist > 500)
139: VectorScale(dir, 500, self->velocity);
140: else
141: VectorScale(dir, dist, self->velocity);
142: }
143: else
144: VectorClear(self->velocity);
145: }
146: }
147:
148: // *************************
149: // Attack related stuff
150: // *************************
151:
152: // =================
153: // =================
154: void sphere_fire (edict_t *self, edict_t *enemy)
155: {
156: vec3_t dest;
157: vec3_t dir;
158:
159: if(level.time >= self->wait || !enemy)
160: {
161: sphere_think_explode(self);
162: return;
163: }
164:
165: VectorCopy (enemy->s.origin, dest);
166: self->s.effects |= EF_ROCKET;
167:
168: VectorSubtract (dest, self->s.origin, dir);
169: VectorNormalize (dir);
170: vectoangles2 ( dir, self->s.angles );
171: VectorScale (dir, 1000, self->velocity);
172:
173: self->touch = vengeance_touch;
174: self->think = sphere_think_explode;
175: self->nextthink = self->wait;
176: }
177:
178: // =================
179: // =================
180: void sphere_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf, int mod)
181: {
182: if(self->spawnflags & SPHERE_DOPPLEGANGER)
183: {
184: if (other == self->teammaster)
185: return;
186:
187: self->takedamage = DAMAGE_NO;
188: self->owner = self->teammaster;
189: self->teammaster = NULL;
190: }
191: else
192: {
193: if (other == self->owner)
194: return;
195: // PMM - don't blow up on bodies
196: if (!strcmp(other->classname, "bodyque"))
197: return;
198: }
199:
200: if (surf && (surf->flags & SURF_SKY))
201: {
202: G_FreeEdict (self);
203: return;
204: }
205:
206: if (other->takedamage)
207: {
208: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
209: 10000, 1, DAMAGE_DESTROY_ARMOR, mod);
210: }
211: else
212: {
213: T_RadiusDamage (self, self->owner, 512, self->owner, 256, mod);
214: }
215:
216: sphere_think_explode (self);
217: }
218:
219: // =================
220: // =================
221: void vengeance_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
222: {
223: if(self->spawnflags & SPHERE_DOPPLEGANGER)
224: sphere_touch (self, other, plane, surf, MOD_DOPPLE_VENGEANCE);
225: else
226: sphere_touch (self, other, plane, surf, MOD_VENGEANCE_SPHERE);
227: }
228:
229: // =================
230: // =================
231: void hunter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
232: {
233: edict_t *owner;
234:
235: // don't blow up if you hit the world.... sheesh.
236: if(other==world)
237: return;
238:
239: if(self->owner)
240: {
241: // if owner is flying with us, make sure they stop too.
242: owner=self->owner;
243: if(owner->flags & FL_SAM_RAIMI)
244: {
245: VectorClear(owner->velocity);
246: owner->movetype = MOVETYPE_NONE;
247: gi.linkentity(owner);
248: }
249: }
250:
251: if(self->spawnflags & SPHERE_DOPPLEGANGER)
252: sphere_touch (self, other, plane, surf, MOD_DOPPLE_HUNTER);
253: else
254: sphere_touch (self, other, plane, surf, MOD_HUNTER_SPHERE);
255: }
256:
257: // =================
258: // =================
259: void defender_shoot (edict_t *self, edict_t *enemy)
260: {
261: vec3_t dir;
262: vec3_t start;
263:
264: if(!(enemy->inuse) || enemy->health <= 0)
265: return;
266:
267: if(enemy == self->owner)
268: return;
269:
270: VectorSubtract (enemy->s.origin, self->s.origin, dir);
271: VectorNormalize (dir);
272:
273: if(self->monsterinfo.attack_finished > level.time)
274: return;
275:
276: if(!visible(self, self->enemy))
277: return;
278:
279: VectorCopy(self->s.origin, start);
280: start[2] += 2;
281: fire_blaster2 (self->owner, start, dir, 10, 1000, EF_BLASTER, 0);
282:
283: self->monsterinfo.attack_finished = level.time + 0.4;
284: }
285:
286: // *************************
287: // Activation Related Stuff
288: // *************************
289:
290: // =================
291: // =================
292: void body_gib (edict_t *self)
293: {
294: int n;
295:
296: gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
297: for (n= 0; n < 4; n++)
298: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", 50, GIB_ORGANIC);
299: ThrowGib (self, "models/objects/gibs/skull/tris.md2", 50, GIB_ORGANIC);
300: }
301:
302: // =================
303: // =================
304: void hunter_pain (edict_t *self, edict_t *other, float kick, int damage)
305: {
306: edict_t *owner;
307: float dist;
308: vec3_t dir;
309:
310: if(self->enemy)
311: return;
312:
313: owner = self->owner;
314:
315: if(!(self->spawnflags & SPHERE_DOPPLEGANGER))
316: {
317: if(owner && (owner->health > 0))
318: return;
319:
320: //PMM
321: if(other == owner)
322: {
323: // if ((g_showlogic) && (g_showlogic->value))
324: // gi.dprintf ("hunter: won't get mad at my owner!\n");
325: return;
326: }
327: //pmm
328: }
329: else
330: {
331: // if fired by a doppleganger, set it to 10 second timeout
332: self->wait = level.time + MINIMUM_FLY_TIME;
333: }
334:
335: if((self->wait - level.time) < MINIMUM_FLY_TIME)
336: self->wait = level.time + MINIMUM_FLY_TIME;
337: self->s.effects |= EF_BLASTER | EF_TRACKER;
338: self->touch = hunter_touch;
339: self->enemy = other;
340:
341: if(g_showlogic && g_showlogic->value)
342: gi.dprintf("hunter_pain: mad at %s\n", other->classname);
343:
344: // if we're not owned by a player, no sam raimi
345: // if we're spawned by a doppleganger, no sam raimi
346: if((self->spawnflags & SPHERE_DOPPLEGANGER) || !(owner && owner->client))
347: return;
348:
349: // sam raimi cam is disabled if FORCE_RESPAWN is set.
350: // sam raimi cam is also disabled if huntercam->value is 0.
351: if(!((int)dmflags->value & DF_FORCE_RESPAWN) && (huntercam && (huntercam->value)))
352: {
353: VectorSubtract(other->s.origin, self->s.origin, dir);
354: dist=VectorLength(dir);
355:
356: if(owner && (dist >= 192))
357: {
358: // detach owner from body and send him flying
359: owner->movetype = MOVETYPE_FLYMISSILE;
360:
361: // gib like we just died, even though we didn't, really.
362: body_gib(owner);
363:
364: // move the sphere to the owner's current viewpoint.
365: // we know it's a valid spot (or will be momentarily)
366: VectorCopy(owner->s.origin, self->s.origin);
367: self->s.origin[2] += owner->viewheight;
368:
369: // move the player's origin to the sphere's new origin
370: VectorCopy(self->s.origin, owner->s.origin);
371: VectorCopy(self->s.angles, owner->s.angles);
372: VectorCopy(self->s.angles, owner->client->v_angle);
373: VectorClear(owner->mins);
374: VectorClear(owner->maxs);
375: VectorSet(owner->mins, -5, -5, -5);
376: VectorSet(owner->maxs, 5, 5, 5);
377: owner->client->ps.fov = 140;
378: owner->s.modelindex = 0;
379: owner->s.modelindex2 = 0;
380: owner->viewheight = 8;
381: owner->solid = SOLID_NOT;
382: owner->flags |= FL_SAM_RAIMI;
383: gi.linkentity(owner);
384:
385: // PMM - set bounding box so we don't clip out of world
386: // VectorSet(self->mins, -5, -5, -5);
387: // VectorSet(self->maxs, 5, 5, 5);
388: self->solid = SOLID_BBOX;
389: gi.linkentity (self);
390: }
391: // else
392: // gi.dprintf("too close for sam raimi cam\n");
393: }
394: }
395:
396: // =================
397: // =================
398: void defender_pain (edict_t *self, edict_t *other, float kick, int damage)
399: {
400: //PMM
401: if(other == self->owner)
402: {
403: // if ((g_showlogic) && (g_showlogic->value))
404: // gi.dprintf ("defender: won't get mad at my owner!\n");
405: return;
406: }
407: //pmm
408: self->enemy = other;
409: }
410:
411: // =================
412: // =================
413: void vengeance_pain (edict_t *self, edict_t *other, float kick, int damage)
414: {
415: if(self->enemy)
416: return;
417:
418: if(!(self->spawnflags & SPHERE_DOPPLEGANGER))
419: {
420: if(self->owner->health >= 25)
421: return;
422:
423: //PMM
424: if(other == self->owner)
425: {
426: // if ((g_showlogic) && (g_showlogic->value))
427: // gi.dprintf ("vengeance: won't get mad at my owner!\n");
428: return;
429: }
430: //pmm
431: }
432: else
433: {
434: self->wait = level.time + MINIMUM_FLY_TIME;
435: }
436:
437: if((self->wait - level.time) < MINIMUM_FLY_TIME)
438: self->wait = level.time + MINIMUM_FLY_TIME;
439: self->s.effects |= EF_ROCKET;
440: self->touch = vengeance_touch;
441: self->enemy = other;
442: }
443:
444: // *************************
445: // Think Functions
446: // *************************
447:
448: // ===================
449: // ===================
450: void defender_think (edict_t *self)
451: {
452: if(!self->owner)
453: {
454: // gi.dprintf("think: no owner\n");
455: G_FreeEdict(self);
456: return;
457: }
458:
459: if(self->owner->health <=0)
460: {
461: sphere_think_explode(self);
462: return;
463: }
464:
465: if(level.time - self->timestamp > 1)
466: {
467: gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/dsphere.wav"), 0.6, ATTN_NORM, 0);
468: self->timestamp = level.time;
469: }
470:
471: self->s.frame++;
472: if(self->s.frame>19)
473: self->s.frame = 0;
474:
475: if(self->enemy)
476: {
477: if(self->enemy->health > 0)
478: {
479: // gi.dprintf( "shooting at %s\n", self->enemy->classname);
480: defender_shoot (self, self->enemy);
481: }
482: else
483: self->enemy = NULL;
484: }
485: // else
486: // {
487: // self->ideal_yaw+=3;
488: // M_ChangeYaw (self);
489: // }
490:
491: sphere_fly (self);
492:
493: if(self->inuse)
494: self->nextthink = level.time + 0.1;
495: }
496:
497: // =================
498: // =================
499: void hunter_think (edict_t *self)
500: {
501: edict_t *owner;
502: vec3_t dir, ang;
503:
504:
505: owner = self->owner;
506: if(!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER))
507: {
508: // gi.dprintf("think: no owner\n");
509: G_FreeEdict(self);
510: return;
511: }
512:
513: if(owner)
514: self->ideal_yaw = owner->s.angles[YAW];
515: else if(self->enemy) // fired by doppleganger
516: {
517: VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
518: vectoangles2(dir, ang);
519: self->ideal_yaw = ang[YAW];
520: }
521:
522: M_ChangeYaw(self);
523:
524: if(level.time - self->timestamp > 1)
525: {
526: gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/hsphere.wav"), 0.5, ATTN_NORM, 0);
527: self->timestamp = level.time;
528: }
529:
530: if(self->enemy)
531: {
532: sphere_chase (self, 0);
533:
534: // deal with sam raimi cam
535: if(owner && (owner->flags & FL_SAM_RAIMI))
536: {
537: if(self->inuse)
538: {
539: owner->movetype = MOVETYPE_FLYMISSILE;
540: // VectorCopy(self->s.angles, owner->s.angles);
541: // VectorCopy(self->s.angles, owner->client->v_angle);
542: LookAtKiller (owner, self, self->enemy);
543: // owner->viewheight = 22;
544:
545: // owner->client->v_angle[YAW]+=5;
546: // owner is flying with us, move him too
547: owner->movetype = MOVETYPE_FLYMISSILE;
548: owner->viewheight = self->s.origin[2] - owner->s.origin[2];
549: // VectorCopy(self->s.angles, owner->s.angles);
550: // VectorCopy(self->s.angles, owner->client->v_angle);
551: VectorCopy(self->s.origin, owner->s.origin);
552: VectorCopy(self->velocity, owner->velocity);
553: VectorClear(owner->mins);
554: VectorClear(owner->maxs);
555: gi.linkentity(owner);
556: }
557: else // sphere timed out
558: {
559: VectorClear(owner->velocity);
560: owner->movetype = MOVETYPE_NONE;
561: gi.linkentity(owner);
562: }
563: }
564: }
565: else
566: {
567: // self->ideal_yaw+=3;
568: // M_ChangeYaw (self);
569: sphere_fly (self);
570: }
571:
572: if(self->inuse)
573: self->nextthink = level.time + 0.1;
574: }
575:
576: // =================
577: // =================
578: void vengeance_think (edict_t *self)
579: {
580: if(!(self->owner) && !(self->spawnflags & SPHERE_DOPPLEGANGER))
581: {
582: // gi.dprintf("think: no owner\n");
583: G_FreeEdict(self);
584: return;
585: }
586:
587: if(level.time - self->timestamp > 1)
588: {
589: gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/vsphere.wav"), 0.5, ATTN_NORM, 0);
590: self->timestamp = level.time;
591: }
592:
593: if(self->enemy)
594: {
595: // sphere_fire (self, self->owner->enemy);
596: sphere_chase (self, 1);
597: }
598: else
599: sphere_fly (self);
600:
601: if(self->inuse)
602: self->nextthink = level.time + 0.1;
603: }
604:
605: // *************************
606: // Spawning / Creation
607: // *************************
608:
609: // monsterinfo_t
610: // =================
611: // =================
612: edict_t *Sphere_Spawn (edict_t *owner, int spawnflags)
613: {
614: edict_t *sphere;
615:
616: sphere = G_Spawn();
617: VectorCopy(owner->s.origin, sphere->s.origin);
618: sphere->s.origin[2] = owner->absmax[2];
619: sphere->s.angles[YAW] = owner->s.angles[YAW];
620: sphere->solid = SOLID_BBOX;
621: sphere->clipmask = MASK_SHOT;
622: sphere->s.renderfx = RF_FULLBRIGHT | RF_IR_VISIBLE;
623: sphere->movetype = MOVETYPE_FLYMISSILE;
624:
625: if(spawnflags & SPHERE_DOPPLEGANGER)
626: sphere->teammaster = owner->teammaster;
627: else
628: sphere->owner = owner;
629:
630: sphere->classname = "sphere";
631: sphere->yaw_speed = 40;
632: sphere->monsterinfo.attack_finished = 0;
633: sphere->spawnflags = spawnflags; // need this for the HUD to recognize sphere
634: //PMM
635: sphere->takedamage = DAMAGE_NO;
636:
637: switch(spawnflags & SPHERE_TYPE)
638: {
639: case SPHERE_DEFENDER:
640: sphere->s.modelindex = gi.modelindex("models/items/defender/tris.md2");
641: sphere->s.modelindex2 = gi.modelindex("models/items/shell/tris.md2") | 0x80;
642: sphere->pain = defender_pain;
643: sphere->wait = level.time + DEFENDER_LIFESPAN;
644: sphere->die = sphere_explode;
645: sphere->think = defender_think;
646: break;
647: case SPHERE_HUNTER:
648: sphere->s.modelindex = gi.modelindex("models/items/hunter/tris.md2");
649: sphere->wait = level.time + HUNTER_LIFESPAN;
650: sphere->pain = hunter_pain;
651: sphere->die = sphere_if_idle_die;
652: sphere->think = hunter_think;
653: break;
654: case SPHERE_VENGEANCE:
655: sphere->s.modelindex = gi.modelindex("models/items/vengnce/tris.md2");
656: sphere->wait = level.time + VENGEANCE_LIFESPAN;
657: sphere->pain = vengeance_pain;
658: sphere->die = sphere_if_idle_die;
659: sphere->think = vengeance_think;
660: VectorSet (sphere->avelocity, 30, 30, 0);
661: break;
662: default:
663: gi.dprintf("Tried to create an invalid sphere\n");
664: G_FreeEdict(sphere);
665: return NULL;
666: }
667:
668: sphere->nextthink = level.time + 0.1;
669:
670: gi.linkentity (sphere);
671:
672: return sphere;
673: }
674:
675: // =================
676: // Own_Sphere - attach the sphere to the client so we can
677: // directly access it later
678: // =================
679: void Own_Sphere (edict_t *self, edict_t *sphere)
680: {
681: if(!sphere)
682: return;
683:
684: // ownership only for players
685: if(self->client)
686: {
687: // if they don't have one
688: if(!(self->client->owned_sphere))
689: {
690: self->client->owned_sphere = sphere;
691: }
692: // they already have one, take care of the old one
693: else
694: {
695: if(self->client->owned_sphere->inuse)
696: {
697: G_FreeEdict(self->client->owned_sphere);
698: self->client->owned_sphere = sphere;
699: }
700: else
701: {
702: self->client->owned_sphere = sphere;
703: }
704: }
705: }
706: }
707:
708: // =================
709: // =================
710: void Defender_Launch (edict_t *self)
711: {
712: edict_t *sphere;
713:
714: sphere = Sphere_Spawn (self, SPHERE_DEFENDER);
715: Own_Sphere (self, sphere);
716: }
717:
718: // =================
719: // =================
720: void Hunter_Launch (edict_t *self)
721: {
722: edict_t *sphere;
723:
724: sphere = Sphere_Spawn (self, SPHERE_HUNTER);
725: Own_Sphere (self, sphere);
726: }
727:
728: // =================
729: // =================
730: void Vengeance_Launch (edict_t *self)
731: {
732: edict_t *sphere;
733:
734: sphere = Sphere_Spawn (self, SPHERE_VENGEANCE);
735: Own_Sphere (self, sphere);
736: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.