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