|
|
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: // RAFAEL
43: void monster_fire_blueblaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
44: {
45: fire_blueblaster (self, start, dir, damage, speed, effect);
46:
47: gi.WriteByte (svc_muzzleflash2);
48: gi.WriteShort (self - g_edicts);
49: gi.WriteByte (MZ_BLUEHYPERBLASTER);
50: gi.multicast (start, MULTICAST_PVS);
51: }
52:
53: // RAFAEL
54: void monster_fire_ionripper (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
55: {
56: fire_ionripper (self, start, dir, damage, speed, effect);
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: // RAFAEL
65: void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
66: {
67: fire_heat (self, start, dir, damage, speed, damage, damage);
68:
69: gi.WriteByte (svc_muzzleflash2);
70: gi.WriteShort (self - g_edicts);
71: gi.WriteByte (flashtype);
72: gi.multicast (start, MULTICAST_PVS);
73: }
74:
75: // RAFAEL
76: void dabeam_hit (edict_t *self)
77: {
78: edict_t *ignore;
79: vec3_t start;
80: vec3_t end;
81: trace_t tr;
82: int count;
83: static vec3_t lmins = {-4, -4, -4};
84: static vec3_t lmaxs = {4, 4, 4};
85:
86: if (self->spawnflags & 0x80000000)
87: count = 8;
88: else
89: count = 4;
90:
91: ignore = self;
92: VectorCopy (self->s.origin, start);
93: VectorMA (start, 2048, self->movedir, end);
94:
95: while(1)
96: {
97: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
98: if (!tr.ent)
99: break;
100:
101: // hurt it if we can
102: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
103: T_Damage (tr.ent, self, self->owner, self->movedir, tr.endpos, vec3_origin, self->dmg, skill->value, DAMAGE_ENERGY, MOD_TARGET_LASER);
104:
105: if (self->dmg < 0) // healer ray
106: {
107: // when player is at 100 health
108: // just undo health fix
109: // keeping fx
110: if (tr.ent->client && tr.ent->health > 100)
111: tr.ent->health += self->dmg;
112: }
113:
114: // if we hit something that's not a monster or player or is immune to lasers, we're done
115: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
116: {
117: if (self->spawnflags & 0x80000000)
118: {
119: self->spawnflags &= ~0x80000000;
120: gi.WriteByte (svc_temp_entity);
121: gi.WriteByte (TE_LASER_SPARKS);
122: gi.WriteByte (10);
123: gi.WritePosition (tr.endpos);
124: gi.WriteDir (tr.plane.normal);
125: gi.WriteByte (self->s.skinnum);
126: gi.multicast (tr.endpos, MULTICAST_PVS);
127: }
128: break;
129: }
130:
131: ignore = tr.ent;
132: VectorCopy (tr.endpos, start);
133: }
134:
135:
136: VectorCopy (tr.endpos, self->s.old_origin);
137: self->nextthink = level.time + 0.1;
138: self->think = G_FreeEdict;
139:
140: }
141:
142: // RAFAEL
143: void monster_dabeam (edict_t *self)
144: {
145: vec3_t last_movedir;
146: vec3_t point;
147:
148: self->movetype = MOVETYPE_NONE;
149: self->solid = SOLID_NOT;
150: self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
151: self->s.modelindex = 1;
152:
153: self->s.frame = 2;
154:
155: if (self->owner->monsterinfo.aiflags & AI_MEDIC)
156: self->s.skinnum = 0xf3f3f1f1;
157: else
158: self->s.skinnum = 0xf2f2f0f0;
159:
160: if (self->enemy)
161: {
162: VectorCopy (self->movedir, last_movedir);
163: VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
164: if (self->owner->monsterinfo.aiflags & AI_MEDIC)
165: point[0] += sin (level.time) * 8;
166: VectorSubtract (point, self->s.origin, self->movedir);
167: VectorNormalize (self->movedir);
168: if (!VectorCompare(self->movedir, last_movedir))
169: self->spawnflags |= 0x80000000;
170: }
171: else
172: G_SetMovedir (self->s.angles, self->movedir);
173:
174: self->think = dabeam_hit;
175: self->nextthink = level.time + 0.1;
176: VectorSet (self->mins, -8, -8, -8);
177: VectorSet (self->maxs, 8, 8, 8);
178: gi.linkentity (self);
179:
180: self->spawnflags |= 0x80000001;
181: self->svflags &= ~SVF_NOCLIENT;
182: }
183:
184: void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
185: {
186: fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
187:
188: gi.WriteByte (svc_muzzleflash2);
189: gi.WriteShort (self - g_edicts);
190: gi.WriteByte (flashtype);
191: gi.multicast (start, MULTICAST_PVS);
192: }
193:
194: void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
195: {
196: fire_rocket (self, start, dir, damage, speed, damage+20, damage);
197:
198: gi.WriteByte (svc_muzzleflash2);
199: gi.WriteShort (self - g_edicts);
200: gi.WriteByte (flashtype);
201: gi.multicast (start, MULTICAST_PVS);
202: }
203:
204: void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
205: {
206: fire_rail (self, start, aimdir, damage, kick);
207:
208: gi.WriteByte (svc_muzzleflash2);
209: gi.WriteShort (self - g_edicts);
210: gi.WriteByte (flashtype);
211: gi.multicast (start, MULTICAST_PVS);
212: }
213:
214: void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
215: {
216: fire_bfg (self, start, aimdir, damage, speed, damage_radius);
217:
218: gi.WriteByte (svc_muzzleflash2);
219: gi.WriteShort (self - g_edicts);
220: gi.WriteByte (flashtype);
221: gi.multicast (start, MULTICAST_PVS);
222: }
223:
224:
225:
226: //
227: // Monster utility functions
228: //
229:
230: static void M_FliesOff (edict_t *self)
231: {
232: self->s.effects &= ~EF_FLIES;
233: self->s.sound = 0;
234: }
235:
236: static void M_FliesOn (edict_t *self)
237: {
238: if (self->waterlevel)
239: return;
240: self->s.effects |= EF_FLIES;
241: self->s.sound = gi.soundindex ("infantry/inflies1.wav");
242: self->think = M_FliesOff;
243: self->nextthink = level.time + 60;
244: }
245:
246: void M_FlyCheck (edict_t *self)
247: {
248: if (self->waterlevel)
249: return;
250:
251: if (random() > 0.5)
252: return;
253:
254: self->think = M_FliesOn;
255: self->nextthink = level.time + 5 + 10 * random();
256: }
257:
258: void AttackFinished (edict_t *self, float time)
259: {
260: self->monsterinfo.attack_finished = level.time + time;
261: }
262:
263:
264: void M_CheckGround (edict_t *ent)
265: {
266: vec3_t point;
267: trace_t trace;
268:
269: if (ent->flags & (FL_SWIM|FL_FLY))
270: return;
271:
272: if (ent->velocity[2] > 100)
273: {
274: ent->groundentity = NULL;
275: return;
276: }
277:
278: // if the hull point one-quarter unit down is solid the entity is on ground
279: point[0] = ent->s.origin[0];
280: point[1] = ent->s.origin[1];
281: point[2] = ent->s.origin[2] - 0.25;
282:
283: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
284:
285: // check steepness
286: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
287: {
288: ent->groundentity = NULL;
289: return;
290: }
291:
292: // ent->groundentity = trace.ent;
293: // ent->groundentity_linkcount = trace.ent->linkcount;
294: // if (!trace.startsolid && !trace.allsolid)
295: // VectorCopy (trace.endpos, ent->s.origin);
296: if (!trace.startsolid && !trace.allsolid)
297: {
298: VectorCopy (trace.endpos, ent->s.origin);
299: ent->groundentity = trace.ent;
300: ent->groundentity_linkcount = trace.ent->linkcount;
301: ent->velocity[2] = 0;
302: }
303: }
304:
305:
306: void M_CatagorizePosition (edict_t *ent)
307: {
308: vec3_t point;
309: int cont;
310:
311: //
312: // get waterlevel
313: //
314: point[0] = ent->s.origin[0];
315: point[1] = ent->s.origin[1];
316: point[2] = ent->s.origin[2] + ent->mins[2] + 1;
317: cont = gi.pointcontents (point);
318:
319: if (!(cont & MASK_WATER))
320: {
321: ent->waterlevel = 0;
322: ent->watertype = 0;
323: return;
324: }
325:
326: ent->watertype = cont;
327: ent->waterlevel = 1;
328: point[2] += 26;
329: cont = gi.pointcontents (point);
330: if (!(cont & MASK_WATER))
331: return;
332:
333: ent->waterlevel = 2;
334: point[2] += 22;
335: cont = gi.pointcontents (point);
336: if (cont & MASK_WATER)
337: ent->waterlevel = 3;
338: }
339:
340:
341: void M_WorldEffects (edict_t *ent)
342: {
343: int dmg;
344:
345: if (ent->health > 0)
346: {
347: if (!(ent->flags & FL_SWIM))
348: {
349: if (ent->waterlevel < 3)
350: {
351: ent->air_finished = level.time + 12;
352: }
353: else if (ent->air_finished < level.time)
354: { // drown!
355: if (ent->pain_debounce_time < level.time)
356: {
357: dmg = 2 + 2 * floor(level.time - ent->air_finished);
358: if (dmg > 15)
359: dmg = 15;
360: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
361: ent->pain_debounce_time = level.time + 1;
362: }
363: }
364: }
365: else
366: {
367: if (ent->waterlevel > 0)
368: {
369: ent->air_finished = level.time + 9;
370: }
371: else if (ent->air_finished < level.time)
372: { // suffocate!
373: if (ent->pain_debounce_time < level.time)
374: {
375: dmg = 2 + 2 * floor(level.time - ent->air_finished);
376: if (dmg > 15)
377: dmg = 15;
378: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
379: ent->pain_debounce_time = level.time + 1;
380: }
381: }
382: }
383: }
384:
385: if (ent->waterlevel == 0)
386: {
387: if (ent->flags & FL_INWATER)
388: {
389: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
390: ent->flags &= ~FL_INWATER;
391: }
392: return;
393: }
394:
395: if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
396: {
397: if (ent->damage_debounce_time < level.time)
398: {
399: ent->damage_debounce_time = level.time + 0.2;
400: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
401: }
402: }
403: if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
404: {
405: if (ent->damage_debounce_time < level.time)
406: {
407: ent->damage_debounce_time = level.time + 1;
408: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
409: }
410: }
411:
412: if ( !(ent->flags & FL_INWATER) )
413: {
414: if (!(ent->svflags & SVF_DEADMONSTER))
415: {
416: if (ent->watertype & CONTENTS_LAVA)
417: if (random() <= 0.5)
418: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
419: else
420: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
421: else if (ent->watertype & CONTENTS_SLIME)
422: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
423: else if (ent->watertype & CONTENTS_WATER)
424: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
425: }
426:
427: ent->flags |= FL_INWATER;
428: ent->damage_debounce_time = 0;
429: }
430: }
431:
432:
433: void M_droptofloor (edict_t *ent)
434: {
435: vec3_t end;
436: trace_t trace;
437:
438: ent->s.origin[2] += 1;
439: VectorCopy (ent->s.origin, end);
440: end[2] -= 256;
441:
442: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
443:
444: if (trace.fraction == 1 || trace.allsolid)
445: return;
446:
447: VectorCopy (trace.endpos, ent->s.origin);
448:
449: gi.linkentity (ent);
450: M_CheckGround (ent);
451: M_CatagorizePosition (ent);
452: }
453:
454:
455: void M_SetEffects (edict_t *ent)
456: {
457: ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN);
458: ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
459:
460: if (ent->monsterinfo.aiflags & AI_RESURRECTING)
461: {
462: ent->s.effects |= EF_COLOR_SHELL;
463: ent->s.renderfx |= RF_SHELL_RED;
464: }
465:
466: if (ent->health <= 0)
467: return;
468:
469: if (ent->powerarmor_time > level.time)
470: {
471: if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
472: {
473: ent->s.effects |= EF_POWERSCREEN;
474: }
475: else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
476: {
477: ent->s.effects |= EF_COLOR_SHELL;
478: ent->s.renderfx |= RF_SHELL_GREEN;
479: }
480: }
481: }
482:
483:
484: void M_MoveFrame (edict_t *self)
485: {
486: mmove_t *move;
487: int index;
488:
489: move = self->monsterinfo.currentmove;
490: self->nextthink = level.time + FRAMETIME;
491:
492: if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
493: {
494: self->s.frame = self->monsterinfo.nextframe;
495: self->monsterinfo.nextframe = 0;
496: }
497: else
498: {
499: if (self->s.frame == move->lastframe)
500: {
501: if (move->endfunc)
502: {
503: move->endfunc (self);
504:
505: // regrab move, endfunc is very likely to change it
506: move = self->monsterinfo.currentmove;
507:
508: // check for death
509: if (self->svflags & SVF_DEADMONSTER)
510: return;
511: }
512: }
513:
514: if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
515: {
516: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
517: self->s.frame = move->firstframe;
518: }
519: else
520: {
521: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
522: {
523: self->s.frame++;
524: if (self->s.frame > move->lastframe)
525: self->s.frame = move->firstframe;
526: }
527: }
528: }
529:
530: index = self->s.frame - move->firstframe;
531: if (move->frame[index].aifunc)
532: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
533: move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
534: else
535: move->frame[index].aifunc (self, 0);
536:
537: if (move->frame[index].thinkfunc)
538: move->frame[index].thinkfunc (self);
539: }
540:
541:
542: void monster_think (edict_t *self)
543: {
544: M_MoveFrame (self);
545: if (self->linkcount != self->monsterinfo.linkcount)
546: {
547: self->monsterinfo.linkcount = self->linkcount;
548: M_CheckGround (self);
549: }
550: M_CatagorizePosition (self);
551: M_WorldEffects (self);
552: M_SetEffects (self);
553: }
554:
555:
556: /*
557: ================
558: monster_use
559:
560: Using a monster makes it angry at the current activator
561: ================
562: */
563: void monster_use (edict_t *self, edict_t *other, edict_t *activator)
564: {
565: if (self->enemy)
566: return;
567: if (self->health <= 0)
568: return;
569: if (activator->flags & FL_NOTARGET)
570: return;
571: if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
572: return;
573:
574: // delay reaction so if the monster is teleported, its sound is still heard
575: self->enemy = activator;
576: FoundTarget (self);
577: }
578:
579:
580: void monster_start_go (edict_t *self);
581:
582:
583: void monster_triggered_spawn (edict_t *self)
584: {
585: self->s.origin[2] += 1;
586: KillBox (self);
587:
588: self->solid = SOLID_BBOX;
589: self->movetype = MOVETYPE_STEP;
590: self->svflags &= ~SVF_NOCLIENT;
591: self->air_finished = level.time + 12;
592: gi.linkentity (self);
593:
594: monster_start_go (self);
595:
596: // RAFAEL
597: if (strcmp (self->classname, "monster_fixbot") == 0)
598: {
599: if (self->spawnflags &16 || self->spawnflags &8 || self->spawnflags &4)
600: {
601: self->enemy = NULL;
602: return;
603: }
604: }
605:
606: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
607: {
608: FoundTarget (self);
609: }
610: else
611: {
612: self->enemy = NULL;
613: }
614: }
615:
616: void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
617: {
618: // we have a one frame delay here so we don't telefrag the guy who activated us
619: self->think = monster_triggered_spawn;
620: self->nextthink = level.time + FRAMETIME;
621: if (activator->client)
622: self->enemy = activator;
623: self->use = monster_use;
624: }
625:
626: void monster_triggered_start (edict_t *self)
627: {
628: self->solid = SOLID_NOT;
629: self->movetype = MOVETYPE_NONE;
630: self->svflags |= SVF_NOCLIENT;
631: self->nextthink = 0;
632: self->use = monster_triggered_spawn_use;
633: }
634:
635:
636: /*
637: ================
638: monster_death_use
639:
640: When a monster dies, it fires all of its targets with the current
641: enemy as activator.
642: ================
643: */
644: void monster_death_use (edict_t *self)
645: {
646: self->flags &= ~(FL_FLY|FL_SWIM);
647: self->monsterinfo.aiflags &= AI_GOOD_GUY;
648:
649: if (self->item)
650: {
651: Drop_Item (self, self->item);
652: self->item = NULL;
653: }
654:
655: if (self->deathtarget)
656: self->target = self->deathtarget;
657:
658: if (!self->target)
659: return;
660:
661: G_UseTargets (self, self->enemy);
662: }
663:
664:
665: //============================================================================
666:
667: qboolean monster_start (edict_t *self)
668: {
669: if (deathmatch->value)
670: {
671: G_FreeEdict (self);
672: return false;
673: }
674:
675: if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
676: {
677: self->spawnflags &= ~4;
678: self->spawnflags |= 1;
679: // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
680: }
681:
682: if (!(self->monsterinfo.aiflags & AI_GOOD_GUY))
683: level.total_monsters++;
684:
685: self->nextthink = level.time + FRAMETIME;
686: self->svflags |= SVF_MONSTER;
687: self->s.renderfx |= RF_FRAMELERP;
688: self->takedamage = DAMAGE_AIM;
689: self->air_finished = level.time + 12;
690: self->use = monster_use;
691: self->max_health = self->health;
692: self->clipmask = MASK_MONSTERSOLID;
693:
694: self->s.skinnum = 0;
695: self->deadflag = DEAD_NO;
696: self->svflags &= ~SVF_DEADMONSTER;
697:
698: if (!self->monsterinfo.checkattack)
699: self->monsterinfo.checkattack = M_CheckAttack;
700: VectorCopy (self->s.origin, self->s.old_origin);
701:
702: if (st.item)
703: {
704: self->item = FindItemByClassname (st.item);
705: if (!self->item)
706: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
707: }
708:
709: // randomize what frame they start on
710: if (self->monsterinfo.currentmove)
711: self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
712:
713: return true;
714: }
715:
716: void monster_start_go (edict_t *self)
717: {
718: vec3_t v;
719:
720: if (self->health <= 0)
721: return;
722:
723: // check for target to combat_point and change to combattarget
724: if (self->target)
725: {
726: qboolean notcombat;
727: qboolean fixup;
728: edict_t *target;
729:
730: target = NULL;
731: notcombat = false;
732: fixup = false;
733: while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
734: {
735: if (strcmp(target->classname, "point_combat") == 0)
736: {
737: self->combattarget = self->target;
738: fixup = true;
739: }
740: else
741: {
742: notcombat = true;
743: }
744: }
745: if (notcombat && self->combattarget)
746: gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
747: if (fixup)
748: self->target = NULL;
749: }
750:
751: // validate combattarget
752: if (self->combattarget)
753: {
754: edict_t *target;
755:
756: target = NULL;
757: while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
758: {
759: if (strcmp(target->classname, "point_combat") != 0)
760: {
761: gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
762: self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
763: self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
764: (int)target->s.origin[2]);
765: }
766: }
767: }
768:
769: if (self->target)
770: {
771: self->goalentity = self->movetarget = G_PickTarget(self->target);
772: if (!self->movetarget)
773: {
774: gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
775: self->target = NULL;
776: self->monsterinfo.pausetime = 100000000;
777: self->monsterinfo.stand (self);
778: }
779: else if (strcmp (self->movetarget->classname, "path_corner") == 0)
780: {
781: VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
782: self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
783: self->monsterinfo.walk (self);
784: self->target = NULL;
785: }
786: else
787: {
788: self->goalentity = self->movetarget = NULL;
789: self->monsterinfo.pausetime = 100000000;
790: self->monsterinfo.stand (self);
791: }
792: }
793: else
794: {
795: self->monsterinfo.pausetime = 100000000;
796: self->monsterinfo.stand (self);
797: }
798:
799: self->think = monster_think;
800: self->nextthink = level.time + FRAMETIME;
801: }
802:
803:
804: void walkmonster_start_go (edict_t *self)
805: {
806: if (!(self->spawnflags & 2) && level.time < 1)
807: {
808: M_droptofloor (self);
809:
810: if (self->groundentity)
811: if (!M_walkmove (self, 0, 0))
812: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
813: }
814:
815: if (!self->yaw_speed)
816: self->yaw_speed = 20;
817: self->viewheight = 25;
818:
819: monster_start_go (self);
820:
821: if (self->spawnflags & 2)
822: monster_triggered_start (self);
823: }
824:
825: void walkmonster_start (edict_t *self)
826: {
827: self->think = walkmonster_start_go;
828: monster_start (self);
829: }
830:
831:
832: void flymonster_start_go (edict_t *self)
833: {
834: if (!M_walkmove (self, 0, 0))
835: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
836:
837: if (!self->yaw_speed)
838: self->yaw_speed = 10;
839: self->viewheight = 25;
840:
841: monster_start_go (self);
842:
843: if (self->spawnflags & 2)
844: monster_triggered_start (self);
845: }
846:
847:
848: void flymonster_start (edict_t *self)
849: {
850: self->flags |= FL_FLY;
851: self->think = flymonster_start_go;
852: monster_start (self);
853: }
854:
855:
856: void swimmonster_start_go (edict_t *self)
857: {
858: if (!self->yaw_speed)
859: self->yaw_speed = 10;
860: self->viewheight = 10;
861:
862: monster_start_go (self);
863:
864: if (self->spawnflags & 2)
865: monster_triggered_start (self);
866: }
867:
868: void swimmonster_start (edict_t *self)
869: {
870: self->flags |= FL_SWIM;
871: self->think = swimmonster_start_go;
872: monster_start (self);
873: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.