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.
monster_fire_bullet(edict_t * self,vec3_t start,vec3_t dir,int damage,int kick,int hspread,int vspread,int flashtype)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
monster_fire_shotgun(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int hspread,int vspread,int count,int flashtype)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
monster_fire_blaster(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int flashtype,int effect)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
monster_fire_blaster2(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int flashtype,int effect)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
monster_fire_tracker(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,edict_t * enemy,int flashtype)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
monster_fire_heat(edict_t * self,vec3_t start,vec3_t dir,vec3_t offset,int damage,int kick,int flashtype)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
monster_fire_grenade(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,int flashtype)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
monster_fire_rocket(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int flashtype)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
monster_fire_railgun(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick,int flashtype)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
monster_fire_bfg(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int speed,int kick,float damage_radius,int flashtype)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
M_FliesOff(edict_t * self)123 void M_FliesOff (edict_t *self)
124 {
125 self->s.effects &= ~EF_FLIES;
126 self->s.sound = 0;
127 }
128
M_FliesOn(edict_t * self)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
M_FlyCheck(edict_t * self)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
AttackFinished(edict_t * self,float time)151 void AttackFinished (edict_t *self, float time)
152 {
153 self->monsterinfo.attack_finished = level.time + time;
154 }
155
156
M_CheckGround(edict_t * ent)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
M_CatagorizePosition(edict_t * ent)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
M_WorldEffects(edict_t * ent)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
M_droptofloor(edict_t * ent)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
M_SetEffects(edict_t * ent)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
M_MoveFrame(edict_t * self)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
monster_think(edict_t * self)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 */
monster_use(edict_t * self,edict_t * other,edict_t * activator)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
monster_triggered_spawn(edict_t * self)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 else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
620 self->enemy = NULL;
621 }
622 else
623 {
624 self->enemy = NULL;
625 }
626 }
627
monster_triggered_spawn_use(edict_t * self,edict_t * other,edict_t * activator)628 void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
629 {
630 // we have a one frame delay here so we don't telefrag the guy who activated us
631 self->think = monster_triggered_spawn;
632 self->nextthink = level.time + FRAMETIME;
633 if (activator->client)
634 self->enemy = activator;
635 self->use = monster_use;
636 }
637
monster_triggered_start(edict_t * self)638 void monster_triggered_start (edict_t *self)
639 {
640 self->solid = SOLID_NOT;
641 self->movetype = MOVETYPE_NONE;
642 self->svflags |= SVF_NOCLIENT;
643 self->nextthink = 0;
644 self->use = monster_triggered_spawn_use;
645 }
646
647
648 /*
649 ================
650 monster_death_use
651
652 When a monster dies, it fires all of its targets with the current
653 enemy as activator.
654 ================
655 */
monster_death_use(edict_t * self)656 void monster_death_use (edict_t *self)
657 {
658 self->flags &= ~(FL_FLY|FL_SWIM);
659 self->monsterinfo.aiflags &= AI_GOOD_GUY;
660
661 if (self->item)
662 {
663 Drop_Item (self, self->item);
664 self->item = NULL;
665 }
666
667 if (self->deathtarget)
668 self->target = self->deathtarget;
669
670 if (!self->target)
671 return;
672
673 G_UseTargets (self, self->enemy);
674 }
675
676
677 //============================================================================
678
monster_start(edict_t * self)679 qboolean monster_start (edict_t *self)
680 {
681 if (deathmatch->value)
682 {
683 G_FreeEdict (self);
684 return false;
685 }
686
687 if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
688 {
689 self->spawnflags &= ~4;
690 self->spawnflags |= 1;
691 // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
692 }
693
694 if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
695 level.total_monsters++;
696
697 self->nextthink = level.time + FRAMETIME;
698 self->svflags |= SVF_MONSTER;
699 self->s.renderfx |= RF_FRAMELERP;
700 self->takedamage = DAMAGE_AIM;
701 self->air_finished = level.time + 12;
702 self->use = monster_use;
703 self->max_health = self->health;
704 self->clipmask = MASK_MONSTERSOLID;
705
706 self->s.skinnum = 0;
707 self->deadflag = DEAD_NO;
708 self->svflags &= ~SVF_DEADMONSTER;
709
710 if (!self->monsterinfo.checkattack)
711 self->monsterinfo.checkattack = M_CheckAttack;
712 VectorCopy (self->s.origin, self->s.old_origin);
713
714 if (st.item)
715 {
716 self->item = FindItemByClassname (st.item);
717 if (!self->item)
718 gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
719 }
720
721 // randomize what frame they start on
722 if (self->monsterinfo.currentmove)
723 self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
724
725 // PMM - get this so I don't have to do it in all of the monsters
726 self->monsterinfo.base_height = self->maxs[2];
727
728 // PMM - clear these
729 self->monsterinfo.quad_framenum = 0;
730 self->monsterinfo.double_framenum = 0;
731 self->monsterinfo.invincible_framenum = 0;
732
733 return true;
734 }
735
monster_start_go(edict_t * self)736 void monster_start_go (edict_t *self)
737 {
738 vec3_t v;
739
740 if (self->health <= 0)
741 return;
742
743 // check for target to combat_point and change to combattarget
744 if (self->target)
745 {
746 qboolean notcombat;
747 qboolean fixup;
748 edict_t *target;
749
750 target = NULL;
751 notcombat = false;
752 fixup = false;
753 while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
754 {
755 if (strcmp(target->classname, "point_combat") == 0)
756 {
757 self->combattarget = self->target;
758 fixup = true;
759 }
760 else
761 {
762 notcombat = true;
763 }
764 }
765 if (notcombat && self->combattarget)
766 gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
767 if (fixup)
768 self->target = NULL;
769 }
770
771 // validate combattarget
772 if (self->combattarget)
773 {
774 edict_t *target;
775
776 target = NULL;
777 while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
778 {
779 if (strcmp(target->classname, "point_combat") != 0)
780 {
781 gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
782 self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
783 self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
784 (int)target->s.origin[2]);
785 }
786 }
787 }
788
789 if (self->target)
790 {
791 self->goalentity = self->movetarget = G_PickTarget(self->target);
792 if (!self->movetarget)
793 {
794 gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
795 self->target = NULL;
796 self->monsterinfo.pausetime = 100000000;
797 self->monsterinfo.stand (self);
798 }
799 else if (strcmp (self->movetarget->classname, "path_corner") == 0)
800 {
801 VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
802 self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
803 self->monsterinfo.walk (self);
804 self->target = NULL;
805 }
806 else
807 {
808 self->goalentity = self->movetarget = NULL;
809 self->monsterinfo.pausetime = 100000000;
810 self->monsterinfo.stand (self);
811 }
812 }
813 else
814 {
815 self->monsterinfo.pausetime = 100000000;
816 self->monsterinfo.stand (self);
817 }
818
819 self->think = monster_think;
820 self->nextthink = level.time + FRAMETIME;
821 }
822
823
walkmonster_start_go(edict_t * self)824 void walkmonster_start_go (edict_t *self)
825 {
826 if (!(self->spawnflags & 2) && level.time < 1)
827 {
828 M_droptofloor (self);
829
830 if (self->groundentity)
831 if (!M_walkmove (self, 0, 0))
832 gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
833 }
834
835 if (!self->yaw_speed)
836 self->yaw_speed = 20;
837 // PMM - stalkers are too short for this
838 if (!(strcmp(self->classname, "monster_stalker")))
839 self->viewheight = 15;
840 else
841 self->viewheight = 25;
842
843 monster_start_go (self);
844
845 if (self->spawnflags & 2)
846 monster_triggered_start (self);
847 }
848
walkmonster_start(edict_t * self)849 void walkmonster_start (edict_t *self)
850 {
851 self->think = walkmonster_start_go;
852 monster_start (self);
853 }
854
855
flymonster_start_go(edict_t * self)856 void flymonster_start_go (edict_t *self)
857 {
858 if (!M_walkmove (self, 0, 0))
859 gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
860
861 if (!self->yaw_speed)
862 self->yaw_speed = 10;
863 self->viewheight = 25;
864
865 monster_start_go (self);
866
867 if (self->spawnflags & 2)
868 monster_triggered_start (self);
869 }
870
871
flymonster_start(edict_t * self)872 void flymonster_start (edict_t *self)
873 {
874 self->flags |= FL_FLY;
875 self->think = flymonster_start_go;
876 monster_start (self);
877 }
878
879
swimmonster_start_go(edict_t * self)880 void swimmonster_start_go (edict_t *self)
881 {
882 if (!self->yaw_speed)
883 self->yaw_speed = 10;
884 self->viewheight = 10;
885
886 monster_start_go (self);
887
888 if (self->spawnflags & 2)
889 monster_triggered_start (self);
890 }
891
swimmonster_start(edict_t * self)892 void swimmonster_start (edict_t *self)
893 {
894 self->flags |= FL_SWIM;
895 self->think = swimmonster_start_go;
896 monster_start (self);
897 }
898
899 //ROGUE
900
901 void stationarymonster_start_go (edict_t *self);
902
stationarymonster_triggered_spawn(edict_t * self)903 void stationarymonster_triggered_spawn (edict_t *self)
904 {
905 KillBox (self);
906
907 self->solid = SOLID_BBOX;
908 self->movetype = MOVETYPE_NONE;
909 self->svflags &= ~SVF_NOCLIENT;
910 self->air_finished = level.time + 12;
911 gi.linkentity (self);
912
913 // FIXME - why doesn't this happen with real monsters?
914 self->spawnflags &= ~2;
915
916 stationarymonster_start_go (self);
917
918 if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
919 {
920 if(!(self->enemy->flags & FL_DISGUISED)) // PGM
921 FoundTarget (self);
922 else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
923 self->enemy = NULL;
924 }
925 else
926 {
927 self->enemy = NULL;
928 }
929 }
930
stationarymonster_triggered_spawn_use(edict_t * self,edict_t * other,edict_t * activator)931 void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
932 {
933 // we have a one frame delay here so we don't telefrag the guy who activated us
934 self->think = stationarymonster_triggered_spawn;
935 self->nextthink = level.time + FRAMETIME;
936 if (activator->client)
937 self->enemy = activator;
938 self->use = monster_use;
939 }
940
stationarymonster_triggered_start(edict_t * self)941 void stationarymonster_triggered_start (edict_t *self)
942 {
943 self->solid = SOLID_NOT;
944 self->movetype = MOVETYPE_NONE;
945 self->svflags |= SVF_NOCLIENT;
946 self->nextthink = 0;
947 self->use = stationarymonster_triggered_spawn_use;
948 }
949
stationarymonster_start_go(edict_t * self)950 void stationarymonster_start_go (edict_t *self)
951 {
952 // PGM - only turrets use this, so remove the error message. They're supposed to be in solid.
953
954 // if (!M_walkmove (self, 0, 0))
955 // gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
956
957 if (!self->yaw_speed)
958 self->yaw_speed = 20;
959 // self->viewheight = 25;
960
961 monster_start_go (self);
962
963 if (self->spawnflags & 2)
964 stationarymonster_triggered_start (self);
965 }
966
stationarymonster_start(edict_t * self)967 void stationarymonster_start (edict_t *self)
968 {
969 self->think = stationarymonster_start_go;
970 monster_start (self);
971 }
972
monster_done_dodge(edict_t * self)973 void monster_done_dodge (edict_t *self)
974 {
975 // if ((g_showlogic) && (g_showlogic->value))
976 // gi.dprintf ("%s done dodging\n", self->classname);
977 self->monsterinfo.aiflags &= ~AI_DODGING;
978 }
979 //ROGUE
980