|
|
1.1 root 1: #include "g_local.h"
2:
3:
4: //
5: // monster weapons
6: //
7:
8: //FIXME mosnters should call these with a totally accurate direction
9: // and we can mess it up based on skill. Spread should be for normal
10: // and we can tighten or loosen based on skill. We could muck with
11: // the damages too, but I'm not sure that's such a good idea.
12: void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype)
13: {
14: fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
15:
16: gi.WriteByte (svc_muzzleflash2);
17: gi.WriteShort (self - g_edicts);
18: gi.WriteByte (flashtype);
19: gi.multicast (start, MULTICAST_PVS);
20: }
21:
22: void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
23: {
24: fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
25:
26: gi.WriteByte (svc_muzzleflash2);
27: gi.WriteShort (self - g_edicts);
28: gi.WriteByte (flashtype);
29: gi.multicast (start, MULTICAST_PVS);
30: }
31:
32: void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
33: {
34: fire_blaster (self, start, dir, damage, speed, effect, false);
35:
36: gi.WriteByte (svc_muzzleflash2);
37: gi.WriteShort (self - g_edicts);
38: gi.WriteByte (flashtype);
39: gi.multicast (start, MULTICAST_PVS);
40: }
41:
42: //ROGUE
43: void monster_fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
44: {
45: fire_blaster2 (self, start, dir, damage, speed, effect, false);
46:
47: gi.WriteByte (svc_muzzleflash2);
48: gi.WriteShort (self - g_edicts);
49: gi.WriteByte (flashtype);
50: gi.multicast (start, MULTICAST_PVS);
51: }
52:
53: // FIXME -- add muzzle flash
54: void monster_fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy, int flashtype)
55: {
56: fire_tracker (self, start, dir, damage, speed, enemy);
57:
58: gi.WriteByte (svc_muzzleflash2);
59: gi.WriteShort (self - g_edicts);
60: gi.WriteByte (flashtype);
61: gi.multicast (start, MULTICAST_PVS);
62: }
63:
64: void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, int damage, int kick, int flashtype)
65: {
66: fire_heat (self, start, dir, offset, damage, kick, true);
67:
68: gi.WriteByte (svc_muzzleflash2);
69: gi.WriteShort (self - g_edicts);
70: gi.WriteByte (flashtype);
71: gi.multicast (start, MULTICAST_PVS);
72: }
73: //ROGUE
74:
75: void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
76: {
77: fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
78:
79: gi.WriteByte (svc_muzzleflash2);
80: gi.WriteShort (self - g_edicts);
81: gi.WriteByte (flashtype);
82: gi.multicast (start, MULTICAST_PVS);
83: }
84:
85: void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
86: {
87: fire_rocket (self, start, dir, damage, speed, damage+20, damage);
88:
89: gi.WriteByte (svc_muzzleflash2);
90: gi.WriteShort (self - g_edicts);
91: gi.WriteByte (flashtype);
92: gi.multicast (start, MULTICAST_PVS);
93: }
94:
95: void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
96: {
97: // PMM
98: if (!(gi.pointcontents (start) & MASK_SOLID))
99: fire_rail (self, start, aimdir, damage, kick);
100:
101: gi.WriteByte (svc_muzzleflash2);
102: gi.WriteShort (self - g_edicts);
103: gi.WriteByte (flashtype);
104: gi.multicast (start, MULTICAST_PVS);
105: }
106:
107: void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
108: {
109: fire_bfg (self, start, aimdir, damage, speed, damage_radius);
110:
111: gi.WriteByte (svc_muzzleflash2);
112: gi.WriteShort (self - g_edicts);
113: gi.WriteByte (flashtype);
114: gi.multicast (start, MULTICAST_PVS);
115: }
116:
117:
118:
119: //
120: // Monster utility functions
121: //
122:
123: void M_FliesOff (edict_t *self)
124: {
125: self->s.effects &= ~EF_FLIES;
126: self->s.sound = 0;
127: }
128:
129: void M_FliesOn (edict_t *self)
130: {
131: if (self->waterlevel)
132: return;
133: self->s.effects |= EF_FLIES;
134: self->s.sound = gi.soundindex ("infantry/inflies1.wav");
135: self->think = M_FliesOff;
136: self->nextthink = level.time + 60;
137: }
138:
139: void M_FlyCheck (edict_t *self)
140: {
141: if (self->waterlevel)
142: return;
143:
144: if (random() > 0.5)
145: return;
146:
147: self->think = M_FliesOn;
148: self->nextthink = level.time + 5 + 10 * random();
149: }
150:
151: void AttackFinished (edict_t *self, float time)
152: {
153: self->monsterinfo.attack_finished = level.time + time;
154: }
155:
156:
157: void M_CheckGround (edict_t *ent)
158: {
159: vec3_t point;
160: trace_t trace;
161:
162: if (ent->flags & (FL_SWIM|FL_FLY))
163: return;
164:
165: #ifdef ROGUE_GRAVITY
166: if ((ent->velocity[2] * ent->gravityVector[2]) < -100) // PGM
167: #else
168: if (ent->velocity[2] > 100)
169: #endif
170: {
171: ent->groundentity = NULL;
172: return;
173: }
174:
175: // if the hull point one-quarter unit down is solid the entity is on ground
176: point[0] = ent->s.origin[0];
177: point[1] = ent->s.origin[1];
178: #ifdef ROGUE_GRAVITY
179: point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]); //PGM
180: #else
181: point[2] = ent->s.origin[2] - 0.25;
182: #endif
183:
184: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
185:
186: // check steepness
187: #ifdef ROGUE_GRAVITY
188: //PGM
189: if ( ent->gravityVector[2] < 0) // normal gravity
190: {
191: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
192: {
193: ent->groundentity = NULL;
194: return;
195: }
196: }
197: else // inverted gravity
198: {
199: if ( trace.plane.normal[2] > -0.7 && !trace.startsolid)
200: {
201: ent->groundentity = NULL;
202: return;
203: }
204: }
205: //PGM
206: #else
207: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
208: {
209: ent->groundentity = NULL;
210: return;
211: }
212: #endif
213:
214: // ent->groundentity = trace.ent;
215: // ent->groundentity_linkcount = trace.ent->linkcount;
216: // if (!trace.startsolid && !trace.allsolid)
217: // VectorCopy (trace.endpos, ent->s.origin);
218: if (!trace.startsolid && !trace.allsolid)
219: {
220: VectorCopy (trace.endpos, ent->s.origin);
221: ent->groundentity = trace.ent;
222: ent->groundentity_linkcount = trace.ent->linkcount;
223: ent->velocity[2] = 0;
224: }
225: }
226:
227:
228: void M_CatagorizePosition (edict_t *ent)
229: {
230: vec3_t point;
231: int cont;
232:
233: //
234: // get waterlevel
235: //
236: point[0] = ent->s.origin[0];
237: point[1] = ent->s.origin[1];
238: point[2] = ent->s.origin[2] + ent->mins[2] + 1;
239: cont = gi.pointcontents (point);
240:
241: if (!(cont & MASK_WATER))
242: {
243: ent->waterlevel = 0;
244: ent->watertype = 0;
245: return;
246: }
247:
248: ent->watertype = cont;
249: ent->waterlevel = 1;
250: point[2] += 26;
251: cont = gi.pointcontents (point);
252: if (!(cont & MASK_WATER))
253: return;
254:
255: ent->waterlevel = 2;
256: point[2] += 22;
257: cont = gi.pointcontents (point);
258: if (cont & MASK_WATER)
259: ent->waterlevel = 3;
260: }
261:
262:
263: void M_WorldEffects (edict_t *ent)
264: {
265: int dmg;
266:
267: if (ent->health > 0)
268: {
269: if (!(ent->flags & FL_SWIM))
270: {
271: if (ent->waterlevel < 3)
272: {
273: ent->air_finished = level.time + 12;
274: }
275: else if (ent->air_finished < level.time)
276: { // drown!
277: if (ent->pain_debounce_time < level.time)
278: {
279: dmg = 2 + 2 * floor(level.time - ent->air_finished);
280: if (dmg > 15)
281: dmg = 15;
282: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
283: ent->pain_debounce_time = level.time + 1;
284: }
285: }
286: }
287: else
288: {
289: if (ent->waterlevel > 0)
290: {
291: ent->air_finished = level.time + 9;
292: }
293: else if (ent->air_finished < level.time)
294: { // suffocate!
295: if (ent->pain_debounce_time < level.time)
296: {
297: dmg = 2 + 2 * floor(level.time - ent->air_finished);
298: if (dmg > 15)
299: dmg = 15;
300: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
301: ent->pain_debounce_time = level.time + 1;
302: }
303: }
304: }
305: }
306:
307: if (ent->waterlevel == 0)
308: {
309: if (ent->flags & FL_INWATER)
310: {
311: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
312: ent->flags &= ~FL_INWATER;
313: }
314: return;
315: }
316:
317: if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
318: {
319: if (ent->damage_debounce_time < level.time)
320: {
321: ent->damage_debounce_time = level.time + 0.2;
322: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
323: }
324: }
325: if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
326: {
327: if (ent->damage_debounce_time < level.time)
328: {
329: ent->damage_debounce_time = level.time + 1;
330: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
331: }
332: }
333:
334: if ( !(ent->flags & FL_INWATER) )
335: {
336: if (!(ent->svflags & SVF_DEADMONSTER))
337: {
338: if (ent->watertype & CONTENTS_LAVA)
339: if (random() <= 0.5)
340: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
341: else
342: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
343: else if (ent->watertype & CONTENTS_SLIME)
344: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
345: else if (ent->watertype & CONTENTS_WATER)
346: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
347: }
348:
349: ent->flags |= FL_INWATER;
350: ent->damage_debounce_time = 0;
351: }
352: }
353:
354:
355: void M_droptofloor (edict_t *ent)
356: {
357: vec3_t end;
358: trace_t trace;
359:
360: #ifdef ROGUE_GRAVITY
361: //PGM
362: if(ent->gravityVector[2] < 0)
363: {
364: ent->s.origin[2] += 1;
365: VectorCopy (ent->s.origin, end);
366: end[2] -= 256;
367: }
368: else
369: {
370: ent->s.origin[2] -= 1;
371: VectorCopy (ent->s.origin, end);
372: end[2] += 256;
373: }
374: //PGM
375: #else
376: ent->s.origin[2] += 1;
377: VectorCopy (ent->s.origin, end);
378: end[2] -= 256;
379: #endif
380:
381: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
382:
383: if (trace.fraction == 1 || trace.allsolid)
384: return;
385:
386: VectorCopy (trace.endpos, ent->s.origin);
387:
388: gi.linkentity (ent);
389: M_CheckGround (ent);
390: M_CatagorizePosition (ent);
391: }
392:
393:
394: void M_SetEffects (edict_t *ent)
395: {
396: int remaining;
397:
398: ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN|EF_DOUBLE|EF_QUAD|EF_PENT);
399: ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE|RF_SHELL_DOUBLE);
400:
401: if (ent->monsterinfo.aiflags & AI_RESURRECTING)
402: {
403: ent->s.effects |= EF_COLOR_SHELL;
404: ent->s.renderfx |= RF_SHELL_RED;
405: }
406:
407: if (ent->health <= 0)
408: return;
409:
410: if (ent->powerarmor_time > level.time)
411: {
412: if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
413: {
414: ent->s.effects |= EF_POWERSCREEN;
415: }
416: else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
417: {
418: ent->s.effects |= EF_COLOR_SHELL;
419: ent->s.renderfx |= RF_SHELL_GREEN;
420: }
421: }
422: // PMM - new monster powerups
423: if (ent->monsterinfo.quad_framenum > level.framenum)
424: {
425: remaining = ent->monsterinfo.quad_framenum - level.framenum;
426: if (remaining > 30 || (remaining & 4) )
427: ent->s.effects |= EF_QUAD;
428: }
429: else
430: ent->s.effects &= ~EF_QUAD;
431:
432: if (ent->monsterinfo.double_framenum > level.framenum)
433: {
434: remaining = ent->monsterinfo.double_framenum - level.framenum;
435: if (remaining > 30 || (remaining & 4) )
436: ent->s.effects |= EF_DOUBLE;
437: }
438: else
439: ent->s.effects &= ~EF_DOUBLE;
440:
441: if (ent->monsterinfo.invincible_framenum > level.framenum)
442: {
443: remaining = ent->monsterinfo.invincible_framenum - level.framenum;
444: if (remaining > 30 || (remaining & 4) )
445: ent->s.effects |= EF_PENT;
446: }
447: else
448: ent->s.effects &= ~EF_PENT;
449:
450: // PMM
451: // PMM - testing
452: // ent->s.effects |= EF_COLOR_SHELL;
453: // ent->s.renderfx |= RF_SHELL_HALF_DAM;
454: /*
455: if (fmod (level.time, 4.0) > 2.0)
456: {
457: gi.dprintf ("invulnerable ");
458: ent->s.renderfx |= RF_SHELL_RED;
459: }
460: else
461: ent->s.renderfx &= ~RF_SHELL_RED;
462:
463: if (fmod (level.time, 8.0) > 4.0)
464: {
465: gi.dprintf ("shield ");
466: ent->s.renderfx |= RF_SHELL_GREEN;
467: }
468: else
469: ent->s.renderfx &= ~RF_SHELL_GREEN;
470:
471: if (fmod (level.time, 16.0) > 8.0)
472: {
473: gi.dprintf ("quad ");
474: ent->s.renderfx |= RF_SHELL_BLUE;\
475: }
476: else
477: ent->s.renderfx &= ~RF_SHELL_BLUE;
478:
479: if (fmod (level.time, 32.0) > 16.0)
480: {
481: gi.dprintf ("double ");
482: ent->s.renderfx |= RF_SHELL_DOUBLE;
483: }
484: else
485: ent->s.renderfx &= ~RF_SHELL_DOUBLE;
486:
487: if (fmod (level.time, 64.0) > 32.0)
488: {
489: gi.dprintf ("half ");
490: ent->s.renderfx |= RF_SHELL_HALF_DAM;
491: }
492: else
493: ent->s.renderfx &= ~RF_SHELL_HALF_DAM;
494:
495: gi.dprintf ("\n");
496: */
497: }
498:
499:
500: void M_MoveFrame (edict_t *self)
501: {
502: mmove_t *move;
503: int index;
504:
505: move = self->monsterinfo.currentmove;
506: self->nextthink = level.time + FRAMETIME;
507:
508: if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
509: {
510: self->s.frame = self->monsterinfo.nextframe;
511: self->monsterinfo.nextframe = 0;
512: }
513: else
514: {
515: if (self->s.frame == move->lastframe)
516: {
517: if (move->endfunc)
518: {
519: move->endfunc (self);
520:
521: // regrab move, endfunc is very likely to change it
522: move = self->monsterinfo.currentmove;
523:
524: // check for death
525: if (self->svflags & SVF_DEADMONSTER)
526: return;
527: }
528: }
529:
530: if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
531: {
532: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
533: self->s.frame = move->firstframe;
534: }
535: else
536: {
537: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
538: {
539: self->s.frame++;
540: if (self->s.frame > move->lastframe)
541: self->s.frame = move->firstframe;
542: }
543: }
544: }
545:
546: index = self->s.frame - move->firstframe;
547:
548: if (move->frame[index].aifunc)
549: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
550: move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
551: else
552: move->frame[index].aifunc (self, 0);
553:
554: if (move->frame[index].thinkfunc)
555: move->frame[index].thinkfunc (self);
556: }
557:
558:
559: void monster_think (edict_t *self)
560: {
561: M_MoveFrame (self);
562: if (self->linkcount != self->monsterinfo.linkcount)
563: {
564: self->monsterinfo.linkcount = self->linkcount;
565: M_CheckGround (self);
566: }
567: M_CatagorizePosition (self);
568: M_WorldEffects (self);
569: M_SetEffects (self);
570: }
571:
572:
573: /*
574: ================
575: monster_use
576:
577: Using a monster makes it angry at the current activator
578: ================
579: */
580: void monster_use (edict_t *self, edict_t *other, edict_t *activator)
581: {
582: if (self->enemy)
583: return;
584: if (self->health <= 0)
585: return;
586: if (activator->flags & FL_NOTARGET)
587: return;
588: if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
589: return;
590: if (activator->flags & FL_DISGUISED) // PGM
591: return; // PGM
592:
593: // delay reaction so if the monster is teleported, its sound is still heard
594: self->enemy = activator;
595: FoundTarget (self);
596: }
597:
598:
599: void monster_start_go (edict_t *self);
600:
601:
602: void monster_triggered_spawn (edict_t *self)
603: {
604: self->s.origin[2] += 1;
605: KillBox (self);
606:
607: self->solid = SOLID_BBOX;
608: self->movetype = MOVETYPE_STEP;
609: self->svflags &= ~SVF_NOCLIENT;
610: self->air_finished = level.time + 12;
611: gi.linkentity (self);
612:
613: monster_start_go (self);
614:
615: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
616: {
617: if(!(self->enemy->flags & FL_DISGUISED)) // PGM
618: FoundTarget (self);
619: }
620: else
621: {
622: self->enemy = NULL;
623: }
624: }
625:
626: void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
627: {
628: // we have a one frame delay here so we don't telefrag the guy who activated us
629: self->think = monster_triggered_spawn;
630: self->nextthink = level.time + FRAMETIME;
631: if (activator->client)
632: self->enemy = activator;
633: self->use = monster_use;
634: }
635:
636: void monster_triggered_start (edict_t *self)
637: {
638: self->solid = SOLID_NOT;
639: self->movetype = MOVETYPE_NONE;
640: self->svflags |= SVF_NOCLIENT;
641: self->nextthink = 0;
642: self->use = monster_triggered_spawn_use;
643: }
644:
645:
646: /*
647: ================
648: monster_death_use
649:
650: When a monster dies, it fires all of its targets with the current
651: enemy as activator.
652: ================
653: */
654: void monster_death_use (edict_t *self)
655: {
656: self->flags &= ~(FL_FLY|FL_SWIM);
657: self->monsterinfo.aiflags &= AI_GOOD_GUY;
658:
659: if (self->item)
660: {
661: Drop_Item (self, self->item);
662: self->item = NULL;
663: }
664:
665: if (self->deathtarget)
666: self->target = self->deathtarget;
667:
668: if (!self->target)
669: return;
670:
671: G_UseTargets (self, self->enemy);
672: }
673:
674:
675: //============================================================================
676:
677: qboolean monster_start (edict_t *self)
678: {
679: if (deathmatch->value)
680: {
681: G_FreeEdict (self);
682: return false;
683: }
684:
685: if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
686: {
687: self->spawnflags &= ~4;
688: self->spawnflags |= 1;
689: // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
690: }
691:
692: if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
693: level.total_monsters++;
694:
695: self->nextthink = level.time + FRAMETIME;
696: self->svflags |= SVF_MONSTER;
697: self->s.renderfx |= RF_FRAMELERP;
698: self->takedamage = DAMAGE_AIM;
699: self->air_finished = level.time + 12;
700: self->use = monster_use;
701: self->max_health = self->health;
702: self->clipmask = MASK_MONSTERSOLID;
703:
704: self->s.skinnum = 0;
705: self->deadflag = DEAD_NO;
706: self->svflags &= ~SVF_DEADMONSTER;
707:
708: if (!self->monsterinfo.checkattack)
709: self->monsterinfo.checkattack = M_CheckAttack;
710: VectorCopy (self->s.origin, self->s.old_origin);
711:
712: if (st.item)
713: {
714: self->item = FindItemByClassname (st.item);
715: if (!self->item)
716: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
717: }
718:
719: // randomize what frame they start on
720: if (self->monsterinfo.currentmove)
721: self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
722:
723: // PMM - get this so I don't have to do it in all of the monsters
724: self->monsterinfo.base_height = self->maxs[2];
725:
726: // PMM - clear these
727: self->monsterinfo.quad_framenum = 0;
728: self->monsterinfo.double_framenum = 0;
729: self->monsterinfo.invincible_framenum = 0;
730:
731: return true;
732: }
733:
734: void monster_start_go (edict_t *self)
735: {
736: vec3_t v;
737:
738: if (self->health <= 0)
739: return;
740:
741: // check for target to combat_point and change to combattarget
742: if (self->target)
743: {
744: qboolean notcombat;
745: qboolean fixup;
746: edict_t *target;
747:
748: target = NULL;
749: notcombat = false;
750: fixup = false;
751: while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
752: {
753: if (strcmp(target->classname, "point_combat") == 0)
754: {
755: self->combattarget = self->target;
756: fixup = true;
757: }
758: else
759: {
760: notcombat = true;
761: }
762: }
763: if (notcombat && self->combattarget)
764: gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
765: if (fixup)
766: self->target = NULL;
767: }
768:
769: // validate combattarget
770: if (self->combattarget)
771: {
772: edict_t *target;
773:
774: target = NULL;
775: while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
776: {
777: if (strcmp(target->classname, "point_combat") != 0)
778: {
779: gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
780: self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
781: self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
782: (int)target->s.origin[2]);
783: }
784: }
785: }
786:
787: if (self->target)
788: {
789: self->goalentity = self->movetarget = G_PickTarget(self->target);
790: if (!self->movetarget)
791: {
792: gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
793: self->target = NULL;
794: self->monsterinfo.pausetime = 100000000;
795: self->monsterinfo.stand (self);
796: }
797: else if (strcmp (self->movetarget->classname, "path_corner") == 0)
798: {
799: VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
800: self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
801: self->monsterinfo.walk (self);
802: self->target = NULL;
803: }
804: else
805: {
806: self->goalentity = self->movetarget = NULL;
807: self->monsterinfo.pausetime = 100000000;
808: self->monsterinfo.stand (self);
809: }
810: }
811: else
812: {
813: self->monsterinfo.pausetime = 100000000;
814: self->monsterinfo.stand (self);
815: }
816:
817: self->think = monster_think;
818: self->nextthink = level.time + FRAMETIME;
819: }
820:
821:
822: void walkmonster_start_go (edict_t *self)
823: {
824: if (!(self->spawnflags & 2) && level.time < 1)
825: {
826: M_droptofloor (self);
827:
828: if (self->groundentity)
829: if (!M_walkmove (self, 0, 0))
830: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
831: }
832:
833: if (!self->yaw_speed)
834: self->yaw_speed = 20;
835: // PMM - stalkers are too short for this
836: if (!(strcmp(self->classname, "monster_stalker")))
837: self->viewheight = 15;
838: else
839: self->viewheight = 25;
840:
841: monster_start_go (self);
842:
843: if (self->spawnflags & 2)
844: monster_triggered_start (self);
845: }
846:
847: void walkmonster_start (edict_t *self)
848: {
849: self->think = walkmonster_start_go;
850: monster_start (self);
851: }
852:
853:
854: void flymonster_start_go (edict_t *self)
855: {
856: if (!M_walkmove (self, 0, 0))
857: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
858:
859: if (!self->yaw_speed)
860: self->yaw_speed = 10;
861: self->viewheight = 25;
862:
863: monster_start_go (self);
864:
865: if (self->spawnflags & 2)
866: monster_triggered_start (self);
867: }
868:
869:
870: void flymonster_start (edict_t *self)
871: {
872: self->flags |= FL_FLY;
873: self->think = flymonster_start_go;
874: monster_start (self);
875: }
876:
877:
878: void swimmonster_start_go (edict_t *self)
879: {
880: if (!self->yaw_speed)
881: self->yaw_speed = 10;
882: self->viewheight = 10;
883:
884: monster_start_go (self);
885:
886: if (self->spawnflags & 2)
887: monster_triggered_start (self);
888: }
889:
890: void swimmonster_start (edict_t *self)
891: {
892: self->flags |= FL_SWIM;
893: self->think = swimmonster_start_go;
894: monster_start (self);
895: }
896:
897: //ROGUE
898:
899: void stationarymonster_start_go (edict_t *self);
900:
901: void stationarymonster_triggered_spawn (edict_t *self)
902: {
903: KillBox (self);
904:
905: self->solid = SOLID_BBOX;
906: self->movetype = MOVETYPE_NONE;
907: self->svflags &= ~SVF_NOCLIENT;
908: self->air_finished = level.time + 12;
909: gi.linkentity (self);
910:
911: // FIXME - why doesn't this happen with real monsters?
912: self->spawnflags &= ~2;
913:
914: stationarymonster_start_go (self);
915:
916: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
917: {
918: if(!(self->enemy->flags & FL_DISGUISED)) // PGM
919: FoundTarget (self);
920: }
921: else
922: {
923: self->enemy = NULL;
924: }
925: }
926:
927: void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
928: {
929: // we have a one frame delay here so we don't telefrag the guy who activated us
930: self->think = stationarymonster_triggered_spawn;
931: self->nextthink = level.time + FRAMETIME;
932: if (activator->client)
933: self->enemy = activator;
934: self->use = monster_use;
935: }
936:
937: void stationarymonster_triggered_start (edict_t *self)
938: {
939: self->solid = SOLID_NOT;
940: self->movetype = MOVETYPE_NONE;
941: self->svflags |= SVF_NOCLIENT;
942: self->nextthink = 0;
943: self->use = stationarymonster_triggered_spawn_use;
944: }
945:
946: void stationarymonster_start_go (edict_t *self)
947: {
948: if (!M_walkmove (self, 0, 0))
949: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
950:
951: if (!self->yaw_speed)
952: self->yaw_speed = 20;
953: // self->viewheight = 25;
954:
955: monster_start_go (self);
956:
957: if (self->spawnflags & 2)
958: stationarymonster_triggered_start (self);
959: }
960:
961: void stationarymonster_start (edict_t *self)
962: {
963: self->think = stationarymonster_start_go;
964: monster_start (self);
965: }
966:
967: void monster_done_dodge (edict_t *self)
968: {
969: // if ((g_showlogic) && (g_showlogic->value))
970: // gi.dprintf ("%s done dodging\n", self->classname);
971: self->monsterinfo.aiflags &= ~AI_DODGING;
972: }
973: //ROGUE
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.