1 /**
2  * @file
3  * @brief Slow projectiles, done as monsters.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-project.h"
9 
10 #include <cmath>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 
15 #include "act-iter.h"
16 #include "areas.h"
17 #include "cloud.h"
18 #include "directn.h"
19 #include "env.h"
20 #include "item-prop.h"
21 #include "message.h"
22 #include "mgen-data.h"
23 #include "mon-death.h"
24 #include "mon-place.h"
25 #include "ouch.h"
26 #include "shout.h"
27 #include "stepdown.h"
28 #include "terrain.h"
29 #include "viewchar.h"
30 
31 static void _fuzz_direction(const actor *caster, monster& mon, int pow);
32 
cast_iood(actor * caster,int pow,bolt * beam,float vx,float vy,int foe,bool fail,bool needs_tracer)33 spret cast_iood(actor *caster, int pow, bolt *beam, float vx, float vy,
34                      int foe, bool fail, bool needs_tracer)
35 {
36     const bool is_player = caster->is_player();
37     if (beam && is_player && needs_tracer
38              && !player_tracer(ZAP_IOOD, pow, *beam))
39     {
40         return spret::abort;
41     }
42 
43     fail_check();
44 
45     int mtarg = !beam ? MHITNOT :
46                 beam->target == you.pos() ? int{MHITYOU} : env.mgrid(beam->target);
47 
48     monster *mon = place_monster(mgen_data(MONS_ORB_OF_DESTRUCTION,
49                 (is_player) ? BEH_FRIENDLY :
50                     ((monster*)caster)->friendly() ? BEH_FRIENDLY : BEH_HOSTILE,
51                 coord_def(),
52                 mtarg).set_summoned(caster, 0, SPELL_IOOD), true, true);
53     if (!mon)
54     {
55         mprf(MSGCH_ERROR, "Failed to spawn projectile.");
56         return spret::abort;
57     }
58 
59     if (beam)
60     {
61         beam->choose_ray();
62 #ifdef DEBUG_DIAGNOSTICS
63         const coord_def pos = caster->pos();
64         dprf("beam (%d,%d)+t*(%d,%d)  ray (%f,%f)+t*(%f,%f)",
65             pos.x, pos.y, beam->target.x - pos.x, beam->target.y - pos.y,
66             beam->ray.r.start.x - 0.5, beam->ray.r.start.y - 0.5,
67             beam->ray.r.dir.x, beam->ray.r.dir.y);
68 #endif
69         mon->props[IOOD_X].get_float() = beam->ray.r.start.x - 0.5;
70         mon->props[IOOD_Y].get_float() = beam->ray.r.start.y - 0.5;
71         mon->props[IOOD_VX].get_float() = beam->ray.r.dir.x;
72         mon->props[IOOD_VY].get_float() = beam->ray.r.dir.y;
73         _fuzz_direction(caster, *mon, pow);
74     }
75     else
76     {
77         // Multi-orb: spread the orbs a bit, otherwise diagonal ones might
78         // fail to leave the cardinal direction: orb A moves -0.4,+0.9 and
79         // orb B +0.4,+0.9, both rounded to 0,1.
80         mon->props[IOOD_X].get_float() = caster->pos().x + 0.4 * vx;
81         mon->props[IOOD_Y].get_float() = caster->pos().y + 0.4 * vy;
82         mon->props[IOOD_VX].get_float() = vx;
83         mon->props[IOOD_VY].get_float() = vy;
84     }
85 
86     mon->props[IOOD_KC].get_byte() = (is_player) ? KC_YOU :
87         ((monster*)caster)->friendly() ? KC_FRIENDLY : KC_OTHER;
88     mon->props[IOOD_POW].get_short() = pow;
89     mon->flags &= ~MF_JUST_SUMMONED;
90     mon->props[IOOD_CASTER].get_string() = caster->as_monster()
91         ? caster->name(DESC_A, true)
92         : (caster->is_player()) ? "you" : "";
93     mon->summoner = caster->mid;
94 
95     if (caster->is_player() || caster->type == MONS_PLAYER_GHOST
96         || caster->type == MONS_PLAYER_ILLUSION)
97     {
98         mon->props[IOOD_FLAWED].get_byte() = true;
99     }
100 
101     // Move away from the caster's square.
102     iood_act(*mon, true);
103 
104     // If the foe was adjacent to the caster, that might have destroyed it.
105     if (mon->alive())
106     {
107         // We need to take at least one full move (for the above), but let's
108         // randomize it and take more so players won't get guaranteed instant
109         // damage.
110         mon->lose_energy(EUT_MOVE, 2, random2(3)+2);
111 
112         // Multi-orbs don't home during the first move, they'd likely
113         // immediately explode otherwise.
114         if (foe != MHITNOT)
115             mon->foe = foe;
116     }
117 
118     return spret::success;
119 }
120 
121 /**
122  * Find a target for a bursty (non-player-targeted) IOOD.
123  *
124  * Try to find an enemy that's at a reasonable angle from the angle the IOOD
125  * is fired at, preferring the given foe (if a non-MHITNOT foe is given) if
126  * they're valid, and otherwise preferring the closest valid foe.
127  *
128  * @param angle             The angle that the IOOD will be fired at, relative
129  *                          to the player's position.
130  * @param preferred_foe     The mindex of a target to choose if possible; may
131  *                          be MHITNOT (no preferred target)
132  * @return                  The mindex of a valid target for the IOOD.
133  */
_burst_iood_target(double iood_angle,int preferred_foe)134 static int _burst_iood_target(double iood_angle, int preferred_foe)
135 {
136     int closest_foe = MHITNOT;
137     int closest_dist = INT_MAX;
138 
139     for (monster_near_iterator mi(you.pos(), LOS_SOLID); mi; ++mi)
140     {
141         const monster* m = *mi;
142         ASSERT(m);
143 
144         if (!you.can_see(*m) || mons_is_projectile(*m))
145             continue;
146 
147         // is this position at a valid angle?
148         const coord_def delta = mi->pos() - you.pos();
149         const double angle = atan2(delta.x, delta.y);
150         const double abs_angle_diff = abs(angle - fmod(iood_angle, PI * 2));
151         const double angle_diff = (abs_angle_diff > PI) ?
152                                         2 * PI - abs_angle_diff :
153                                         abs_angle_diff;
154         if (angle_diff >= PI / 3)
155         {
156             dprf("can't target %s; angle diff %f",
157                  m->name(DESC_PLAIN).c_str(), angle_diff);
158             continue;
159         }
160 
161         // if preferred foe is valid, choose it.
162         if (m->mindex() == preferred_foe)
163         {
164             dprf("preferred target %s is valid burst target (delta %f)",
165                  m->name(DESC_PLAIN).c_str(), angle_diff);
166             return preferred_foe;
167         }
168 
169         if (mons_aligned(m, &you) || mons_is_firewood(*m))
170         {
171             dprf("skipping invalid burst target %s (%s)",
172                  m->name(DESC_PLAIN).c_str(),
173                  mons_aligned(m, &you) ? "aligned" : "firewood");
174             continue;
175         }
176 
177         const int dist = grid_distance(m->pos(), you.pos());
178         // on distance ties, bias by iterator order (mindex)
179         if (dist >= closest_dist)
180         {
181             dprf("%s not closer to target than closest (%d vs %d)",
182                  m->name(DESC_PLAIN).c_str(), dist, closest_dist);
183             continue;
184         }
185 
186         dprf("%s is valid burst target (delta %f, dist %d)",
187              m->name(DESC_PLAIN).c_str(), angle_diff, dist);
188         closest_dist = dist;
189         closest_foe = m->mindex();
190     }
191 
192     const int foe = closest_foe != MHITNOT ? closest_foe : preferred_foe;
193     dprf("targeting %d", foe);
194     return foe;
195 }
196 
cast_iood_burst(int pow,coord_def target)197 void cast_iood_burst(int pow, coord_def target)
198 {
199     const monster* mons = monster_at(target);
200     const int preferred_foe = mons && you.can_see(*mons) ?
201                               mons->mindex() :
202                               MHITNOT;
203 
204     const int n_orbs = random_range(3, 7);
205     dprf("Bursting %d orbs.", n_orbs);
206     const double angle0 = random2(2097152) * PI * 2 / 2097152;
207 
208     for (int i = 0; i < n_orbs; i++)
209     {
210         const double angle = angle0 + i * PI * 2 / n_orbs;
211         const int foe = _burst_iood_target(angle, preferred_foe);
212         cast_iood(&you, pow, 0, sin(angle), cos(angle), foe);
213     }
214 }
215 
_normalize(float & x,float & y)216 static void _normalize(float &x, float &y)
217 {
218     const float d = sqrt(x*x + y*y);
219     if (d <= 0.000001)
220         return;
221     x/=d;
222     y/=d;
223 }
224 
225 // angle measured in chord length
_in_front(float vx,float vy,float dx,float dy,float angle)226 static bool _in_front(float vx, float vy, float dx, float dy, float angle)
227 {
228     return (dx-vx)*(dx-vx) + (dy-vy)*(dy-vy) <= (angle*angle);
229 }
230 
_iood_stop(monster & mon,bool msg=true)231 static void _iood_stop(monster& mon, bool msg = true)
232 {
233     if (!mon.alive())
234         return;
235 
236     if (msg)
237         simple_monster_message(mon, " dissipates.");
238     dprf("iood: dissipating");
239     monster_die(mon, KILL_DISMISSED, NON_MONSTER);
240 }
241 
_fuzz_direction(const actor * caster,monster & mon,int pow)242 static void _fuzz_direction(const actor *caster, monster& mon, int pow)
243 {
244     const float x = mon.props[IOOD_X];
245     const float y = mon.props[IOOD_Y];
246     float vx = mon.props[IOOD_VX];
247     float vy = mon.props[IOOD_VY];
248 
249     _normalize(vx, vy);
250 
251     if (pow < 10)
252         pow = 10;
253     const float off = random_choose(-0.25, 0.25);
254     float tan = (random2(31) - 15) * 0.019; // approx from degrees
255     tan *= 75.0 / pow;
256     const int inaccuracy = caster ? caster->inaccuracy() : 0;
257     if (inaccuracy > 0)
258         tan *= 2 * inaccuracy;
259 
260     // Cast either from left or right hand.
261     mon.props[IOOD_X] = x + vy*off;
262     mon.props[IOOD_Y] = y - vx*off;
263     // And off the direction a bit.
264     mon.props[IOOD_VX] = vx + vy*tan;
265     mon.props[IOOD_VY] = vy - vx*tan;
266 }
267 
268 // Alas, too much differs to reuse beam shield blocks :(
_iood_shielded(monster & mon,actor & victim)269 static bool _iood_shielded(monster& mon, actor &victim)
270 {
271     if (!victim.shielded() || victim.incapacitated())
272         return false;
273 
274     const int to_hit = 15 + (mons_is_projectile(mon.type) ?
275         mon.props[IOOD_POW].get_short()/12 : mon.get_hit_dice()/2);
276     const int con_block = random2(to_hit + victim.shield_block_penalty());
277     const int pro_block = victim.shield_bonus();
278     dprf("iood shield: pro %d, con %d", pro_block, con_block);
279     return pro_block >= con_block;
280 }
281 
iood_damage(int pow,int dist)282 dice_def iood_damage(int pow, int dist)
283 {
284     pow = stepdown_value(pow, 30, 30, 200, -1);
285     if (dist < 4)
286         pow = pow * (dist*2+3) / 10;
287     return dice_def(9, pow / 4);
288 }
289 
_iood_hit(monster & mon,const coord_def & pos,bool big_boom=false)290 static bool _iood_hit(monster& mon, const coord_def &pos, bool big_boom = false)
291 {
292     bolt beam;
293     beam.name = "orb of destruction";
294     beam.flavour = BEAM_DEVASTATION;
295     beam.attitude = mon.attitude;
296 
297     actor *caster = actor_by_mid(mon.summoner);
298     if (!caster)        // caster is dead/gone, blame the orb itself (as its
299         caster = &mon;  // friendliness is correct)
300     beam.set_agent(caster);
301     if (mon.props.exists(IOOD_REFLECTOR))
302     {
303         beam.reflections = 1;
304 
305         const mid_t refl_mid = mon.props[IOOD_REFLECTOR].get_int64();
306 
307         if (refl_mid == MID_PLAYER)
308             beam.reflector = MID_PLAYER;
309         else
310         {
311             // If the reflecting monster has died, credit the original caster.
312             const monster * const rmon = monster_by_mid(refl_mid);
313             beam.reflector = rmon ? refl_mid : caster->mid;
314         }
315     }
316     beam.colour = WHITE;
317     beam.glyph = dchar_glyph(DCHAR_FIRED_BURST);
318     beam.range = 1;
319     beam.source = pos;
320     beam.target = pos;
321     beam.hit = AUTOMATIC_HIT;
322     beam.source_name = mon.props[IOOD_CASTER].get_string();
323     beam.origin_spell = SPELL_IOOD;
324 
325     const int pow = mon.props[IOOD_POW].get_short();
326     const int dist = mon.props[IOOD_DIST].get_int();
327     ASSERT(dist >= 0);
328     beam.damage = iood_damage(pow, dist);
329 
330     if (dist < 3)
331         beam.name = "wavering " + beam.name;
332     if (dist < 2)
333         beam.hit_verb = "weakly hits";
334     beam.ex_size = 1;
335     beam.loudness = 7;
336 
337     monster_die(mon, KILL_DISMISSED, NON_MONSTER);
338 
339     if (big_boom)
340         beam.explode(true, false);
341     else
342         beam.fire();
343 
344     return true;
345 }
346 
347 // returns true if the orb is gone
iood_act(monster & mon,bool no_trail)348 bool iood_act(monster& mon, bool no_trail)
349 {
350     ASSERT(mons_is_projectile(mon.type));
351 
352     float x = mon.props[IOOD_X];
353     float y = mon.props[IOOD_Y];
354     float vx = mon.props[IOOD_VX];
355     float vy = mon.props[IOOD_VY];
356 
357     dprf("iood_act: pos=(%d,%d) rpos=(%f,%f) v=(%f,%f) foe=%d",
358          mon.pos().x, mon.pos().y,
359          x, y, vx, vy, mon.foe);
360 
361     if (!vx && !vy) // not initialized
362     {
363         _iood_stop(mon);
364         return true;
365     }
366 
367     _normalize(vx, vy);
368 
369     const actor *foe = mon.get_foe();
370     // If the target is gone, the orb continues on a ballistic course since
371     // picking a new one would require intelligence.
372 
373     // IOODs can't home in on a submerged creature.
374     if (foe && !foe->submerged())
375     {
376         const coord_def target = foe->pos();
377         float dx = target.x - x;
378         float dy = target.y - y;
379         _normalize(dx, dy);
380 
381         // Special case:
382         // Moving diagonally when the orb is just about to hit you
383         //      2
384         //    ->*1
385         // (from 1 to 2) would be a guaranteed escape. This may be
386         // realistic (strafing!), but since the game has no non-cheesy
387         // means of waiting a small fraction of a turn, we don't want it.
388         const int old_t_pos = mon.props[IOOD_TPOS].get_short();
389         const coord_def rpos(static_cast<int>(round(x)), static_cast<int>(round(y)));
390         if (old_t_pos && old_t_pos != (256 * target.x + target.y)
391             && (rpos - target).rdist() <= 1
392             // ... but following an orb is ok.
393             && _in_front(vx, vy, dx, dy, 1.5)) // ~97 degrees
394         {
395             vx = dx;
396             vy = dy;
397         }
398         mon.props[IOOD_TPOS].get_short() = 256 * target.x + target.y;
399 
400         if (!_in_front(vx, vy, dx, dy, 0.3)) // ~17 degrees
401         {
402             float ax, ay;
403             if (dy*vx < dx*vy)
404                 ax = vy, ay = -vx, dprf("iood: veering left");
405             else
406                 ax = -vy, ay = vx, dprf("iood: veering right");
407             vx += ax * 0.3;
408             vy += ay * 0.3;
409         }
410         else
411             dprf("iood: keeping course");
412 
413         _normalize(vx, vy);
414         mon.props[IOOD_VX] = vx;
415         mon.props[IOOD_VY] = vy;
416     }
417 
418 move_again:
419     coord_def starting_pos = (mon.pos() == coord_def()) ?
420                                                 coord_def(x, y) : mon.pos();
421 
422     x += vx;
423     y += vy;
424 
425     mon.props[IOOD_X] = x;
426     mon.props[IOOD_Y] = y;
427     mon.props[IOOD_DIST].get_int()++;
428 
429     const coord_def pos(static_cast<int>(round(x)), static_cast<int>(round(y)));
430     if (!in_bounds(pos))
431     {
432         _iood_stop(mon);
433         return true;
434     }
435 
436     if (mon.props.exists(IOOD_FLAWED))
437     {
438         const actor *caster = actor_by_mid(mon.summoner);
439         if (!caster || caster->pos().origin() ||
440             (caster->pos() - pos).rdist() > LOS_RADIUS)
441         {   // not actual vision, because of the smoke trail
442             _iood_stop(mon);
443             return true;
444         }
445     }
446 
447     if (pos == mon.pos())
448         return false;
449 
450     if (!no_trail)
451         place_cloud(CLOUD_MAGIC_TRAIL, starting_pos, 2 + random2(3), &mon);
452 
453     actor *victim = actor_at(pos);
454     if (cell_is_solid(pos) || victim)
455     {
456         if (cell_is_solid(pos)
457             && you.see_cell(pos)
458             && you.see_cell(starting_pos))
459         {
460             mprf("%s hits %s.", mon.name(DESC_THE, true).c_str(),
461                  feature_description_at(pos, false, DESC_A).c_str());
462         }
463 
464         monster* mons = (victim && victim->is_monster()) ?
465             (monster*) victim : 0;
466 
467         if (mons && mons_is_projectile(victim->type))
468         {
469             // Weak orbs just fizzle instead of exploding.
470             if (mons->props[IOOD_DIST].get_int() < 2
471                 || mon.props[IOOD_DIST].get_int() < 2)
472             {
473                 if (mons->props[IOOD_DIST].get_int() < 2)
474                 {
475                     if (you.see_cell(pos))
476                         mpr("The orb fizzles.");
477                     monster_die(*mons, KILL_DISMISSED, NON_MONSTER);
478                 }
479 
480                 // Return, if the acting orb fizzled.
481                 if (mon.props[IOOD_DIST].get_int() < 2)
482                 {
483                     if (you.see_cell(pos))
484                         mpr("The orb fizzles.");
485                     monster_die(mon, KILL_DISMISSED, NON_MONSTER);
486                     return true;
487                 }
488             }
489             else
490             {
491                 if (mon.observable())
492                     mpr("The orbs collide in a blinding explosion!");
493                 else
494                     mpr("You hear a loud magical explosion!");
495                 noisy(40, pos);
496                 monster_die(*mons, KILL_DISMISSED, NON_MONSTER);
497                 _iood_hit(mon, pos, true);
498                 return true;
499             }
500         }
501 
502         if (mons && (mons->submerged() || mons->type == MONS_BATTLESPHERE))
503         {
504             // Try to swap with the submerged creature.
505             if (mon.swap_with(mons))
506             {
507                 dprf("iood: Swapping with a submerged monster.");
508                 return false;
509             }
510             else // if swap fails, move ahead
511             {
512                 dprf("iood: Boosting above a submerged monster (can't swap).");
513                 mon.lose_energy(EUT_MOVE);
514                 goto move_again;
515             }
516         }
517 
518         if (victim && _iood_shielded(mon, *victim))
519         {
520             item_def *shield = victim->shield();
521             if ((!shield || !shield_reflects(*shield)) && !victim->reflection())
522             {
523                 if (victim->is_player())
524                     mprf("You block %s.", mon.name(DESC_THE, true).c_str());
525                 else
526                 {
527                     simple_monster_message(*mons, (" blocks "
528                         + mon.name(DESC_THE, true) + ".").c_str());
529                 }
530                 victim->shield_block_succeeded();
531                 _iood_stop(mon);
532                 return true;
533             }
534 
535             if (victim->is_player())
536             {
537                 if (shield && shield_reflects(*shield))
538                 {
539                     mprf("Your %s reflects %s!",
540                          shield->name(DESC_PLAIN).c_str(),
541                          mon.name(DESC_THE, true).c_str());
542                     ident_reflector(shield);
543                 }
544                 else // has reflection property not from shield
545                 {
546                     mprf("%s reflects off an invisible shield around you!",
547                          mon.name(DESC_THE, true).c_str());
548                 }
549             }
550             else if (you.see_cell(pos))
551             {
552                 if (victim->observable())
553                 {
554                     if (shield && shield_reflects(*shield))
555                     {
556                         mprf("%s reflects %s off %s %s!",
557                              victim->name(DESC_THE, true).c_str(),
558                              mon.name(DESC_THE, true).c_str(),
559                              victim->pronoun(PRONOUN_POSSESSIVE).c_str(),
560                              shield->name(DESC_PLAIN).c_str());
561                         ident_reflector(shield);
562                     }
563                     else
564                     {
565                         mprf("%s reflects off an invisible shield around %s!",
566                              mon.name(DESC_THE, true).c_str(),
567                              victim->name(DESC_THE, true).c_str());
568 
569                         item_def *amulet = victim->slot_item(EQ_AMULET);
570                         if (amulet)
571                             ident_reflector(amulet);
572                     }
573                 }
574                 else
575                 {
576                     mprf("%s bounces off of thin air!",
577                          mon.name(DESC_THE, true).c_str());
578                 }
579             }
580             victim->shield_block_succeeded();
581 
582             // mid_t is unsigned so won't fit in a plain int
583             mon.props[IOOD_REFLECTOR] = (int64_t) victim->mid;
584             mon.props[IOOD_VX] = vx = -vx;
585             mon.props[IOOD_VY] = vy = -vy;
586 
587             // Need to get out of the victim's square.
588 
589             // If you're next to the caster and both of you wear shields of
590             // reflection, this can lead to a brief game of ping-pong, but
591             // rapidly increasing shield penalties will make it short.
592             mon.lose_energy(EUT_MOVE);
593             goto move_again;
594         }
595 
596         if (_iood_hit(mon, pos))
597             return true;
598     }
599 
600     if (!mon.move_to_pos(pos))
601     {
602         _iood_stop(mon);
603         return true;
604     }
605 
606     // move_to_pos() just trashed the coords, set them again
607     mon.props[IOOD_X] = x;
608     mon.props[IOOD_Y] = y;
609 
610     return false;
611 }
612 
613 // Reduced copy of iood_act to move the orb while the player is off-level.
614 // Just goes straight and dissipates instead of hitting anything.
_iood_catchup_move(monster & mon)615 static bool _iood_catchup_move(monster& mon)
616 {
617     float x = mon.props[IOOD_X];
618     float y = mon.props[IOOD_Y];
619     float vx = mon.props[IOOD_VX];
620     float vy = mon.props[IOOD_VY];
621 
622     if (!vx && !vy) // not initialized
623     {
624         _iood_stop(mon, false);
625         return true;
626     }
627 
628     _normalize(vx, vy);
629 
630     x += vx;
631     y += vy;
632 
633     mon.props[IOOD_X] = x;
634     mon.props[IOOD_Y] = y;
635     mon.props[IOOD_DIST].get_int()++;
636 
637     const coord_def pos(static_cast<int>(round(x)), static_cast<int>(round(y)));
638     if (!in_bounds(pos))
639     {
640         _iood_stop(mon, true);
641         return true;
642     }
643 
644     if (pos == mon.pos())
645         return false;
646 
647     actor *victim = actor_at(pos);
648     if (cell_is_solid(pos) || victim)
649     {
650         // Just dissipate instead of hitting something.
651         _iood_stop(mon, true);
652         return true;
653     }
654 
655     if (!mon.move_to_pos(pos))
656     {
657         _iood_stop(mon);
658         return true;
659     }
660 
661     // move_to_pos() just trashed the coords, set them again
662     mon.props[IOOD_X] = x;
663     mon.props[IOOD_Y] = y;
664 
665     return false;
666 }
667 
iood_catchup(monster * mons,int pturns)668 void iood_catchup(monster* mons, int pturns)
669 {
670     monster& mon = *mons;
671     ASSERT(mons_is_projectile(*mons));
672 
673     const int moves = pturns * mon.speed / BASELINE_DELAY;
674 
675     // Handle some cases for IOOD only
676     if (mons_is_projectile(*mons))
677     {
678         if (moves > 50)
679         {
680             _iood_stop(mon, false);
681             return;
682         }
683 
684         if (mon.props[IOOD_KC].get_byte() == KC_YOU)
685         {
686             // Left player's vision.
687             _iood_stop(mon, false);
688             return;
689         }
690     }
691 
692     for (int i = 0; i < moves; ++i)
693         if (_iood_catchup_move(mon))
694             return;
695 }
696