Much slaying of non-Coord coordinates. FOV is cleverer.
authorMartin Read <martin@blackswordsonics.com>
Tue, 1 Oct 2013 23:27:36 +0000 (00:27 +0100)
committerMartin Read <martin@blackswordsonics.com>
Tue, 1 Oct 2013 23:27:36 +0000 (00:27 +0100)
20 files changed:
Makefile
combat.cc
combat.hh
coord.hh
display-nc.cc
display.hh
fov.cc
fov.hh
main.cc
map.cc
map.hh
mon2.cc
monsters.cc
monsters.hh
objects.cc
objects.hh
sorcery.cc
u.cc
vector.cc [deleted file]
victrix-abyssi.hh

index a2f5d9a..01d1c5d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # Makefile for Victrix Abyssi
 
-OBJS=combat.o display-nc.o fov.o main.o map.o misc.o monsters.o mon2.o objects.o permobj.o permons.o pmon2.o rng.o sorcery.o u.o vector.o
+OBJS=combat.o display-nc.o fov.o main.o map.o misc.o monsters.o mon2.o objects.o permobj.o permons.o pmon2.o rng.o sorcery.o u.o
 
 include dirs.mk
 include features.mk
@@ -52,8 +52,6 @@ monsters.o: monsters.cc victrix-abyssi.hh monsters.hh objects.hh
 
 mon2.o: mon2.cc victrix-abyssi.hh sorcery.hh monsters.hh objects.hh
 
-vector.o: vector.cc victrix-abyssi.hh
-
 sorcery.o: sorcery.cc victrix-abyssi.hh sorcery.hh objects.hh monsters.hh
 
 # vim:ts=8:sts=8:sw=8:noexpandtab
index d0eb1a0..86571a9 100644 (file)
--- a/combat.cc
+++ b/combat.cc
 #include "combat.hh"
 #include "monsters.hh"
 
-Action_cost player_attack(int dy, int dx)
+Action_cost player_attack(Offset delta)
 {
+    Coord c = u.pos + delta;
     if ((objects[u.weapon].obj_id == PO_BOW) || (objects[u.weapon].obj_id == PO_CROSSBOW))
     {
-        ushootm(dy, dx);
+        ushootm(delta);
     }
-    else if (lvl.mons[u.y + dy][u.x + dx] != NO_MON)
+    else if (lvl.mon_at(c) != NO_MON)
     {
-        uhitm(lvl.mons[u.y + dy][u.x + dx]);
+        uhitm(lvl.mon_at(c));
     }
     else
     {
@@ -121,14 +122,15 @@ int uhitm(int mon)
     return 1;   /* Hit. */
 }
 
-int ushootm(int sy, int sx)
+int ushootm(Offset step)
 {
     /* Propagate a missile in direction (sy,sx). Attack first target in
      * LOF. */
     int tohit;
     int range;
-    int y, x;
+    Coord c = u.pos + step;
     int done = 0;
+    int mon;
     Mon *mptr;
     Obj *wep;
     struct permobj *pwep;
@@ -136,15 +138,14 @@ int ushootm(int sy, int sx)
     wep = objects + u.weapon;
     pwep = permobjs + wep->obj_id;
     damage = one_die(pwep->power);
-    y = u.y + sy;
-    x = u.x + sx;
     range = 1;
-    for ( ; !done; (y += sy), (x += sx))
+    for ( ; !done; c += step)
     {
-        if (lvl.mons[y][x] != NO_MON)
+        mon = lvl.mon_at(c);
+        if (mon != NO_MON)
         {
             done = 1;
-            mptr = monsters + lvl.mons[y][x];
+            mptr = monsters + mon;
             tohit = zero_die(u.agility + u.level - range);
             if (range == 1)
             {
@@ -153,17 +154,17 @@ int ushootm(int sy, int sx)
             }
             if (tohit >= mptr->defence)
             {
-                if (mon_visible(lvl.mons[y][x]))
+                if (mon_visible(mon))
                 {
                     print_msg("You hit ");
-                    print_mon_name(lvl.mons[y][x], 1);
+                    print_mon_name(mon, 1);
                     print_msg(".\n");
                     print_msg("You do %d damage.\n", damage);
                 }
-                damage_mon(lvl.mons[y][x], damage, true);
+                damage_mon(mon, damage, true);
                 if ((mptr->used) && (wep->obj_id == PO_THUNDERBOW))
                 {
-                    int kb = knockback_mon(lvl.mons[y][x], sy, sx, true, true);
+                    int kb = knockback_mon(mon, step, true, true);
                     switch (kb)
                     {
                     case 0:
@@ -182,15 +183,15 @@ int ushootm(int sy, int sx)
             else
             {
                 print_msg("You miss ");
-                print_mon_name(lvl.mons[y][x], 1);
+                print_mon_name(mon, 1);
                 print_msg(".\n");
                 return 0;
             }
         }
-        else if ((lvl.terrain[y][x] == WALL) || (lvl.terrain[y][x] == HARDWALL) ||
-                 (lvl.terrain[y][x] == DOOR))
+        else if ((lvl.terrain_at(c) == WALL) || (lvl.terrain_at(c) == HARDWALL) ||
+                 (lvl.terrain_at(c) == DOOR))
         {
-            print_msg("Your %s hits the %s.\n", (wep->obj_id == PO_CROSSBOW) ? "bolt" : "arrow", (lvl.terrain[y][x] == DOOR) ? "door" : "wall");
+            print_msg("Your %s hits the %s.\n", (wep->obj_id == PO_CROSSBOW) ? "bolt" : "arrow", (lvl.terrain_at(c) == DOOR) ? "door" : "wall");
             return 0;
         }
     }
@@ -306,11 +307,9 @@ int mshootu(int mon)
 {
     Mon *mptr;
     Mon *bystander;
-    int y;
-    int x;
-    int dy;
-    int dx;
-    int sy, sx;
+    Coord c;
+    Offset delta;
+    Offset step;
     int done;
     int unaffected = 0;
     int tohit;
@@ -319,13 +318,10 @@ int mshootu(int mon)
     int defence;
     Damtyp dtype;
     mptr = monsters + mon;
-    y = mptr->y;
-    x = mptr->x;
+    c = mptr->pos;
     /* dy, dx == trajectory of missile */
-    dy = u.y - y;
-    dx = u.x - x;
-    sy = mysign(dy);
-    sx = mysign(dx);
+    delta = u.pos.delta(c);
+    step = mysign(delta);
     /* Don't get the bonus that applies to melee attacks. */
     tohit = zero_die(mptr->rtohit);
     print_mon_name(mon, 3);
@@ -354,15 +350,15 @@ int mshootu(int mon)
         defence = u.defence;
     }
     /* Move projectile one square before looking for targets. */
-    for ((done = 0), (y = mptr->y + sy), (x = mptr->x + sx);
-         !done;
-         (y += sy), (x += sx))
+    for ((done = 0), (c = mptr->pos + step); !done; c += step)
     {
-        if ((lvl.terrain[y][x] == WALL) || (lvl.terrain[y][x] == DOOR))
+        int mon;
+        if ((lvl.terrain_at(c) == WALL) || (lvl.terrain_at(c) == DOOR))
         {
             done = 1;
         }
-        if ((y == u.y) && (x == u.x))
+        mon = lvl.mon_at(c);
+        if (c == u.pos)
         {
             if (tohit >= defence)
             {
@@ -412,10 +408,10 @@ int mshootu(int mon)
                 print_msg("It misses you.\n");
             }
         }
-        else if (lvl.mons[y][x] != NO_MON)
+        else if (mon != NO_MON)
         {
             done = 1;
-            bystander = monsters + lvl.mons[y][x];
+            bystander = monsters + mon;
             switch (dtype)
             {
             case DT_COLD:
@@ -455,7 +451,7 @@ int mshootu(int mon)
             if (tohit >= bystander->defence)
             {
                 damage = one_die(mptr->rdam);
-                damage_mon(lvl.mons[y][x], dtype, false);
+                damage_mon(mon, dtype, false);
             }
         }
     }
@@ -501,10 +497,10 @@ static void flask_effect_fire(Mon *mptr)
     }
 }
 
-Action_cost throw_flask(int obj, int sy, int sx)
+Action_cost throw_flask(int obj, Offset step)
 {
     int i;
-    int y, x;
+    Coord c;
     int mon;
     void (*flask_effect)(Mon *);
     switch (objects[obj].obj_id)
@@ -522,11 +518,9 @@ Action_cost throw_flask(int obj, int sy, int sx)
         print_msg("internal error: attempt to throw non-flask.\n");
         return Cost_none;
     }
-    for (i = 0, y = u.y, x = u.x; i < 10; ++i)
+    for ((i = 1), (c = u.pos + step); i < 10; ++i, (c += step))
     {
-        y += sy;
-        x += sx;
-        mon = lvl.mons[y][x];
+        mon = lvl.mon_at(c);
         if ((mon != NO_MON) && (monsters[mon].used))
         {
             Mon *mptr = monsters + mon;
index caf5b96..149db04 100644 (file)
--- a/combat.hh
+++ b/combat.hh
 
 #define agility_modifier() (u.withering ? (u.agility / 10) : (u.agility / 5))
 /* XXX combat.c data and funcs */
-extern Action_cost throw_flask(int obj, int sy, int sx);
-extern Action_cost player_attack(int dy, int dx);
+extern Action_cost throw_flask(int obj, Offset step);
+extern Action_cost player_attack(Offset step);
 extern int mhitu(int mon, Damtyp dtyp);
 extern int uhitm(int mon);
 extern int mshootu(int mon);
-extern int ushootm(int sy, int sx);
+extern int ushootm(Offset step);
 
 #endif
 
index f3a06e8..b803cdd 100644 (file)
--- a/coord.hh
+++ b/coord.hh
 
 #include <algorithm>
 
-template <typename T> T myabs(T val) { return (val < 0) ? -val : val; }
-template <typename T> T mysign(T val) { return (val < 0) ? -1 : ((val > 0) ? 1 : 0); }
+template <typename T> T myabs(T val);
+template <typename T> T mysign(T val);
+
+template <typename T> inline T myabs(T val) { return (val < 0) ? -val : val; }
+template <typename T> inline T mysign(T val) { return (val < 0) ? -1 : ((val > 0) ? 1 : 0); }
 
 struct Offset
 {
     int y;
     int x;
+    bool ecardinal(void) const { return ((y && !x) || (x && !y)); }
+    bool rcardinal(void) const { return ecardinal() || (myabs(y) == myabs(x)); }
     int len_cheb(void) const { return std::max(myabs(y), myabs(x)); }
     int len_taxi(void) const { return myabs(y) + myabs(x); }
     int lensq_eucl(void) const { return y * y + x * x; }
@@ -51,6 +56,9 @@ struct Coord
 {
     int y;
     int x;
+    int dist_cheb(const Coord& right) const { return std::max(myabs(y - right.y), myabs(x - right.x)); }
+    int dist_taxi(const Coord& right) const { return myabs(y - right.y) + myabs(x - right.x); }
+    int distsq_eucl(const Coord& right) const { return (y - right.y) * (y - right.y) + (x - right.x) * (x - right.x); }
     Offset delta(const Coord& right) const { Offset d = { y - right.y, x - right.x }; return d; }
     Coord operator +(const Offset& right) const { Coord c = { y + right.y, x + right.x}; return c; }
     Coord operator -(const Offset& right) const { Coord c = { y - right.y, x - right.x}; return c; }
@@ -63,6 +71,8 @@ struct Coord
 extern const Coord Nowhere;
 extern const Offset North, Northeast, East, Southeast, South, Southwest, West, Northwest, Stationary;
 
+template<> inline Coord myabs<Coord>(Coord val) { Coord c = { myabs(val.y), myabs(val.x) }; return c; }
+template<> inline Offset myabs<Offset>(Offset val) { Offset o = { myabs(val.y), myabs(val.x) }; return o; }
 template<> inline Coord mysign<Coord>(Coord val) { Coord c = { mysign(val.y), mysign(val.x) }; return c; }
 template<> inline Offset mysign<Offset>(Offset val) { Offset o = { mysign(val.y), mysign(val.x) }; return o; }
 #endif
index a2ef8de..fc8831c 100644 (file)
@@ -165,47 +165,50 @@ static const cchar_t *object_char(int object_id)
 
 void touch_back_buffer(void)
 {
-    int y;
-    int x;
-    for (y = 0; y < DUN_HEIGHT; y++)
+    Coord c;
+    for (c.y = 0; c.y < DUN_HEIGHT; ++c.y)
     {
-        for (x = 0; x < DUN_WIDTH; x++)
+        for (c.x = 0; c.x < DUN_WIDTH; ++c.x)
         {
-            newsym(y, x);
+            newsym(c);
         }
     }
     map_updated = 1;
     hard_redraw = 1;
 }
 
-void newsym(int y, int x)
+void newsym(Coord c)
 {
     const cchar_t *ch;
-    ch = back_buffer[y][x];
-    if ((y == u.y) && (x == u.x))
+    int obj = lvl.obj_at(c);
+    int mon = lvl.mon_at(c);
+    Terrain terr = lvl.terrain_at(c);
+    uint32_t flags = lvl.flags_at(c);
+    ch = back_buffer[c.y][c.x];
+    if (c == u.pos)
     {
-        back_buffer[y][x] = &player_tile;
+        back_buffer[c.y][c.x] = &player_tile;
     }
-    else if ((!show_terrain) && (lvl.mons[y][x] != NO_MON) && mon_visible(lvl.mons[y][x]))
+    else if ((!show_terrain) && (mon != NO_MON) && mon_visible(lvl.mon_at(c)))
     {
-        back_buffer[y][x] = monster_char(monsters[lvl.mons[y][x]].mon_id);
+        back_buffer[c.y][c.x] = monster_char(monsters[mon].mon_id);
     }
-    else if (lvl.flags[y][x] & MAPFLAG_EXPLORED)
+    else if (flags & MAPFLAG_EXPLORED)
     {
-        if ((!show_terrain) && (lvl.objs[y][x] != NO_OBJ))
+        if ((!show_terrain) && (obj != NO_OBJ))
         {
-            back_buffer[y][x] = object_char(objects[lvl.objs[y][x]].obj_id);
+            back_buffer[c.y][c.x] = object_char(objects[obj].obj_id);
         }
         else
         {
-            back_buffer[y][x] = terrain_char(lvl.terrain[y][x]);
+            back_buffer[c.y][c.x] = terrain_char(terr);
         }
     }
     else
     {
-        back_buffer[y][x] = &blank_tile;
+        back_buffer[c.y][c.x] = &blank_tile;
     }
-    if (ch != back_buffer[y][x])
+    if (ch != back_buffer[c.y][c.x])
     {
         map_updated = 1;
     }
@@ -215,17 +218,16 @@ static void draw_world(void)
 {
     int i;
     int j;
-    int x;
-    int y;
+    Coord c;
 
     for (i = 0; i < 21; i++)
     {
-        y = u.y + i - 10;
+        c.y = u.pos.y + i - 10;
         for (j = 0; j < 21; j++)
         {
-            x = u.x + j - 10;
-            if ((y < 0) || (x < 0) ||
-                (y >= DUN_HEIGHT) || (x >= DUN_WIDTH))
+            c.x = u.pos.x + j - 10;
+            if ((c.y < 0) || (c.x < 0) ||
+                (c.y >= DUN_HEIGHT) || (c.x >= DUN_WIDTH))
             {
                 if ((front_buffer[i][j] != &blank_tile) || hard_redraw)
                 {
@@ -233,14 +235,14 @@ static void draw_world(void)
                 }
                 front_buffer[i][j] = &blank_tile;
             }
-            else if (hard_redraw || (front_buffer[i][j] != back_buffer[y][x]))
+            else if (hard_redraw || (front_buffer[i][j] != back_buffer[c.y][c.x]))
             {
-                mvwadd_wch(world_window, i, j, back_buffer[y][x]);
+                mvwadd_wch(world_window, i, j, back_buffer[c.y][c.x]);
                 if (!player_fov.affected[i][j])
                 {
                     mvwchgat(world_window, i, j, 1, A_BOLD, Gcol_d_grey, NULL);
                 }
-                front_buffer[i][j] = back_buffer[y][x];
+                front_buffer[i][j] = back_buffer[c.y][c.x];
             }
         }
     }
@@ -556,7 +558,7 @@ tryagain:
     }
 }
 
-Pass_fail select_dir(int *psy, int *psx)
+Pass_fail select_dir(Offset *pstep)
 {
     int ch;
     bool done = false;
@@ -568,50 +570,42 @@ Pass_fail select_dir(int *psy, int *psx)
         {
         case 'h':
         case '4':
-            *psx = -1;
-            *psy = 0;
+            *pstep = North;
             done = true;
             break;
         case 'j':
         case '2':
-            *psx = 0;
-            *psy = 1;
+            *pstep = South;
             done = true;
             break;
         case 'k':
         case '8':
-            *psx = 0;
-            *psy = -1;
+            *pstep = West;
             done = true;
             break;
         case 'l':
         case '6':
-            *psx = 1;
-            *psy = 0;
+            *pstep = East;
             done = true;
             break;
         case 'y':
         case '7':
-            *psx = -1;
-            *psy = -1;
+            *pstep = Northwest;
             done = true;
             break;
         case 'u':
         case '9':
-            *psx = 1;
-            *psy = -1;
+            *pstep = Northeast;
             done = true;
             break;
         case 'b':
         case '1':
-            *psx = -1;
-            *psy = 1;
+            *pstep = Southwest;
             done = true;
             break;
         case 'n':
         case '3':
-            *psx = 1;
-            *psy = 1;
+            *pstep = Southeast;
             done = true;
             break;
         case '\x1b':
@@ -861,22 +855,22 @@ void print_help(void)
     print_msg("\nThis is all the help you get. Good luck!\n");
 }
 
-void touch_one_screen(int y, int x)
+void touch_one_screen(Coord c)
 {
-    int y2, x2;
-    for (y2 = y - 10; y2 <= y + 10; y2++)
+    Coord c2;
+    for (c2.y = c.y - 10; c2.y <= c.y + 10; c2.y++)
     {
-        if ((y2 < 0) || (y2 >= DUN_HEIGHT))
+        if ((c2.y < 0) || (c2.y >= DUN_HEIGHT))
         {
             continue;
         }
-        for (x2 = x - 10; x2 <= x + 10; x2++)
+        for (c2.x = c.x - 10; c2.x <= c.x + 10; c2.x++)
         {
-            if ((x2 < 0) || (x2 >= DUN_WIDTH))
+            if ((c2.x < 0) || (c2.x >= DUN_WIDTH))
             {
                 continue;
             }
-            newsym(y2, x2);
+            newsym(c2);
         }
     }
 }
index 49cca87..2994659 100644 (file)
@@ -35,17 +35,17 @@ extern void print_help(void);
 extern int display_init(void);
 extern void display_update(void);
 extern int display_shutdown(void);
-extern void newsym(int y, int x);
+extern void newsym(Coord c);
 extern void touch_back_buffer(void);
 extern int inv_select(enum poclass_num filter, const char *action, int accept_blank);
 extern enum game_cmd get_command(void);
-extern Pass_fail select_dir(int *psy, int *psx);
+extern Pass_fail select_dir(Offset *pstep);
 extern int getYN(const char *msg);
 extern int getyn(const char *msg);
 extern void press_enter(void);
 extern void pressanykey(void);
 extern void show_discoveries(void);
-extern void touch_one_screen(int y, int x);
+extern void touch_one_screen(Coord c);
 
 /* "I've changed things that need to be redisplayed" flags. */
 extern int hard_redraw;
diff --git a/fov.cc b/fov.cc
index 890e13f..c4085ae 100644 (file)
--- a/fov.cc
+++ b/fov.cc
@@ -77,8 +77,9 @@ static bool dflt_blk(int y, int x)
  */
 static bool mark_explored(int y, int x, void *pvt)
 {
-    lvl.flags[y][x] |= MAPFLAG_EXPLORED;
-    newsym(y, x);
+    Coord c = { y, x };
+    lvl.set_flags_at(c, MAPFLAG_EXPLORED);
+    newsym(c);
     return true;
 }
 
@@ -112,6 +113,11 @@ static inline double outer_visible_slope(int rdl, int trn)
     return (rdl == trn) ? 1.0 : ((trn + 0.5) / (rdl - 0.5));
 }
 
+static inline double centre_slope(int rdl, int trn)
+{
+    return ((double) trn) / ((double) rdl);
+}
+
 /*! \brief Reset the affected flags of a Radiance object
  */
 void clear_radiance(Radiance *rad)
@@ -137,6 +143,7 @@ static void compute_row(Radiance *rad, int octant, int radius, double inmost_slo
     int outer_idx;
     int inner_idx;
     double isl;
+    double csl;
 
     /* 
      * We should never get the slopes mismatched like this, but if we do,
@@ -167,8 +174,13 @@ static void compute_row(Radiance *rad, int octant, int radius, double inmost_slo
     {
         /* Get the inner "grazing" slope of the diamond inside the tile. */
         isl = inner_slope(radius, trn);
+        csl = centre_slope(radius, trn);
         /* Mark the tile being examined as affected. */
-        rad->affected[MAX_FOV_RADIUS + dy][MAX_FOV_RADIUS + dx] = true;
+        rad->affected[MAX_FOV_RADIUS + dy][MAX_FOV_RADIUS + dx] |= Visflag_partial;
+        if ((csl >= inmost_slope) && (csl <= outmost_slope))
+        {
+            rad->affected[MAX_FOV_RADIUS + dy][MAX_FOV_RADIUS + dx] |= Visflag_central;
+        }
         if (block_flag)
         {
             /* If the previous tile was opaque... */
@@ -402,8 +414,8 @@ Radiance player_fov;
 void compute_fov(void)
 {
     clear_radiance(&player_fov);
-    player_fov.centre_y = u.y;
-    player_fov.centre_x = u.x;
+    player_fov.centre_y = u.pos.y;
+    player_fov.centre_x = u.pos.x;
     player_fov.radius = MAX_FOV_RADIUS;
     player_fov.order = Reo_ascending;
     player_fov.exclude_centre = false;
diff --git a/fov.hh b/fov.hh
index bd9ea2a..e5381c4 100644 (file)
--- a/fov.hh
+++ b/fov.hh
@@ -47,9 +47,13 @@ enum rad_eval_order {
 
 typedef enum rad_eval_order Rad_eval_order;
 
+#define Visflag_partial 0x01u
+#define Visflag_central 0x02u
+
+typedef uint8_t Vision_flags; /* 8 bits should do for now */
 struct radiance_data
 {
-    bool affected[MAX_FOV_RADIUS * 2 + 1][MAX_FOV_RADIUS * 2 + 1];
+    Vision_flags affected[MAX_FOV_RADIUS * 2 + 1][MAX_FOV_RADIUS * 2 + 1];
     int centre_y;
     int centre_x;
     int radius;
diff --git a/main.cc b/main.cc
index a6e45e4..07b8c86 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -66,7 +66,7 @@ static void rebuild_mapmons(void)
     {
         if (monsters[i].used)
         {
-            lvl.mons[monsters[i].y][monsters[i].x] = i;
+            lvl.set_mon_at(monsters[i].pos, i);
         }
     }
 }
@@ -78,7 +78,7 @@ static void rebuild_mapobjs(void)
     {
         if (objects[i].used && !objects[i].with_you)
         {
-            lvl.objs[objects[i].y][objects[i].x] = i;
+            lvl.set_obj_at(objects[i].pos, i);
         }
     }
 }
@@ -128,7 +128,7 @@ void load_game(void)
     fread(&u, 1, sizeof u, fp);
     fread(&game_tick, 1, sizeof game_tick, fp);
     fclose(fp);
-    explore_around(u.y, u.x);
+    look_around_you();
     status_updated = 1;
     map_updated = 1;
     hard_redraw = 1;
@@ -146,6 +146,42 @@ int inclusive_flat(int lower, int upper)
     return lower + zero_die(upper - lower + 1);
 }
 
+Offset random_step(void)
+{
+    switch (zero_die(8))
+    {
+    case 0:
+        return Northwest;
+    case 1:
+        return North;
+    case 2:
+        return Northwest;
+    case 3:
+        return West;
+    case 4:
+        return East;
+    case 5:
+        return Southwest;
+    case 6:
+        return South;
+    case 7:
+        return Southeast;
+    }
+    return Stationary;
+}
+
+Coord inclusive_boxed(Coord topleft, Coord botright)
+{
+    Coord c = { inclusive_flat(topleft.y, botright.y), inclusive_flat(topleft.x, botright.x) };
+    return c;
+}
+
+Coord exclusive_boxed(Coord topleft, Coord botright)
+{
+    Coord c = { exclusive_flat(topleft.y, botright.y), exclusive_flat(topleft.x, botright.x) };
+    return c;
+}
+
 int one_die(int sides)
 {
     int rval;
@@ -196,36 +232,36 @@ Action_cost do_command(enum game_cmd cmd)
     Pass_fail pf;
     int slot;
     Action_cost cost;
-    int sy, sx;
+    Offset step;
     switch (cmd)
     {
     case MOVE_NORTH:
-        return move_player(-1, 0);
+        return move_player(North);
     case MOVE_SOUTH:
-        return move_player(1, 0);
+        return move_player(South);
     case MOVE_EAST:
-        return move_player(0, 1);
+        return move_player(East);
     case MOVE_WEST:
-        return move_player(0, -1);
+        return move_player(West);
     case MOVE_NW:
-        return move_player(-1, -1);
+        return move_player(Northwest);
     case MOVE_NE:
-        return move_player(-1, 1);
+        return move_player(Northeast);
     case MOVE_SE:
-        return move_player(1, 1);
+        return move_player(Southeast);
     case MOVE_SW:
-        return move_player(1, -1);
+        return move_player(Southwest);
 
     case ATTACK:
-        pf = select_dir(&sy, &sx);
+        pf = select_dir(&step);
         if (pf != You_fail)
         {
-            return player_attack(sy, sx);
+            return player_attack(step);
         }
         return Cost_none;
 
     case GET_ITEM:
-        if (lvl.objs[u.y][u.x] != NO_OBJ)
+        if (lvl.obj_at(u.pos) != NO_OBJ)
         {
             attempt_pickup();
             return Cost_std;
@@ -310,7 +346,7 @@ Action_cost do_command(enum game_cmd cmd)
         if (u.armour != NO_OBJ)
         {
             if ((u.resistances[DT_FIRE] == RESIST_ARMOUR) &&
-                (lvl.terrain[u.y][u.x] == LAVA))
+                (lvl.terrain_at(u.pos) == LAVA))
             {
                 print_msg("Your armour is your only current source of fire\nresistance; removing it here would incinerate you.\n");
                 return Cost_none;
@@ -331,7 +367,7 @@ Action_cost do_command(enum game_cmd cmd)
         return Cost_none;
 
     case GO_DOWN_STAIRS:
-        if (lvl.terrain[u.y][u.x] == STAIRS)
+        if (lvl.terrain_at(u.pos) == STAIRS)
         {
             leave_level();
             make_new_level();
@@ -373,10 +409,10 @@ Action_cost do_command(enum game_cmd cmd)
         slot = inv_select(POCLASS_FLASK, "throw", 0);
         if (slot != SLOT_CANCEL)
         {
-            pf = select_dir(&sy, &sx);
+            pf = select_dir(&step);
             if (pf != You_fail)
             {
-                return throw_flask(u.inventory[slot], sy, sx);
+                return throw_flask(u.inventory[slot], step);
             }
         }
         return Cost_none;
@@ -387,11 +423,11 @@ Action_cost do_command(enum game_cmd cmd)
         {
             print_msg("You have no ring to remove!\n");
         }
-        else if ((lvl.terrain[u.y][u.x] == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
+        else if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
         {
             print_msg("That ring is your only current source of fire resistance. Removing\nit here would incinerate you.\n");
         }
-        else if ((objects[u.ring].obj_id == PO_RING_FROST) && (lvl.terrain[u.y][u.x] == WATER))
+        else if ((objects[u.ring].obj_id == PO_RING_FROST) && (lvl.terrain_at(u.pos) == WATER))
         {
             print_msg("Since nobody ever taught you to swim, removing that ring\nhere would result in your death by drowning.\n");
         }
@@ -464,7 +500,7 @@ Action_cost do_command(enum game_cmd cmd)
         return Cost_none;
     case DROP_ITEM:
         cost = Cost_none;
-        if (lvl.objs[u.y][u.x] != NO_OBJ)
+        if (lvl.obj_at(u.pos) != NO_OBJ)
         {
             print_msg("There is already an item here.\n");
         }
diff --git a/map.cc b/map.cc
index 49309c7..ab9ef86 100644 (file)
--- a/map.cc
+++ b/map.cc
 #include "fov.hh"
 
 #include <string.h>
-level lvl;
+Level lvl;
 int depth = 1;
 
-static Pass_fail get_levgen_mon_floor(int *y, int *x);
+static Pass_fail get_levgen_mon_floor(Coord *c);
 static void build_level_shrine(void);
 static void build_level_intrusions(void);
 static void build_level_classic(void);
-static int excavation_write(int y, int x, void *data);
-static int intrusion_write(int y, int x, void *data);
+static int excavation_write(Coord c, void *data);
+static int intrusion_write(Coord c, void *data);
+
+Coord Level::random_point(int margin) const
+{
+    Coord tl = { margin, margin };
+    Coord br = { DUN_HEIGHT - (margin + 1), DUN_WIDTH - (margin + 1) };
+    return inclusive_boxed(tl, br);
+}
 
 void leave_level(void)
 {
@@ -67,49 +74,47 @@ void make_new_level(void)
     build_level();
     populate_level();
     inject_player();
-    explore_around(u.y, u.x);
+    look_around_you();
     display_update();
 }
 
-typedef int (*rwalk_mod_funcptr)(int y, int x, void *data);
+typedef int (*rwalk_mod_funcptr)(Coord c, void *data);
 
-static void run_random_walk(int oy, int ox, rwalk_mod_funcptr func,
+static void run_random_walk(Coord oc, rwalk_mod_funcptr func,
                             void *priv_ptr, int cells)
 {
     int i;
-    int y = oy;
-    int x = ox;
+    Coord c = oc;
     int bailout = 10000;
 
     for (i = 0; (i < cells) && (bailout > 0); --bailout)
     {
-        oy = y;
-        ox = x;
+        oc = c;
         if (zero_die(2))
         {
-            y += (zero_die(2) ? -1 : 1);
-            if (y == 0)
+            c.y += (zero_die(2) ? -1 : 1);
+            if (c.y == 0)
             {
-                y = 2;
+                c.y = 2;
             }
-            else if (y == DUN_HEIGHT - 1)
+            else if (c.y == DUN_HEIGHT - 1)
             {
-                y = DUN_HEIGHT - 3;
+                c.y = DUN_HEIGHT - 3;
             }
         }
         else
         {
-            x += (zero_die(2) ? -1 : 1);
-            if (x == 0)
+            c.x += (zero_die(2) ? -1 : 1);
+            if (c.x == 0)
             {
-                x = 2;
+                c.x = 2;
             }
-            else if (x == DUN_WIDTH - 1)
+            else if (c.x == DUN_WIDTH - 1)
             {
-                x = DUN_WIDTH - 3;
+                c.x = DUN_WIDTH - 3;
             }
         }
-        switch (func(y, x, priv_ptr))
+        switch (func(c, priv_ptr))
         {
         case 0:
             /* nothing changed */
@@ -120,8 +125,7 @@ static void run_random_walk(int oy, int ox, rwalk_mod_funcptr func,
             break;
         case 2:
             /* reject! */
-            y = oy;
-            x = ox;
+            c = oc;
             break;
         }
     }
@@ -185,13 +189,12 @@ void build_level(void)
 
 void build_level_classic(void)
 {
-    int y = DUN_HEIGHT / 2;
-    int x = DUN_WIDTH / 2;
+    Coord c = { DUN_HEIGHT / 2, DUN_WIDTH / 2 };
     int num_pools;
     int walk_data[4] = { 1, FLOOR, WALL, FLOOR };
 
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
     if ((lvl.theme != THEME_UNDEAD) && (depth > 20) && !zero_die(4))
     {
         num_pools = inclusive_flat(1, 4);
@@ -211,21 +214,18 @@ void build_level_classic(void)
     while (num_pools > 0)
     {
         int pool_size = inclusive_flat(9, 36);
-
         do {
-            y = exclusive_flat(1, DUN_HEIGHT - 2);
-            x = exclusive_flat(1, DUN_WIDTH - 2);
-        } while (lvl.terrain[y][x] != FLOOR);
-        run_random_walk(y, x, excavation_write, walk_data, pool_size);
+            c = lvl.random_point(2);
+        } while (lvl.terrain_at(c) != FLOOR);
+        run_random_walk(c, excavation_write, walk_data, pool_size);
         --num_pools;
     }
     /* Add the stairs */
     do
     {
-        y = exclusive_flat(0, DUN_HEIGHT - 1);
-        x = exclusive_flat(0, DUN_WIDTH - 1);
-    } while (lvl.terrain[y][x] != FLOOR);
-    lvl.terrain[y][x] = STAIRS;
+        c = lvl.random_point(1);
+    } while (lvl.terrain_at(c) != FLOOR);
+    lvl.set_terrain_at(c, STAIRS);
 }
 
 struct shrine shrines[4] =
@@ -298,55 +298,59 @@ struct shrine shrines[4] =
 
 static void build_level_shrine(void)
 {
-    int y = DUN_HEIGHT / 2;
-    int x = DUN_WIDTH / 2;
+    Coord c;
     int shrine_ty = inclusive_flat(DUN_HEIGHT / 4, DUN_HEIGHT / 2);
     int shrine_lx = inclusive_flat(DUN_WIDTH / 4, DUN_WIDTH / 2);
     int walk_data[4] = { 2, FLOOR, WALL, FLOOR };
     int i, j;
     int shrine_num = zero_die(sizeof shrines / sizeof shrines[0]);
 
-    for ((i = 0), (y = shrine_ty); i < SHRINE_HEIGHT; ++i, ++y)
+    for ((i = 0), (c.y = shrine_ty); i < SHRINE_HEIGHT; ++i, ++c.y)
     {
-        for ((j = 0), (x = shrine_lx); j < SHRINE_WIDTH; ++j, ++x)
+        for ((j = 0), (c.x = shrine_lx); j < SHRINE_WIDTH; ++j, ++c.x)
         {
             switch (shrines[shrine_num].grid[i][j])
             {
             case '.':
-                lvl.terrain[y][x] = FLOOR;
+                lvl.set_terrain_at(c, FLOOR);
                 break;
             case '#':
-                lvl.terrain[y][x] = HARDWALL;
+                lvl.set_terrain_at(c, HARDWALL);
                 break;
             case '+':
-                lvl.terrain[y][x] = DOOR;
+                lvl.set_terrain_at(c, DOOR);
                 break;
             case '_':
-                lvl.terrain[y][x] = ALTAR;
+                lvl.set_terrain_at(c, ALTAR);
+                break;
+            case 'L':
+                lvl.set_terrain_at(c, LAVA);
+                break;
+            case 'W':
+                lvl.set_terrain_at(c, WATER);
                 break;
             }
         }
     }
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
     /* and now the stairs, which are not in the shrine */
     do
     {
-        y = exclusive_flat(0, DUN_HEIGHT - 1);
-        x = exclusive_flat(0, DUN_WIDTH - 1);
-    } while ((lvl.terrain[y][x] != FLOOR) &&
-             (y >= shrine_ty) && (y < shrine_ty + SHRINE_HEIGHT) &&
-             (x >= shrine_lx) && (x < shrine_lx + SHRINE_WIDTH));
-    lvl.terrain[y][x] = STAIRS;
+        c = lvl.random_point(1);
+    } while ((lvl.terrain_at(c) != FLOOR) &&
+             (c.y >= shrine_ty) && (c.y < shrine_ty + SHRINE_HEIGHT) &&
+             (c.x >= shrine_lx) && (c.x < shrine_lx + SHRINE_WIDTH));
+    lvl.set_terrain_at(c, STAIRS);
 }
 
-static int excavation_write(int y, int x, void *data)
+static int excavation_write(Coord c, void *data)
 {
     const int *data_as_ints = (const int *) data;
     const int *overwrite = data_as_ints + 2;
     int newterr = data_as_ints[1];
     int j;
-    if (lvl.flags[y][x] & MAPFLAG_HARDWALL)
+    if (lvl.flags_at(c) & MAPFLAG_HARDWALL)
     {
         /* Don't bite into hardened walls, but don't waste a step on
          * them either. */
@@ -354,43 +358,38 @@ static int excavation_write(int y, int x, void *data)
     }
     for (j = 0; j < data_as_ints[0]; ++j)
     {
-        if (lvl.terrain[y][x] == overwrite[j])
+        if (lvl.terrain_at(c) == overwrite[j])
         {
-            lvl.terrain[y][x] = (Terrain) newterr;
+            lvl.set_terrain_at(c, (Terrain) newterr);
             return 1;
         }
     }
     return 0;
 }
 
-static int intrusion_write(int y, int x, void *data)
+static int intrusion_write(Coord c, void *data)
 {
-    if (lvl.terrain[y][x] != WALL)
-    {
-        return 0;
-    }
-    if (lvl.flags[y][x] & MAPFLAG_HARDWALL)
+    if ((lvl.terrain_at(c) != WALL) || (lvl.flags_at(c) & MAPFLAG_HARDWALL))
     {
         return 0;
     }
     /* Don't intrude too closely on the centre of the level */
-    if ((y > ((DUN_HEIGHT / 2) - 4)) && (y < ((DUN_HEIGHT / 2) - 4)))
+    if ((c.y > ((DUN_HEIGHT / 2) - 4)) && (c.y < ((DUN_HEIGHT / 2) - 4)))
     {
         return 2;
     }
-    if ((x > ((DUN_WIDTH / 2) - 4)) && (x < ((DUN_WIDTH / 2) - 4)))
+    if ((c.x > ((DUN_WIDTH / 2) - 4)) && (c.x < ((DUN_WIDTH / 2) - 4)))
     {
         return 2;
     }
-    lvl.flags[y][x] |= MAPFLAG_HARDWALL;
-    lvl.terrain[y][x] = HARDWALL;
+    lvl.set_flags_at(c, MAPFLAG_HARDWALL);
+    lvl.set_terrain_at(c, HARDWALL);
     return 1;
 }
 
 void build_level_intrusions(void)
 {
-    int y;
-    int x;
+    Coord c;
     int i;
     int intrusion_size;
     int walk_data[4] = { 1, FLOOR, WALL, FLOOR };
@@ -401,62 +400,57 @@ void build_level_intrusions(void)
         {
             if (zero_die(2))
             {
-                x = inclusive_flat(1, DUN_WIDTH / 3);
+                c.x = inclusive_flat(1, DUN_WIDTH / 3);
             }
             else
             {
-                x = inclusive_flat((2 * DUN_WIDTH) / 3, DUN_WIDTH - 2);
+                c.x = inclusive_flat((2 * DUN_WIDTH) / 3, DUN_WIDTH - 2);
             }
             if (zero_die(2))
             {
-                y = inclusive_flat(1, DUN_HEIGHT / 3);
+                c.y = inclusive_flat(1, DUN_HEIGHT / 3);
             }
             else
             {
-                y = inclusive_flat((2 * DUN_HEIGHT) / 3, DUN_HEIGHT - 2);
+                c.y = inclusive_flat((2 * DUN_HEIGHT) / 3, DUN_HEIGHT - 2);
             }
-            //print_msg("Trying y %d x %d for intrusion\n", y, x);
-        } while (lvl.flags[y][x] & MAPFLAG_HARDWALL);
+        } while (lvl.flags_at(c) & MAPFLAG_HARDWALL);
         intrusion_size = inclusive_flat(27, 54);
-        //print_msg("Building intrusion %d size %d y %d x %d\n", i, intrusion_size, y, x);
-        run_random_walk(y, x, intrusion_write, NULL, intrusion_size);
+        run_random_walk(c, intrusion_write, NULL, intrusion_size);
     }
-    y = DUN_HEIGHT / 2;
-    x = DUN_WIDTH / 2;
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
-    run_random_walk(y, x, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    c.y = DUN_HEIGHT / 2;
+    c.x = DUN_WIDTH / 2;
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    run_random_walk(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
     /* and now the stairs */
     do
     {
-        y = exclusive_flat(0, DUN_HEIGHT - 1);
-        x = exclusive_flat(0, DUN_WIDTH - 1);
-    } while (lvl.terrain[y][x] != FLOOR);
-    lvl.terrain[y][x] = STAIRS;
+        c.y = exclusive_flat(0, DUN_HEIGHT - 1);
+        c.x = exclusive_flat(0, DUN_WIDTH - 1);
+    } while (lvl.terrain_at(c) != FLOOR);
+    lvl.set_terrain_at(c, STAIRS);
 }
 
-Pass_fail get_levgen_mon_floor(int *y, int *x)
+Pass_fail get_levgen_mon_floor(Coord *c)
 {
     int cell_try;
-    int ty, tx;
+    Coord t;
     for (cell_try = 0; cell_try < 200; cell_try++)
     {
-        ty = exclusive_flat(0, DUN_HEIGHT - 1);
-        tx = exclusive_flat(0, DUN_WIDTH - 1);
-        if ((lvl.terrain[ty][tx] != FLOOR) ||
-            (lvl.mons[ty][tx] != NO_MON))
+        t.y = exclusive_flat(0, DUN_HEIGHT - 1);
+        t.x = exclusive_flat(0, DUN_WIDTH - 1);
+        if ((lvl.terrain_at(t) != FLOOR) || (lvl.mon_at(t) != NO_MON))
         {
-            ty = -1;
-            tx = -1;
+            t = Nowhere;
             continue;
         }
         break;
     }
-    if (ty == -1)
+    if (t == Nowhere)
     {
         return You_fail;
     }
-    *y = ty;
-    *x = tx;
+    *c = t;
     return You_pass;
 }
 
@@ -464,17 +458,17 @@ void populate_level(void)
 {
     int i;
     Pass_fail pf;
-    int y, x;
+    Coord c;
     int ic;
     /* Generate some random monsters */
     for (i = 0; i < 10; i++)
     {
-        pf = get_levgen_mon_floor(&y, &x);
+        pf = get_levgen_mon_floor(&c);
         if (pf == You_fail)
         {
             continue;
         }
-        create_mon(NO_PMON, y, x);
+        create_mon(NO_PMON, c);
     }
     ic = 3 + depth;
     if (ic > 40)
@@ -485,12 +479,12 @@ void populate_level(void)
     /* Generate some random treasure */
     for (i = 0; i < ic; i++)
     {
-        pf = get_levgen_mon_floor(&y, &x);
+        pf = get_levgen_mon_floor(&c);
         if (pf == You_fail)
         {
             continue;
         }
-        create_obj(NO_POBJ, 1, 0, y, x);
+        create_obj(NO_POBJ, 1, 0, c);
     }
 }
 
@@ -499,22 +493,22 @@ void inject_player(void)
     int cell_try;
     for (cell_try = 0; cell_try < 200; cell_try++)
     {
-        u.y = exclusive_flat(0, DUN_HEIGHT - 1);
-        u.x = exclusive_flat(0, DUN_WIDTH - 1);
-        if (lvl.terrain[u.y][u.x] != FLOOR)
+        u.pos.y = exclusive_flat(0, DUN_HEIGHT - 1);
+        u.pos.x = exclusive_flat(0, DUN_WIDTH - 1);
+        if (lvl.terrain_at(u.pos) != FLOOR)
         {
             continue;
         }
-        if (lvl.mons[u.y][u.x] != NO_MON)
+        if (lvl.mon_at(u.pos) != NO_MON)
         {
             continue;
         }
         break;
     }
-    reloc_player(u.y, u.x);
+    reloc_player(u.pos);
 }
 
-void explore_around(int y, int x)
+void look_around_you(void)
 {
     compute_fov();
     touch_back_buffer();
diff --git a/map.hh b/map.hh
index e8064c8..5ecf5f6 100644 (file)
--- a/map.hh
+++ b/map.hh
@@ -72,7 +72,7 @@ struct shrine
 };
 
 /* XXX map.c data and funcs*/
-struct level
+struct Level
 {
     int objs[DUN_HEIGHT][DUN_WIDTH];
     int mons[DUN_HEIGHT][DUN_WIDTH];
@@ -80,9 +80,19 @@ struct level
     uint32_t flags[DUN_HEIGHT][DUN_WIDTH];
     level_theme theme;
     level_layout layout;
+    Terrain terrain_at(Coord c) const { return terrain[c.y][c.x]; }
+    void set_terrain_at(Coord c, Terrain t) { terrain[c.y][c.x] = t; }
+    uint32_t flags_at(Coord c) const { return flags[c.y][c.x]; }
+    void set_flags_at(Coord c, uint32_t to_set) { flags[c.y][c.x] |= to_set; }
+    void clear_flags_at(Coord c, uint32_t to_clear) { flags[c.y][c.x] &= ~to_clear; }
+    int mon_at(Coord c) const { return mons[c.y][c.x]; }
+    void set_mon_at(Coord c, int mon) { mons[c.y][c.x] = mon; }
+    int obj_at(Coord c) const { return objs[c.y][c.x]; }
+    void set_obj_at(Coord c, int obj) { objs[c.y][c.x] = obj; }
+    Coord random_point(int margin) const;
 };
 
-extern level lvl;
+extern Level lvl;
 
 extern int depth;
 extern enum level_theme current_theme;
@@ -93,7 +103,7 @@ extern void make_new_level(void);
 extern void build_level(void);
 extern void populate_level(void);
 extern void inject_player(void);
-extern void explore_around(int y, int x);
+extern void look_around_you(void);
 extern bool terrain_is_opaque(Terrain terr);
 #endif
 
diff --git a/mon2.cc b/mon2.cc
index 8d36354..61ee542 100644 (file)
--- a/mon2.cc
+++ b/mon2.cc
 #define AI_REALLY_HATE (-10000)
 /* AI map cell descriptor. */
 struct ai_cell {
-    int y, x;
-    int dy, dx;
+    Coord pos;
+    Offset delta;
     int score;
 };
 
 /* prototypes for AI preference functions. */
-static void get_naive_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x);
-static void get_seeking_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x);
-static void get_drunk_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x);
-static void build_ai_cells(struct ai_cell *cells, int y, int x);
-static Comparison ai_cell_compare(struct ai_cell *cell, int dy, int dx);
-static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x);
-static void get_chase_prefs(int mon, int *pref_y, int *pref_x);
+static void get_naive_prefs(Coord loc, Offset delta, Coord *pref_pos);
+static void get_seeking_prefs(Coord loc, Offset delta, Coord *pref_pos);
+static void get_drunk_prefs(Coord loc, Offset delta, Coord *pref_pos);
+static void build_ai_cells(struct ai_cell *cells, Coord loc);
+static Comparison ai_cell_compare(struct ai_cell *cell, Offset delta);
+static void get_dodger_prefs(Coord loc, Offset delta, Coord *pref_pos);
+static void get_chase_prefs(int mon, Coord *pref_pos);
 
 /* get_drunk_prefs()
  *
@@ -56,34 +56,27 @@ static void get_chase_prefs(int mon, int *pref_y, int *pref_x);
  * adjacent squares.
  */
 
-static void get_drunk_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x)
+static void get_drunk_prefs(Coord loc, Offset delta, Coord *pref_pos)
 {
-    int sy, sx;
+    Coord trypos;
+    Offset step;
     int tryct;
     int pref_idx;
     int idx2;
     int retry;
-    pref_y[0] = y;
-    pref_y[1] = y;
-    pref_y[2] = y;
-    pref_x[0] = x;
-    pref_x[1] = x;
-    pref_x[2] = x;
+    pref_pos[0] = loc;
+    pref_pos[1] = loc;
+    pref_pos[2] = loc;
     for (pref_idx = 0; pref_idx < 3; pref_idx++)
     {
         for (tryct = 0; tryct < 40; tryct++)
         {
             retry = 0;
-            sy = zero_die(3) - 1;
-            sx = zero_die(3) - 1;
-            if (!sy && !sx)
-            {
-                continue;
-            }
+            step = random_step();
+            trypos = loc + step;
             for (idx2 = 0; idx2 < pref_idx; idx2++)
             {
-                if ((pref_y[idx2] == y + sy) &&
-                    (pref_x[idx2] == x + sx))
+                if (pref_pos[idx2] == trypos)
                 {
                     retry = 1;
                     break;
@@ -93,8 +86,7 @@ static void get_drunk_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref
             {
                 continue;
             }
-            pref_y[pref_idx] = y + sy;
-            pref_x[pref_idx] = x + sx;
+            pref_pos[pref_idx] = trypos;
             break;
         }
     }
@@ -113,121 +105,77 @@ static void get_drunk_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref
  * details.
  */
 
-static void get_chase_prefs(int mon, int *pref_y, int *pref_x)
+static void get_chase_prefs(int mon, Coord *pref_pos)
 {
-    int sy, sx;
-    int ady, adx;
-    int dy, dx;
-    int y, x;
-    y = monsters[mon].y;
-    x = monsters[mon].x;
-    dy = monsters[mon].ai_lasty - y;
-    dx = monsters[mon].ai_lastx - x;
-    sy = mysign(dy);
-    ady = myabs(dy);
-    sx = mysign(dx);
-    adx = myabs(dx);
-    if (mon_can_pass(mon, y + sy, x + sx))
+    const Mon *mptr = monsters + mon;
+    Offset delta = mptr->ai_lastpos.delta(mptr->pos);
+    Offset step = mysign(delta);
+    Offset absdelta = myabs(delta);
+    Coord testpos = mptr->pos + step;
+    if (monsters[mon].can_pass(testpos))
     {
-        *pref_y = y + sy;
-        *pref_x = x + sx;
+        *pref_pos = testpos;
     }
-    else if (!sy)
-    {
-        /* We're on the horizontal; check the horizontally adjacent
-         * square, then the squares one square north or south in a
-         * random order. */
-        if (zero_die(2))
-        {
-            pref_y[1] = y - 1;
-            pref_y[2] = y + 1;
-        }
-        else
-        {
-            pref_y[1] = y + 1;
-            pref_y[2] = y - 1;
-        }
-        pref_x[1] = x + sx;
-        pref_x[2] = x + sx;
-        if (mon_can_pass(mon, pref_y[1], pref_x[1]))
-        {
-            pref_y[0] = pref_y[1];
-            pref_x[0] = pref_x[1];
-        }
-        else if (mon_can_pass(mon, pref_y[2], pref_x[2]))
-        {
-            pref_y[0] = pref_y[2];
-            pref_x[0] = pref_x[2];
-        }
-        else
-        {
-            pref_y[0] = monsters[mon].y;
-            pref_x[0] = monsters[mon].x;
-        }
-    }
-    else if (!sx)
+    else
     {
-        /* We're on the horizontal; check the horizontally adjacent
-         * square, then the squares one square north or south in a
-         * random order. */
-        if (zero_die(2))
+        if (!step.y)
         {
-            pref_x[1] = x - 1;
-            pref_x[2] = x + 1;
-        }
-        else
-        {
-            pref_x[1] = x + 1;
-            pref_x[2] = x - 1;
-        }
-        pref_y[1] = y + sy;
-        pref_y[2] = y + sy;
-        if (mon_can_pass(mon, pref_y[1], pref_x[1]))
-        {
-            pref_y[0] = pref_y[1];
-            pref_x[0] = pref_x[1];
-        }
-        else if (mon_can_pass(mon, pref_y[2], pref_x[2]))
-        {
-            pref_y[0] = pref_y[2];
-            pref_x[0] = pref_x[2];
-        }
-        else
-        {
-            pref_y[0] = monsters[mon].y;
-            pref_x[0] = monsters[mon].x;
+            /* We're on the horizontal; check the horizontally adjacent
+             * square, then the squares one square north or south in a
+             * random order. */
+            if (zero_die(2))
+            {
+                pref_pos[1] = testpos + North;
+                pref_pos[2] = testpos + South;
+            }
+            else
+            {
+                pref_pos[2] = testpos + North;
+                pref_pos[1] = testpos + South;
+            }
         }
-    }
-    else
-    {
-        if (zero_die(2))
+        else if (!step.x)
         {
-            pref_x[1] = x;
-            pref_y[1] = y + sy;
-            pref_x[2] = x + sx;
-            pref_y[2] = y;
+            /* We're on the horizontal; check the horizontally adjacent
+             * square, then the squares one square north or south in a
+             * random order. */
+            if (zero_die(2))
+            {
+                pref_pos[1] = testpos + West;
+                pref_pos[2] = testpos + East;
+            }
+            else
+            {
+                pref_pos[2] = testpos + West;
+                pref_pos[1] = testpos + East;
+            }
         }
         else
         {
-            pref_x[2] = x;
-            pref_y[2] = y + sy;
-            pref_x[1] = x + sx;
-            pref_y[1] = y;
+            pref_pos[1] = testpos;
+            pref_pos[2] = testpos;
+            if (zero_die(2))
+            {
+                pref_pos[1].x = mptr->pos.x;
+                pref_pos[2].y = mptr->pos.y;
+            }
+            else
+            {
+                pref_pos[2].x = mptr->pos.x;
+                pref_pos[1].y = mptr->pos.y;
+            }
         }
-        if (mon_can_pass(mon, pref_y[1], pref_x[1]))
+        if (mptr->can_pass(pref_pos[1]))
         {
-            pref_y[0] = pref_y[1];
-            pref_x[0] = pref_x[1];
+            pref_pos[0] = pref_pos[1];
         }
-        else if (mon_can_pass(mon, pref_y[2], pref_x[2]))
+        else if (mptr->can_pass(pref_pos[2]))
         {
-            pref_y[0] = pref_y[2];
-            pref_x[0] = pref_x[2];
+            pref_pos[0] = pref_pos[2];
         }
         else
         {
-            pref_y[0] = monsters[mon].y;
-            pref_x[0] = monsters[mon].x;
+            pref_pos[0] = mptr->pos;
         }
     }
 }
@@ -238,46 +186,43 @@ static void get_chase_prefs(int mon, int *pref_y, int *pref_x)
  * AI monster to move to.
  */
 
-static void get_seeking_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x)
+static void get_seeking_prefs(Coord loc, Offset delta, Coord *pref_pos)
 {
     struct ai_cell ai_cells[8];
     int i;
-    int ady, adx;
-    int j;
     int highest_score = AI_REALLY_HATE;
     int tryct;
-    *pref_y = y;
-    *pref_x = x;
-    ady = myabs(dy);
-    adx = myabs(dx);
-    build_ai_cells(ai_cells, y, x);
+    Mon *mptr = monsters + lvl.mon_at(loc);
+    *pref_pos = loc;
+    build_ai_cells(ai_cells, loc);
     for (i = 0; i < 8; i++)
     {
-        ai_cells[i].dy = u.y - ai_cells[i].y;
-        ai_cells[i].dx = u.x - ai_cells[i].x;
+        ai_cells[i].delta = u.pos.delta(ai_cells[i].pos);
         /* Scoring factors:
          *   Square closer to player: +1
          *   Square further from player: -1
          */
-        if (!mon_can_pass(lvl.mons[y][x], ai_cells[i].y, ai_cells[i].x))
+        if (!mptr->can_pass(ai_cells[i].pos))
         {
             /* Square impassable to this monster. Set score WAY
              * out of bounds and continue. */
             ai_cells[i].score = AI_REALLY_HATE;
             continue;
         }
-        if ((ai_cells[i].y == u.y) || (ai_cells[i].x == u.x))
+        if (ai_cells[i].delta.rcardinal())
         {
             ai_cells[i].score += 1;
         }
-        j = ai_cell_compare(ai_cells + i, dy, dx);
-        if (j > 0)
+        switch (ai_cell_compare(ai_cells + i, delta))
         {
+        case Greater:
             ai_cells[i].score -= 1;
-        }
-        else if (j < 0)
-        {
+            break;
+        case Lesser:
             ai_cells[i].score += 1;
+            break;
+        default:
+            break;
         }
         if (ai_cells[i].score > highest_score)
         {
@@ -294,8 +239,7 @@ static void get_seeking_prefs(int y, int x, int dy, int dx, int *pref_y, int *pr
         i = zero_die(8);
         if (ai_cells[i].score == highest_score)
         {
-            *pref_y = ai_cells[i].y;
-            *pref_x = ai_cells[i].x;
+            *pref_pos = ai_cells[i].pos;
             break;
         }
     }
@@ -309,68 +253,53 @@ static void get_seeking_prefs(int y, int x, int dy, int dx, int *pref_y, int *pr
  * #3.
  */
 
-static void get_naive_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x)
+static void get_naive_prefs(Coord loc, Offset delta, Coord *pref_pos)
 {
-    int sy, sx;
-    int ady, adx;
-    sy = mysign(dy);
-    ady = myabs(dy);
-    sx = mysign(dx);
-    adx = myabs(dx);
-    if (!sy)
+    Offset step = mysign(delta);
+    Offset absdelta = myabs(delta);
+    pref_pos[0] = loc + step;
+    if (!step.y)
     {
         /* We're on the horizontal; check the horizontally adjacent
          * square, then the squares one square north or south in a
          * random order. */
-        pref_y[0] = y;
         if (zero_die(2))
         {
-            pref_y[1] = y - 1;
-            pref_y[2] = y + 1;
+            pref_pos[1] = pref_pos[0] + North;
+            pref_pos[2] = pref_pos[0] + South;
         }
         else
         {
-            pref_y[1] = y + 1;
-            pref_y[2] = y - 1;
+            pref_pos[2] = pref_pos[0] + North;
+            pref_pos[1] = pref_pos[0] + South;
         }
-        pref_x[0] = x + sx;
-        pref_x[1] = x + sx;
-        pref_x[2] = x + sx;
     }
-    else if (!sx)
+    else if (!step.x)
     {
-        pref_x[0] = x;
         if (zero_die(2))
         {
-            pref_x[1] = x - 1;
-            pref_x[2] = x + 1;
+            pref_pos[1] = pref_pos[0] + West;
+            pref_pos[2] = pref_pos[0] + East;
         }
         else
         {
-            pref_x[1] = x + 1;
-            pref_x[2] = x - 1;
+            pref_pos[2] = pref_pos[0] + West;
+            pref_pos[1] = pref_pos[0] + East;
         }
-        pref_y[0] = y + sy;
-        pref_y[1] = y + sy;
-        pref_y[2] = y + sy;
     }
     else
     {
-        pref_x[0] = x + sx;
-        pref_y[0] = y + sy;
+        pref_pos[1] = pref_pos[0];
+        pref_pos[2] = pref_pos[0];
         if (zero_die(2))
         {
-            pref_x[1] = x;
-            pref_y[1] = y + sy;
-            pref_x[2] = x + sx;
-            pref_y[2] = y;
+            pref_pos[1].x = loc.x;
+            pref_pos[2].y = loc.y;
         }
         else
         {
-            pref_x[2] = x;
-            pref_y[2] = y + sy;
-            pref_x[1] = x + sx;
-            pref_y[1] = y;
+            pref_pos[2].x = loc.x;
+            pref_pos[1].y = loc.y;
         }
     }
 }
@@ -380,7 +309,7 @@ static void get_naive_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref
  * Populate array of eight AI cell descriptors.
  */
 
-static void build_ai_cells(struct ai_cell *cells, int y, int x)
+static void build_ai_cells(struct ai_cell *cells, Coord loc)
 {
     cells[0].score = 0;
     cells[1].score = 0;
@@ -390,32 +319,24 @@ static void build_ai_cells(struct ai_cell *cells, int y, int x)
     cells[5].score = 0;
     cells[6].score = 0;
     cells[7].score = 0;
-    cells[0].y = y - 1;
-    cells[1].y = y - 1;
-    cells[2].y = y - 1;
-    cells[3].y = y;
-    cells[4].y = y;
-    cells[5].y = y + 1;
-    cells[6].y = y + 1;
-    cells[7].y = y + 1;
-    cells[0].x = x - 1;
-    cells[1].x = x;
-    cells[2].x = x + 1;
-    cells[3].x = x - 1;
-    cells[4].x = x + 1;
-    cells[5].x = x - 1;
-    cells[6].x = x;
-    cells[7].x = x + 1;
+    cells[0].pos = loc + Northwest;
+    cells[1].pos = loc + North;
+    cells[2].pos = loc + Northeast;
+    cells[3].pos = loc + East;
+    cells[4].pos = loc + West;
+    cells[5].pos = loc + Southwest;
+    cells[6].pos = loc + South;
+    cells[7].pos = loc + Southeast;
 }
 
 /* XXX ai_cell_compare()
  *
  * Find relative range of cell compared to monster's current range.
  */
-static Comparison ai_cell_compare(struct ai_cell *cell, int dy, int dx)
+static Comparison ai_cell_compare(struct ai_cell *cell, Offset delta)
 {
-    int pointrange = convert_range(dy, dx);
-    int cellrange = convert_range(cell->dy, cell->dx);
+    int pointrange = delta.len_cheb();
+    int cellrange = cell->delta.len_cheb();
     if (cellrange < pointrange)
     {
         return Lesser;
@@ -431,7 +352,7 @@ static Comparison ai_cell_compare(struct ai_cell *cell, int dy, int dx)
  *
  * Get preferences for "smart" monsters without ranged attacks.
  */
-static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pref_x)
+static void get_dodger_prefs(Coord loc, Offset delta, Coord *pref_pos)
 {
     /* "Dodgers" are smart melee-only monsters. They will try to avoid
      * the cardinals as they close, and will even flow around other
@@ -443,20 +364,16 @@ static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pre
      */
     struct ai_cell ai_cells[8];
     int i;
-    int ady, adx;
-    int j;
+    Offset absdelta = myabs(delta);
     int highest_score = AI_REALLY_HATE;
     int tryct;
-    *pref_y = y;
-    *pref_x = x;
-    ady = myabs(dy);
-    adx = myabs(dx);
-    build_ai_cells(ai_cells, y, x);
+    Mon *mptr = monsters + lvl.mon_at(loc);
+    *pref_pos = loc;
+    build_ai_cells(ai_cells, loc);
     /* Build the local dx/dy arrays. */
     for (i = 0; i < 8; i++)
     {
-        ai_cells[i].dy = u.y - ai_cells[i].y;
-        ai_cells[i].dx = u.x - ai_cells[i].x;
+        ai_cells[i].delta = u.pos.delta(ai_cells[i].pos);
         /* Scoring factors:
          * Square on cardinal: -2.
          * Square closer to player: +1.
@@ -469,7 +386,7 @@ static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pre
          * closing. When I add more AI state to the monster structure,
          * this will change.
          */
-        if (!mon_can_pass(lvl.mons[y][x], ai_cells[i].y, ai_cells[i].x))
+        if (!mptr->can_pass(ai_cells[i].pos))
         {
             /* Square impassable. Set score WAY out of bounds
              * and continue. */
@@ -477,25 +394,27 @@ static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pre
             continue;
         }
         /* Cardinality */
-        if ((ai_cells[i].dy == ai_cells[i].dx) || (ai_cells[i].dy == -ai_cells[i].dx) || (ai_cells[i].dy == 0) || (ai_cells[i].dx == 0))
+        if (ai_cells[i].delta.rcardinal())
         {
             /* Score this square down for being on a cardinal. */
             ai_cells[i].score -= 2;
         }
-        j = ai_cell_compare(ai_cells + i, dy, dx);
         /* Range */
-        if ((ai_cells[i].dy < 2) && (ai_cells[i].dy > -2) && (ai_cells[i].dx < 2) && (ai_cells[i].dx > -2))
+        if (ai_cells[i].delta.len_cheb() < 2)
         {
             /* Score upward a *lot* for being adjacent to player */
             ai_cells[i].score += 10;
         }
-        else if (j > 0)
+        switch (ai_cell_compare(ai_cells + i, delta))
         {
+        case Greater:
             ai_cells[i].score -= 3;
-        }
-        else if (j < 0)
-        {
+            break;
+        case Lesser:
             ai_cells[i].score += 1;
+            break;
+        default:
+            break;
         }
         if (ai_cells[i].score > highest_score)
         {
@@ -512,197 +431,180 @@ static void get_dodger_prefs(int y, int x, int dy, int dx, int *pref_y, int *pre
         i = zero_die(8);
         if (ai_cells[i].score == highest_score)
         {
-            *pref_y = ai_cells[i].y;
-            *pref_x = ai_cells[i].x;
+            *pref_pos = ai_cells[i].pos;
             break;
         }
     }
     return;
 }
 
-void select_space(int *py, int *px, int dy, int dx, int selection_mode)
+void select_space(Coord *pc, Offset delta, int selection_mode)
 {
-    int ai_y[3];
-    int ai_x[3];
-    int ady, adx;
-    int y, x;
-    int sy, sx;
-    sy = mysign(dy);
-    ady = myabs(dy);
-    sx = mysign(dx);
-    adx = myabs(dx);
+    Coord ai_pos[3];
+    Offset absdelta = myabs(delta);
+    Coord c;
+    Offset step = mysign(delta);
+    Mon *mptr = monsters + lvl.mon_at(*pc);
     switch (selection_mode)
     {
-    case 0:
+    case AI_charger:
         /* Simple convergence */
-        get_naive_prefs(*py, *px, dy, dx, ai_y, ai_x);
-        if (mon_can_pass(lvl.mons[*py][*px], ai_y[0], ai_x[0]))
+        get_naive_prefs(*pc, delta, ai_pos);
+        if (mptr->can_pass(ai_pos[0]))
         {
-            y = ai_y[0];
-            x = ai_x[0];
+            c = ai_pos[0];
         }
-        else if (mon_can_pass(lvl.mons[*py][*px], ai_y[1], ai_x[1]))
+        else if (mptr->can_pass(ai_pos[1]))
         {
-            y = ai_y[1];
-            x = ai_x[1];
+            c = ai_pos[1];
         }
-        else if (mon_can_pass(lvl.mons[*py][*px], ai_y[2], ai_x[2]))
+        else if (mptr->can_pass(ai_pos[2]))
         {
-            y = ai_y[2];
-            x = ai_x[2];
+            c = ai_pos[2];
         }
         else
         {
-            y = *py;
-            x = *px;
+            c = *pc;
         }
         break;
-    case 1:
+    case AI_archer:
         /* Converge to cardinal */
-        if ((dy == dx) || (dy == -dx) || (!dy) || (!dx))
+        if (delta.rcardinal())
         {
             /* On cardinal. Stay there if we can. But close anyway. */
-            x = *px + sx;
-            y = *py + sy;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c = *pc + step;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            x = *px;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.x = pc->x;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            y = *py;
-            x = *px + sx;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.y = pc->y;
+            c.x = pc->x + step.x;
+            if (mptr->can_pass(c))
             {
                 break;
             }
         }
-        else if ((ady == 1) || ((adx > 1) && (ady > adx)))
+        else if ((absdelta.y == 1) || ((absdelta.x > 1) && (absdelta.y > absdelta.x)))
         {
-            /* One step in ydir off a NSEW cardinal, or further
+            /* One step in ydir off aEW cardinal, or further
              * off cardinal in y than in x */
-            y = *py + sy;
-            x = *px;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.y = pc->y + step.y;
+            c.x = pc->x;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            x = *px + sx;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.x = pc->x + step.x;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            y = *py;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.y = pc->y;
+            if (mptr->can_pass(c))
             {
                 break;
             }
         }
-        else if ((adx == 1) || ((ady > 1) && (adx > ady)))
+        else if ((absdelta.x == 1) || ((absdelta.y > 1) && (absdelta.x > absdelta.y)))
         {
-            /* One step off a diagonal cardinal, with adx > ady */
-            x = *px + sx;
-            y = *py;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            /* One step in xdir off an NS cardinal, with adx > ady */
+            c.x = pc->x + step.x;
+            c.y = pc->y;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            y = *py + sy;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.y = pc->y + step.y;
+            if (mptr->can_pass(c))
             {
                 break;
             }
-            x = *px;
-            if (mon_can_pass(lvl.mons[*py][*px], y, x))
+            c.x = pc->x;
+            if (mptr->can_pass(c))
             {
                 break;
             }
         }
-        y = *py;
-        x = *px;
+        c = *pc;
         break;
-    case 2:
-        get_dodger_prefs(*py, *px, dy, dx, ai_y, ai_x);
-        y = ai_y[0];
-        x = ai_x[0];
+    case AI_dodger:
+        get_dodger_prefs(*pc, delta, ai_pos);
+        c = ai_pos[0];
         break;
-    case 3:
+    case AI_drunk:
         /* "Drunk" monster i.e. monster moving while it doesn't know
          * how to find you. */
-        get_drunk_prefs(*py, *px, dy, dx, ai_y, ai_x);
-        if (mon_can_pass(lvl.mons[*py][*px], ai_y[0], ai_x[0]))
+        get_drunk_prefs(*pc, delta, ai_pos);
+        if (mptr->can_pass(ai_pos[0]))
         {
-            y = ai_y[0];
-            x = ai_x[0];
+            c = ai_pos[0];
         }
-        else if (mon_can_pass(lvl.mons[*py][*px], ai_y[1], ai_x[1]))
+        else if (mptr->can_pass(ai_pos[1]))
         {
-            y = ai_y[1];
-            x = ai_x[1];
+            c = ai_pos[1];
         }
-        else if (mon_can_pass(lvl.mons[*py][*px], ai_y[2], ai_x[2]))
+        else if (mptr->can_pass(ai_pos[2]))
         {
-            y = ai_y[2];
-            x = ai_x[2];
+            c = ai_pos[2];
         }
         else
         {
-            y = *py;
-            x = *px;
+            c = *pc;
         }
         break;
-    case 4:
+    case AI_seeker:
         /* "Seeking" monster i.e. monster moving while it can't see
          * you, but thinks it knows where you are. This AI isn't
          * great, but it'll do for now. */
-        get_seeking_prefs(*py, *px, dy, dx, ai_y, ai_x);
-        y = ai_y[0];
-        x = ai_x[0];
+        get_seeking_prefs(*pc, delta, ai_pos);
+        c = ai_pos[0];
         break;
-    case 5:
+    case AI_chaser:
         /* "chase" AI i.e. pursue your last known position. */
-        get_chase_prefs(lvl.mons[*py][*px], ai_y, ai_x);
-        y = ai_y[0];
-        x = ai_x[0];
+        get_chase_prefs(mptr - monsters, ai_pos);
+        c = ai_pos[0];
         break;
     }
-    *py = y;
-    *px = x;
+    *pc = c;
 }
 
 void mon_acts(int mon)
 {
     Mon *mptr;
-    int dy, dx;
-    int y, x;
-    int sy, sx;
-    int meleerange;
-    int oncardinal;
-    int special_used = false;
+    Offset delta;
+    Coord c;
+    Offset step;
+    bool meleerange;
+    bool oncardinal;
+    bool special_used = false;
     mptr = monsters + mon;
     /* dy,dx == direction monster must go to reach you. */
-    y = mptr->y;
-    x = mptr->x;
-    compute_directions(u.y, u.x, y, x, &dy, &dx, &sy, &sx, &meleerange, &oncardinal);
-    if ((dy == 0) && (dx == 0))
+    c = mptr->pos;
+    delta = u.pos.delta(c);
+    step = mysign(delta);
+    meleerange = delta.len_cheb() < 2;
+    oncardinal = delta.rcardinal();
+    if (delta.len_cheb() == 0)
     {
         print_msg("Program disordered: monster in player's square.\n");
         print_msg("Discarding misplaced monster.\n");
         mptr->used = false;
-        lvl.mons[y][x] = NO_MON;
+        lvl.set_mon_at(c, NO_MON);
         return;
     }
-    if (lvl.mons[y][x] != mon)
+    if (lvl.mon_at(c) != mon)
     {
         print_msg("Program disordered: monster(s) misplaced.\n");
         mptr->used = false;
-        if (lvl.mons[y][x] != NO_MON)
+        if (lvl.mon_at(c) != NO_MON)
         {
-            monsters[lvl.mons[y][x]].used = false;
-            lvl.mons[y][x] = NO_MON;
+            monsters[lvl.mon_at(c)].used = false;
+            lvl.set_mon_at(c, NO_MON);
         }
         return;
     }
@@ -710,15 +612,21 @@ void mon_acts(int mon)
     {
         mptr->awake = true;
     }
-    if (meleerange)
+    if (!mptr->awake)
+    {
+        return;
+    }
+    else if (meleerange)
     {
-        /* Adjacent! Attack you.  Demons have a 1 in 10 chance of
-         * attempting to summon another demon instead of attacking
-         * you. */
-        if ((mptr->mon_id == PM_DEMON) && !zero_die(10))
+        /* Adjacent! Attack you.  Demons have a 1 in 10 chance of attempting to
+         * summon another demon instead of attacking you, if that individual
+         * demon has not summoned in the last 100 ticks. */
+        if ((mptr->mon_id == PM_DEMON) && (mptr->next_summon < game_tick) &&
+            !zero_die(10))
         {
-            summon_demon_near(y, x);
-            special_used = 1;
+            summon_demon_near(c);
+            mptr->next_summon = game_tick + 100;
+            special_used = true;
         }
         else if (pmon_is_magician(mptr->mon_id))
         {
@@ -745,7 +653,7 @@ void mon_acts(int mon)
             }
             /* Didn't, or couldn't, use sorcery; converge
              * as if an archer. */
-            select_space(&y, &x, dy, dx, 1);
+            select_space(&c, delta, AI_archer);
         }
         else if (pmon_is_archer(mptr->mon_id))
         {
@@ -758,26 +666,22 @@ void mon_acts(int mon)
             {
                 return;
             }
-            select_space(&y, &x, dy, dx, 1);
+            select_space(&c, delta, AI_archer);
         }
         else if (pmon_is_smart(mptr->mon_id))
         {
-            select_space(&y, &x, dy, dx, 2);
+            select_space(&c, delta, AI_dodger);
         }
         else /* pmon_is_stupid() */
         {
-            select_space(&y, &x, dy, dx, 0);
+            select_space(&c, delta, AI_charger);
         }
-        if ((y != mptr->y) || (x != mptr->x))
+        if (c != mptr->pos)
         {
             /* We decided to move; move! */
-            move_mon(mon, y, x);
+            move_mon(mon, c);
         }
     }
-    else if (!mptr->awake)
-    {
-        return;
-    }
     else
     {
         /* Out of LOS, but awake. Stupid monsters move "drunkenly"; smart
@@ -795,33 +699,30 @@ void mon_acts(int mon)
         }
         if (pmon_is_smart(mptr->mon_id))
         {
-            select_space(&y, &x, dy, dx, 4);
+            select_space(&c, delta, AI_seeker);
         }
-        else if (pmon_is_stupid(mptr->mon_id) || (mptr->ai_lasty == -1))
+        else if (pmon_is_stupid(mptr->mon_id) || (mptr->ai_lastpos == Nowhere))
         {
-            select_space(&y, &x, dy, dx, 3);
+            select_space(&c, delta, AI_drunk);
         }
         else
         {
-            select_space(&y, &x, dy, dx, 5);
+            select_space(&c, delta, AI_chaser);
         }
-        if ((y != mptr->y) || (x != mptr->x))
+        if (c != mptr->pos)
         {
             /* We decided to move; move! */
-            move_mon(mon, y, x);
+            move_mon(mon, c);
         }
     }
     /* Let's get the data again. */
-    compute_directions(u.y, u.x, y, x, &dy, &dx, &sy, &sx, &meleerange, &oncardinal);
-    if ((dy >= -10) && (dy <= 10) && (dx >= -10) && (dx >= 10))
+    if (u.pos.dist_cheb(c) <= MAX_FOV_RADIUS)
     {
-        mptr->ai_lasty = u.y;
-        mptr->ai_lastx = u.x;
+        mptr->ai_lastpos = u.pos;
     }
-    else if (mptr->ai_lasty != -1)
+    else
     {
-        mptr->ai_lasty = -1;
-        mptr->ai_lastx = -1;
+        mptr->ai_lastpos = Nowhere;
     }
 }
 
index c65b731..b100656 100644 (file)
@@ -38,11 +38,11 @@ static int reject_mon(int pm);
  *  \return Number of monsters summoned
  *  \param how_many Maximum number of monsters to summon
  */
-int summoning(int y, int x, int how_many)
+int summoning(Coord c, int how_many)
 {
     int i;
-    int dy;
-    int dx;
+    Offset delta;
+    Coord testpos;
     int tryct;
     int mon;
     int created = 0;
@@ -51,11 +51,11 @@ int summoning(int y, int x, int how_many)
     {
         for (tryct = 0; tryct < 20; tryct++)
         {
-            dy = zero_die(3) - 1;
-            dx = zero_die(3) - 1;
-            if ((lvl.terrain[y + dy][x + dx] == FLOOR) &&
-                (lvl.mons[y + dy][x + dx] == NO_MON) &&
-                ((y + dy != u.y) || (x + dx != u.x)))
+            delta = random_step();
+            testpos = c + delta;
+            if ((lvl.terrain_at(testpos) == FLOOR) &&
+                (lvl.mon_at(testpos) == NO_MON) &&
+                (testpos != u.pos))
             {
                 pmon = get_random_pmon();
                 if (pmon_is_magician(pmon))
@@ -63,7 +63,7 @@ int summoning(int y, int x, int how_many)
                     /* Never summon magicians! */
                     continue;
                 }
-                mon = create_mon(NO_PMON, y + dy, x + dx);
+                mon = create_mon(NO_PMON, testpos);
                 if (mon != NO_MON)
                 {
                     created++;
@@ -102,12 +102,12 @@ int get_random_pmon(void)
     return pm;
 }
 
-int create_mon(int pm_idx, int y, int x)
+int create_mon(int pm_idx, Coord c)
 {
     int mon;
-    if (lvl.mons[y][x] != NO_MON)
+    if (lvl.mon_at(c) != NO_MON)
     {
-        print_msg("Attempt to create monster at occupied space %d %d\n", y, x);
+        print_msg("Attempt to create monster at occupied space %d %d\n", c.y, c.x);
         return NO_MON;
     }
     if (pm_idx == NO_PMON)
@@ -124,10 +124,8 @@ int create_mon(int pm_idx, int y, int x)
         {
             monsters[mon].mon_id = pm_idx;
             monsters[mon].used = true;
-            monsters[mon].y = y;
-            monsters[mon].x = x;
-            monsters[mon].ai_lasty = -1;
-            monsters[mon].ai_lastx = -1;
+            monsters[mon].pos = c;
+            monsters[mon].ai_lastpos = Nowhere;
             monsters[mon].hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1);
             monsters[mon].hpcur = monsters[mon].hpmax;
             monsters[mon].mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3);
@@ -144,8 +142,8 @@ int create_mon(int pm_idx, int y, int x)
                 monsters[mon].rdam = NO_ATK;
             }
             monsters[mon].awake = false;
-            lvl.mons[y][x] = mon;
-            newsym(y, x);
+            lvl.set_mon_at(c, mon);
+            newsym(c);
             return mon;
         }
     }
@@ -154,18 +152,17 @@ int create_mon(int pm_idx, int y, int x)
 
 void death_drop(int mon)
 {
-    int pm = monsters[mon].mon_id;
-    int y = monsters[mon].y;
-    int x = monsters[mon].x;
-    int dy, dx;
+    Mon *mptr = monsters + mon;
+    int pm = mptr->mon_id;
+    Coord c = mptr->pos;
+    Offset delta;
     int tryct;
-    while (((lvl.objs[y][x] != NO_OBJ) || (lvl.terrain[y][x] != FLOOR)) && tryct < 100)
+    while (((lvl.obj_at(c) != NO_OBJ) || (lvl.terrain_at(c) != FLOOR)) &&
+           (tryct < 100))
     {
-        dy = zero_die(3) - 1;
-        dx = zero_die(3) - 1;
+        delta = random_step();
         tryct++;
-        y += dy;
-        x += dx;
+        c += delta;
     }
     if (tryct >= 100)
     {
@@ -176,58 +173,58 @@ void death_drop(int mon)
     case PM_GOBLIN:
         if (!zero_die(4))
         {
-            create_obj(PO_DAGGER, 1, 0, y, x);
+            create_obj(PO_DAGGER, 1, 0, c);
         }
         break;
     case PM_THUG:
     case PM_GOON:
         if (!zero_die(4))
         {
-            create_obj(PO_MACE, 1, 0, y, x);
+            create_obj(PO_MACE, 1, 0, c);
         }
         else if (!zero_die(3))
         {
-            create_obj(PO_LEATHER_ARMOUR, 1, 0, y, x);
+            create_obj(PO_LEATHER_ARMOUR, 1, 0, c);
         }
         break;
     case PM_HUNTER:
         if (!zero_die(6))
         {
-            create_obj(PO_BOW, 1, 0, y, x);
+            create_obj(PO_BOW, 1, 0, c);
         }
         break;
     case PM_DUELLIST:
         if (!zero_die(6))
         {
-            create_obj(PO_LONG_SWORD, 1, 0, y, x);
+            create_obj(PO_LONG_SWORD, 1, 0, c);
         }
         break;
     case PM_WIZARD:
         if (!zero_die(4))
         {
-            create_obj_class(POCLASS_SCROLL, 1, 0, y, x);
+            create_obj_class(POCLASS_SCROLL, 1, 0, c);
         }
         else if (!zero_die(3))
         {
-            create_obj_class(POCLASS_POTION, 1, 0, y, x);
+            create_obj_class(POCLASS_POTION, 1, 0, c);
         }
         break;
     case PM_WARLORD:
         if (!zero_die(3))
         {
-            create_obj(PO_RUNESWORD, 1, 0, y, x);
+            create_obj(PO_RUNESWORD, 1, 0, c);
         }
         break;
     case PM_DEMON:
         if (!zero_die(100))
         {
-            create_obj(PO_DEVIL_SPLEEN, 1, 0, y, x);
+            create_obj(PO_DEVIL_SPLEEN, 1, 0, c);
         }
         break;
     case PM_DEFILER:
         if (!zero_die(50))
         {
-            create_obj(PO_DEVIL_SPLEEN, 1, 0, y, x);
+            create_obj(PO_DEVIL_SPLEEN, 1, 0, c);
         }
         break;
     default:
@@ -236,40 +233,40 @@ void death_drop(int mon)
     map_updated = 1;
 }
 
-bool mon_can_pass(int mon, int y, int x)
+bool Mon::can_pass(Coord c) const
 {
     Terrain terr;
-    if ((y < 0) || (x < 0) || (y >= DUN_HEIGHT) || (x >= DUN_WIDTH))
+    if ((c.y < 0) || (c.x < 0) || (c.y >= DUN_HEIGHT) || (c.x >= DUN_WIDTH))
     {
         return false;
     }
-    if (lvl.mons[y][x] != NO_MON)
+    if (lvl.mon_at(c) != NO_MON)
     {
         return false;
     }
-    if ((y == u.y) && (x == u.x))
+    if (c == u.pos)
     {
         /* Sanity check! */
         return false;
     }
-    if (mon_is_ethereal(mon))
+    if (is_ethereal())
     {
         return true;
     }
-    terr = lvl.terrain[y][x];
+    terr = lvl.terrain_at(c);
     switch (terr)
     {
     case WALL:
     case HARDWALL:
         return false;
     case LAVA:
-        if (!mon_can_fly(mon) && !mon_resists_fire(mon))
+        if (!can_fly() && !resists(DT_FIRE))
         {
             return false;
         }
         break;
     case WATER:
-        if (!mon_can_fly(mon) && !mon_resists_drowning(mon))
+        if (!can_fly() && !resists(DT_DROWNING))
         {
             return false;
         }
@@ -346,8 +343,8 @@ void damage_mon(int mon, int amount, bool by_you)
             print_msg(" dies.\n");
         }
         death_drop(mon);
-        lvl.mons[mptr->y][mptr->x] = NO_MON;
-        newsym(mptr->y, mptr->x);
+        lvl.set_mon_at(mptr->pos, NO_MON);
+        newsym(mptr->pos);
         mptr->used = 0;
         map_updated = 1;
         display_update();
@@ -370,16 +367,15 @@ int reject_mon(int pm)
 Pass_fail teleport_mon_to_you(int mon)
 {
     int tryct;
-    int dy, dx;
-    int y, x;
+    Offset delta;
+    Coord c;
     int success = 0;
+    Mon *mptr = monsters + mon;
     for (tryct = 0; tryct < 40; tryct++)
     {
-        dy = zero_die(3) - 1;
-        dx = zero_die(3) - 1;
-        y = u.y + dy;
-        x = u.x + dx;
-        if (mon_can_pass(mon, y, x))
+        delta = random_step();
+        c = u.pos + delta;
+        if (mptr->can_pass(c))
         {
             success = 1;
             break;
@@ -387,7 +383,7 @@ Pass_fail teleport_mon_to_you(int mon)
     }
     if (success)
     {
-        reloc_mon(mon, y, x);
+        reloc_mon(mon, c);
         print_mon_name(mon, 2);
         print_msg(" appears in a puff of smoke.\n");
         return You_pass;
@@ -399,14 +395,14 @@ Pass_fail teleport_mon(int mon)
 {
     Pass_fail rval = You_fail;
     int cell_try;
-    int y, x;
+    Coord c;
     for (cell_try = 0; cell_try < 200; cell_try++)
     {
-        y = exclusive_flat(0, DUN_HEIGHT - 1);
-        x = exclusive_flat(0, DUN_WIDTH - 1);
-        if ((lvl.mons[y][x] == NO_MON) && (lvl.terrain[y][x] == FLOOR) && ((y != u.y) || (x != u.x)))
+        c.y = exclusive_flat(0, DUN_HEIGHT - 1);
+        c.x = exclusive_flat(0, DUN_WIDTH - 1);
+        if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
         {
-            reloc_mon(mon, y, x);
+            reloc_mon(mon, c);
             rval = You_pass;
             break;
         }
@@ -414,15 +410,14 @@ Pass_fail teleport_mon(int mon)
     return rval;
 }
 
-int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
+int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
 {
     /* 0 = blocked, 1 = knocked, 2 = killed */
     Mon *mptr = monsters + mon;
-    int y = mptr->y + sy;
-    int x = mptr->x + sx;
-    Terrain terr = lvl.terrain[y][x];
+    Coord c = mptr->pos + step;
+    Terrain terr = lvl.terrain_at(c);
 
-    if (mon_resists_knockback(mon))
+    if (mptr->resists(DT_KNOCKBACK))
     {
         if (cansee)
         {
@@ -439,7 +434,7 @@ int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
         print_msg(" is slammed against the wall.\n");
         return 0;
     case LAVA:
-        if (mon_can_fly(mon))
+        if (mptr->can_fly())
         {
             if (cansee)
             {
@@ -458,7 +453,7 @@ int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
             {
                 print_msg("Splut!\n");
             }
-            if (!mon_resists_fire(mon))
+            if (!mptr->resists(DT_FIRE))
             {
                 damage_mon(mon, 9999, by_you);
                 return 2;
@@ -466,7 +461,7 @@ int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
         }
         break;
     case WATER:
-        if (mon_can_fly(mon))
+        if (mptr->can_fly())
         {
             if (cansee)
             {
@@ -485,7 +480,7 @@ int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
             {
                 print_msg("Splash!\n");
             }
-            if (!mon_resists_drowning(mon))
+            if (!mptr->resists(DT_DROWNING))
             {
                 damage_mon(mon, 9999, by_you);
                 return 2;
@@ -495,50 +490,47 @@ int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you)
     default:
         break;
     }
-    reloc_mon(mon, mptr->y + sy, mptr->x + sx);
+    reloc_mon(mon, c);
     return 1;
 }
 
-void reloc_mon(int mon, int y, int x)
+void reloc_mon(int mon, Coord newpos)
 {
     Mon *mptr = monsters + mon;
-    lvl.mons[mptr->y][mptr->x] = NO_MON;
-    newsym(mptr->y, mptr->x);
-    mptr->y = y;
-    mptr->x = x;
-    lvl.mons[mptr->y][mptr->x] = mon;
-    newsym(mptr->y, mptr->x);
+    lvl.set_mon_at(mptr->pos, NO_MON);
+    newsym(mptr->pos);
+    mptr->pos = newpos;
+    lvl.set_mon_at(mptr->pos, mon);
+    newsym(mptr->pos);
 }
 
-void move_mon(int mon, int y, int x)
+void move_mon(int mon, Coord c)
 {
     Mon *mptr;
-    if (!mon_can_pass(mon, y, x))
+    mptr = monsters + mon;
+    if (!mptr->can_pass(c))
     {
         print_msg("Warning: monster attempted an invalid move.\n");
         return;
     }
-    mptr = monsters + mon;
-    if (lvl.mons[mptr->y][mptr->x] != mon)
+    if (lvl.mon_at(mptr->pos) != mon)
     {
         print_msg("Monster map array in disorder.\n");
         press_enter();
         return;
     }
-    reloc_mon(mon, y, x);
+    reloc_mon(mon, c);
     display_update();
 }
 
-void summon_demon_near(int y, int x)
+void summon_demon_near(Coord c)
 {
-    int y2, x2;
+    Coord c2 = c + random_step();
     int i;
-    y2 = y - 1 + zero_die(3);
-    x2 = x - 1 + zero_die(3);
-    if ((lvl.terrain[y2][x2] == FLOOR) && (lvl.mons[y2][x2] == NO_MON) &&
-        ((y2 != u.y) || (x2 != u.x)))
+    if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) &&
+        (c2 != u.pos))
     {
-        i = create_mon(PM_DEMON, y2, x2);
+        i = create_mon(PM_DEMON, c2);
         if (i != NO_MON)
         {
             print_msg("Another demon appears!\n");
@@ -552,16 +544,15 @@ void summon_demon_near(int y, int x)
 
 bool mon_visible(int mon)
 {
-    int dy, dx;
+    Offset delta;
     if (monsters[mon].used == 0)
     {
         return false;
     }
-    dy = monsters[mon].y - u.y;
-    dx = monsters[mon].x - u.x;
-    if (((dy > -11) && (dy < 11) && (dx > -11) && (dx < 11)))
+    delta = monsters[mon].pos.delta(u.pos);
+    if (delta.len_cheb() <= MAX_FOV_RADIUS)
     {
-        return player_fov.affected[MAX_FOV_RADIUS + dy][MAX_FOV_RADIUS + dx];
+        return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central;
     }
     else
     {
@@ -602,49 +593,38 @@ void update_mon(int mon)
     }
 }
 
-bool mon_resists_cold(int mon)
-{
-    return pmon_resists_cold(monsters[mon].mon_id);
-}
-
-bool mon_resists_fire(int mon)
-{
-    return pmon_resists_fire(monsters[mon].mon_id);
-}
-
-bool mon_resists_poison(int mon)
-{
-    return pmon_resists_poison(monsters[mon].mon_id);
-}
-
-bool mon_resists_necro(int mon)
-{
-    return pmon_resists_necro(monsters[mon].mon_id);
-}
-
-bool mon_resists_elec(int mon)
-{
-    return pmon_resists_elec(monsters[mon].mon_id);
-}
-
-bool mon_can_fly(int mon)
+bool Mon::resists(Damtyp dt) const
 {
-    return pmon_can_fly(monsters[mon].mon_id);
+    switch (dt)
+    {
+    case DT_COLD:
+        return pmon_resists_cold(mon_id);
+    case DT_FIRE:
+        return pmon_resists_fire(mon_id);
+    case DT_POISON:
+        return pmon_resists_poison(mon_id);
+    case DT_NECRO:
+        return pmon_resists_necro(mon_id);
+    case DT_ELEC:
+        return pmon_resists_elec(mon_id);
+    case DT_KNOCKBACK:
+        return pmon_resists_knockback(mon_id);
+    case DT_DROWNING:
+        return pmon_resists_drowning(mon_id);
+    default:
+        return false;
+    }
 }
 
-bool mon_is_ethereal(int mon)
+bool Mon::is_ethereal(void) const
 {
-    return pmon_is_ethereal(monsters[mon].mon_id);
+    return pmon_is_ethereal(mon_id);
 }
 
-bool mon_resists_drowning(int mon)
+bool Mon::can_fly(void) const
 {
-    return pmon_resists_drowning(monsters[mon].mon_id);
+    return pmon_can_fly(mon_id);
 }
 
-bool mon_resists_knockback(int mon)
-{
-    return pmon_resists_knockback(monsters[mon].mon_id);
-}
 /* monsters.c */
 // vim:cindent
index 5b1df9c..54256db 100644 (file)
@@ -85,14 +85,16 @@ extern struct permon permons[NUM_OF_PERMONS];
 #define NO_ATK (-1)
 #define NO_PMON (-1)
 
+enum AI_mode {
+    AI_charger, AI_archer, AI_dodger, AI_drunk, AI_seeker, AI_chaser
+};
+
 /* XXX struct mon */
 #define MONSTERS_IN_PLAY 100
 struct Mon {
     int mon_id;
-    int y;
-    int x;
-    int ai_lasty;       /* AI's belief about your last position. -1 == lost you. */
-    int ai_lastx;       /* AI's belief about your last position. -1 == lost you. */
+    Coord pos;
+    Coord ai_lastpos;
     bool used;
     int hpmax;  /* Improved by OOD rating at 1:1. */
     int hpcur;  /* <= 0 is dead. */
@@ -104,6 +106,9 @@ struct Mon {
     bool awake;
     int next_summon;
     bool resists(Damtyp dt) const;
+    bool can_pass(Coord c) const;
+    bool can_fly(void) const;
+    bool is_ethereal(void) const;
 };
 extern Mon monsters[MONSTERS_IN_PLAY];
 
@@ -115,29 +120,19 @@ extern void update_mon(int mon);
 extern void mon_acts(int mon);
 extern void death_drop(int mon);
 extern void print_mon_name(int mon, int article);
-extern void summon_demon_near(int y, int x);
-extern int create_mon(int pm_idx, int y, int x);
-extern int summoning(int y, int x, int how_many);
+extern void summon_demon_near(Coord  c);
+extern int create_mon(int pm_idx, Coord c);
+extern int summoning(Coord c, int how_many);
 extern int ood(int power, int ratio);
 extern int get_random_pmon(void);
 extern void damage_mon(int mon, int amount, bool by_you);
-extern bool mon_can_pass(int mon, int y, int x);
 extern bool mon_visible(int mon);
-extern int knockback_mon(int mon, int sy, int sx, bool cansee, bool by_you);
-extern void move_mon(int mon, int y, int x);
-extern void reloc_mon(int mon, int y, int x);
+extern int knockback_mon(int mon, Offset step, bool cansee, bool by_you);
+extern void move_mon(int mon, Coord c);
+extern void reloc_mon(int mon, Coord c);
 extern Pass_fail teleport_mon(int mon);       /* Randomly relocate monster. */
 extern Pass_fail teleport_mon_to_you(int mon);        /* Relocate monster to your vicinity. */
 extern void heal_mon(int mon, int amount, int cansee);
-extern bool mon_resists_cold(int mon);
-extern bool mon_resists_fire(int mon);
-extern bool mon_resists_poison(int mon);
-extern bool mon_resists_necro(int mon);
-extern bool mon_resists_elec(int mon);
-extern bool mon_resists_drowning(int mon);
-extern bool mon_can_fly(int mon);
-extern bool mon_is_ethereal(int mon);
-extern bool mon_resists_knockback(int mon);
 
 /* XXX mon2.c data and funcs */
 extern void select_space(int *py, int *px, int dy, int dx, int selection_mode);
index f539cfc..098ad72 100644 (file)
@@ -344,7 +344,7 @@ void flavours_init(void)
     permobjs[PO_POT_RESTORATION].power = colour_choices[3];
 }
 
-int create_obj_class(enum poclass_num po_class, int quantity, bool with_you, int y, int x)
+int create_obj_class(enum poclass_num po_class, int quantity, bool with_you, Coord c)
 {
     int obj;
     int po_idx;
@@ -414,7 +414,7 @@ int get_random_pobj(void)
     return po_idx;
 }
 
-int create_obj(int po_idx, int quantity, bool with_you, int y, int x)
+int create_obj(int po_idx, int quantity, bool with_you, Coord c)
 {
     int i;
     for (i = 0; i < 100; i++)
@@ -440,8 +440,7 @@ int create_obj(int po_idx, int quantity, bool with_you, int y, int x)
     objects[i].obj_id = po_idx;
     objects[i].with_you = with_you;
     objects[i].used = true;
-    objects[i].y = y;
-    objects[i].x = x;
+    objects[i].pos = c;
     objects[i].quan = quantity;
     switch (permobjs[po_idx].poclass)
     {
@@ -457,7 +456,7 @@ int create_obj(int po_idx, int quantity, bool with_you, int y, int x)
     }
     if (!objects[i].with_you)
     {
-        lvl.objs[y][x] = i;
+        lvl.set_obj_at(c, i);
     }
     return i;
 }
@@ -541,11 +540,10 @@ Action_cost drop_obj(int inv_idx)
 {
     Obj *optr;
     optr = objects + u.inventory[inv_idx];
-    if (lvl.objs[u.y][u.x] == NO_OBJ)
+    if (lvl.obj_at(u.pos) == NO_OBJ)
     {
-        optr->y = u.y;
-        optr->x = u.x;
-        lvl.objs[u.y][u.x] = u.inventory[inv_idx];
+        optr->pos = u.pos;
+        lvl.set_obj_at(u.pos, u.inventory[inv_idx]);
         if (u.weapon == u.inventory[inv_idx])
         {
             u.weapon = NO_OBJ;
@@ -553,7 +551,7 @@ Action_cost drop_obj(int inv_idx)
         u.inventory[inv_idx] = NO_OBJ;
         optr->with_you = false;
         print_msg("You drop ");
-        print_obj_name(lvl.objs[u.y][u.x]);
+        print_obj_name(lvl.obj_at(u.pos));
         print_msg(".\n");
         return Cost_std;
     }
@@ -581,19 +579,19 @@ void attempt_pickup(void)
 {
     int i;
     int stackable;
-    stackable = po_is_stackable(objects[lvl.objs[u.y][u.x]].obj_id);
+    stackable = po_is_stackable(objects[lvl.obj_at(u.pos)].obj_id);
     if (stackable)
     {
         for (i = 0; i < 19; i++)
         {
-            if ((objects[u.inventory[i]].obj_id == objects[lvl.objs[u.y][u.x]].obj_id))
+            if ((objects[u.inventory[i]].obj_id == objects[lvl.obj_at(u.pos)].obj_id))
             {
                 print_msg("You get ");
-                print_obj_name(lvl.objs[u.y][u.x]);
+                print_obj_name(lvl.obj_at(u.pos));
                 print_msg(".\nYou now have\n");
-                objects[u.inventory[i]].quan += objects[lvl.objs[u.y][u.x]].quan;
-                objects[lvl.objs[u.y][u.x]].used = false;
-                lvl.objs[u.y][u.x] = NO_OBJ;
+                objects[u.inventory[i]].quan += objects[lvl.obj_at(u.pos)].quan;
+                objects[lvl.obj_at(u.pos)].used = false;
+                lvl.set_obj_at(u.pos, NO_OBJ);
                 print_msg("%c) ", 'a' + i);
                 print_obj_name(u.inventory[i]);
                 print_msg("\n");
@@ -613,11 +611,10 @@ void attempt_pickup(void)
         print_msg("Your pack is full.\n");
         return;
     }
-    u.inventory[i] = lvl.objs[u.y][u.x];
-    lvl.objs[u.y][u.x] = NO_OBJ;
+    u.inventory[i] = lvl.obj_at(u.pos);
+    lvl.set_obj_at(u.pos, NO_OBJ);
     objects[u.inventory[i]].with_you = true;
-    objects[u.inventory[i]].x = -1;
-    objects[u.inventory[i]].y = -1;
+    objects[u.inventory[i]].pos = Nowhere;
     print_msg("You now have\n");
     print_msg("%c) ", 'a' + i);
     print_obj_name(u.inventory[i]);
@@ -769,29 +766,31 @@ Action_cost zap_weapon(void)
     case PO_STAFF_OF_FIRE:
         if (u.food > 150)
         {
-            int y, x;
+            Coord c;
             u.food -= 150;
             print_msg("You unleash the fiery powers of your staff!\n");
-            for (y = u.y - 1; y <= u.y + 1; ++y)
+            for (c.y = u.pos.y - 1; c.y <= u.pos.y + 1; ++c.y)
             {
-                if ((y < 0) || (y >= DUN_HEIGHT))
+                if ((c.y < 0) || (c.y >= DUN_HEIGHT))
                 {
                     continue;
                 }
-                for (x = u.x - 1; x <= u.x + 1; ++x)
+                for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
                 {
-                    if ((x < 0) || (x >= DUN_WIDTH))
+                    int mon;
+                    if ((c.x < 0) || (c.x >= DUN_WIDTH))
                     {
                         continue;
                     }
-                    if (lvl.mons[y][x] != NO_MON)
+                    mon = lvl.mon_at(c);
+                    if (mon != NO_MON)
                     {
-                        Mon *mptr = monsters + lvl.mons[y][x];
+                        Mon *mptr = monsters + mon;
                         if (!pmon_resists_fire(mptr->mon_id))
                         {
-                            print_mon_name(lvl.mons[y][x], 3);
+                            print_mon_name(mon, 3);
                             print_msg(" is engulfed in roaring flames.\n");
-                            damage_mon(lvl.mons[y][x], dice(4, 10), true);
+                            damage_mon(mon, dice(4, 10), true);
                         }
                     }
                 }
index f667fa9..cdaee32 100644 (file)
@@ -104,8 +104,7 @@ struct Obj {
     bool used;   /* Entry is occupied. */
     bool with_you;       /* Preserved when item DB is reaped on level change. */
     int quan;
-    int y;
-    int x;
+    Coord pos;
     int durability;     /* Weapons and armour degrade with use. */
 };
 extern Obj objects[OBJECTS_IN_PLAY];
@@ -118,10 +117,9 @@ extern void sprint_obj_name(char *s, int obj, int len);
 extern void fprint_obj_name(FILE *fp, int obj);
 extern void print_obj_name(int obj);
 extern void describe_object(int obj);
-extern int create_obj(int po_idx, int quantity, bool with_you, int y, int x);
+extern int create_obj(int po_idx, int quantity, bool with_you, Coord c);
 extern bool consume_obj(int obj);
-extern int create_obj_class(enum poclass_num pocl, int quantity, bool with_you, int y, int x);
-extern int create_obj_random(int y, int x);
+extern int create_obj_class(enum poclass_num pocl, int quantity, bool with_you, Coord c);
 
 extern Action_cost drop_obj(int inv_idx);
 extern Action_cost read_scroll(int obj);
index 7e8b35f..18c1b41 100644 (file)
@@ -2,7 +2,7 @@
  *  \brief evil magic used by monsters
  */
 
-/* Copyright 2005-2012 Martin Read
+/* Copyright 2005-2013 Martin Read
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -69,27 +69,18 @@ int mon_use_sorcery(int mon)
     /* Returns zero for no spell selected, -1 for unsupported spell
      * selected, 1 for supported spell selected. */
     Mon *mptr = monsters + mon;
-    int dy, dx;
-    int sy, sx;
+    Offset delta = u.pos.delta(mptr->pos);
+    Offset step = mysign(delta);
     enum monspell to_cast = MS_REJECT;
     int rval = 1;       /* Default to success; failure paths will force this
                          * to an appropriate value. */
+    int range = delta.len_cheb();
+    bool meleerange = (range < 2);
+    bool oncardinal = delta.rcardinal();
     int dieroll;
-    int meleerange;
-    int oncardinal;
+    bool cansee = mon_visible(mon);
     int i;
-    int cansee;
-    int range;
-    compute_directions(u.y, u.x, mptr->y, mptr->x, &dy, &dx, &sy, &sx, &meleerange, &oncardinal);
-    cansee = mon_visible(mon);
-    if ((dy * sy) >= (dx * sx))
-    {
-        range = dy * sy;
-    }
-    else
-    {
-        range = dx * sx;
-    }
+
     switch (monsters[mon].mon_id)
     {
     case PM_ARCHMAGE:
@@ -160,14 +151,14 @@ int mon_use_sorcery(int mon)
                         to_cast = MS_CURSE_WITHERING;
                         break;
                     }
-                case 4:
+                case 5:
                     if (!u.leadfoot)
                     {
                         to_cast = MS_CURSE_LEADFOOT;
                         break;
                     }
                     /* fall through */
-                case 5:
+                case 4:
                     if (!u.armourmelt)
                     {
                         to_cast = MS_CURSE_ARMOURMELT;
@@ -380,7 +371,7 @@ int mon_use_sorcery(int mon)
         print_mon_name(mon, 3);
         print_msg(" calls for help...\n");
         /* (Try to) summon 2-6 monsters. */
-        i = summoning(mptr->y, mptr->x, dice(2, 3));
+        i = summoning(mptr->pos, dice(2, 3));
         if (i == 0)
         {
             print_msg("... luckily for you, help wasn't listening.\n");
diff --git a/u.cc b/u.cc
index a713fc9..23d1a90 100644 (file)
--- a/u.cc
+++ b/u.cc
@@ -82,28 +82,30 @@ void recalc_defence(void)
     display_update();
 }
 
-Action_cost move_player(int dy, int dx)
+Action_cost move_player(Offset delta)
 {
-    if ((u.y + dy < 0) || (u.y + dy >= DUN_HEIGHT) ||
-        (u.x + dx < 0) || (u.x + dx >= DUN_WIDTH))
+    Coord c = u.pos + delta;
+    if ((c.y < 0) || (c.y >= DUN_HEIGHT) ||
+        (c.x < 0) || (c.x >= DUN_WIDTH))
     {
         print_msg("Attempted move out of bounds.\n");
         return Cost_none;       /* No movement. */
     }
-    if (lvl.mons[u.y + dy][u.x + dx] != NO_MON)
+    if (lvl.mon_at(c) != NO_MON)
     {
         if (u.weapon != NO_OBJ)
         {
             if ((objects[u.weapon].obj_id == PO_BOW) ||
-                (objects[u.weapon].obj_id == PO_CROSSBOW))
+                (objects[u.weapon].obj_id == PO_CROSSBOW) ||
+                (objects[u.weapon].obj_id == PO_THUNDERBOW))
             {
                 print_msg("You can't use that weapon in melee!\n");
                 return Cost_none;
             }
         }
-        return player_attack(dy, dx);
+        return player_attack(delta);
     }
-    switch (lvl.terrain[u.y + dy][u.x + dx])
+    switch (lvl.terrain_at(c))
     {
     case WALL:
     case HARDWALL:
@@ -113,16 +115,16 @@ Action_cost move_player(int dy, int dx)
     case DOOR:
     case STAIRS:
     case ALTAR:
-        reloc_player(u.y + dy, u.x + dx);
+        reloc_player(c);
         return Cost_std;
     case LAVA:
         if (u.resistances[DT_FIRE])
         {
-            if (lvl.terrain[u.y][u.x] != LAVA)
+            if (lvl.terrain_at(u.pos) != LAVA)
             {
                 print_msg("You walk on the lava.\n");
             }
-            reloc_player(u.y + dy, u.x + dx);
+            reloc_player(c);
             return Cost_std;
         }
         else
@@ -133,11 +135,11 @@ Action_cost move_player(int dy, int dx)
     case WATER:
         if ((u.ring != NO_OBJ) && (objects[u.ring].obj_id == PO_RING_FROST))
         {
-            if (lvl.terrain[u.y][u.x] != WATER)
+            if (lvl.terrain_at(u.pos) != WATER)
             {
                 print_msg("You walk on the water.\n");
             }
-            reloc_player(u.y + dy, u.x + dx);
+            reloc_player(c);
             return Cost_std;
         }
         else
@@ -149,24 +151,19 @@ Action_cost move_player(int dy, int dx)
     return Cost_none;
 }
 
-void reloc_player(int y, int x)
+void reloc_player(Coord c)
 {
-    int oy, ox;
-
-    oy = u.y;
-    ox = u.x;
-    u.y = y;
-    u.x = x;
-
-    touch_one_screen(oy, ox);
-    explore_around(u.y, u.x);
+    Coord oc = u.pos;
+    u.pos = c;
+    touch_one_screen(oc);
+    look_around_you();
     map_updated = 1;
     status_updated = 1;
     display_update();
-    if (lvl.objs[y][x] != NO_OBJ)
+    if (lvl.obj_at(c) != NO_OBJ)
     {
         print_msg("You see here ");
-        print_obj_name(lvl.objs[y][x]);
+        print_obj_name(lvl.obj_at(c));
         print_msg(".\n");
     }
 }
@@ -482,13 +479,13 @@ void u_init(void)
     u.level = 1;
     u.food = 2000;
     memset(u.inventory, -1, sizeof u.inventory);
-    u.inventory[0] = create_obj(PO_DAGGER, 1, true, -1, -1);
+    u.inventory[0] = create_obj(PO_DAGGER, 1, true, Nowhere);
     if (u.inventory[0] == NO_OBJ)
     {
         print_msg("Couldn't create dagger!\n");
     }
-    u.inventory[1] = create_obj(PO_IRON_RATION, 1, true, -1, -1);
-    u.inventory[2] = create_obj(PO_BATTLE_BALLGOWN, 1, true, -1, -1);
+    u.inventory[1] = create_obj(PO_IRON_RATION, 1, true, Nowhere);
+    u.inventory[2] = create_obj(PO_BATTLE_BALLGOWN, 1, true, Nowhere);
     u.weapon = u.inventory[0];
     u.ring = NO_OBJ;
     u.armour = u.inventory[2];
@@ -558,15 +555,15 @@ void gain_experience(int amount)
 Pass_fail teleport_u(void)
 {
     int cell_try;
-    int y, x;
+    Coord c;
     for (cell_try = 0; cell_try < 200; cell_try++)
     {
-        y = exclusive_flat(0, DUN_HEIGHT - 1);
-        x = exclusive_flat(0, DUN_WIDTH - 1);
-        if ((lvl.mons[y][x] == NO_MON) && (lvl.terrain[y][x] == FLOOR) && ((y != u.y) || (x != u.x)))
+        c.y = exclusive_flat(0, DUN_HEIGHT - 1);
+        c.x = exclusive_flat(0, DUN_WIDTH - 1);
+        if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
         {
             print_msg("You are whisked away!\n");
-            reloc_player(y, x);
+            reloc_player(c);
             return You_pass;
         }
     }
diff --git a/vector.cc b/vector.cc
deleted file mode 100644 (file)
index d428518..0000000
--- a/vector.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-/*! \file vector.cc
- *  \brief direction-handling code
- */
-
-/* Copyright 2005-2012 Martin Read
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "victrix-abyssi.hh"
-
-void compute_directions(int y1, int x1, int y2, int x2, int *pdy, int *pdx, int *psy, int *psx, int *pmelee, int *pcardinal)
-{
-    int dy, dx, sy, sx;
-    dy = y1 - y2;
-    dx = x1 - x2;
-    sy = mysign(dy);
-    sx = mysign(dx);
-    if (pdy)
-    {
-        *pdy = dy;
-    }
-    if (pdx)
-    {
-        *pdx = dx;
-    }
-    if (psy)
-    {
-        *psy = sy;
-    }
-    if (psx)
-    {
-        *psx = sx;
-    }
-    if (pmelee)
-    {
-        *pmelee = (dy < 2) && (dy > -2) && (dx < 2) && (dx > -2);
-    }
-    if (pcardinal)
-    {
-        *pcardinal = (dy == dx) || (dy == -dx) || (dx == 0) || (dy == 0);
-    }
-}
-
-/* vector.c */
-// vim:cindent
index e176527..3ddcf22 100644 (file)
@@ -118,8 +118,7 @@ enum death {
 /* XXX struct player */
 struct Player {
     char name[17];      /* including '\0' the fencepost. */
-    int y;              /* y-coord */
-    int x;              /* x-coord */
+    Coord pos;
     int body;           /* determines mace to-hit, melee damage, max 99 */
     int bdam;           /* current level of temporary body drain. */
     int agility;        /* determines sword, dagger, missile to-hit, max 99 */
@@ -169,11 +168,14 @@ struct Player {
 #endif
 
 /* XXX main.c data and funcs */
-extern int exclusive_flat(int lower, int upper); /* l+1 ... u-1 */
-extern int inclusive_flat(int lower, int upper); /* l ... u */
+extern int zero_die(int sides); /* 0..n-1 */
 extern int one_die(int sides);  /* 1..n */
 extern int dice(int count, int sides);
-extern int zero_die(int sides); /* 0..n-1 */
+extern int exclusive_flat(int lower, int upper); /* l+1 ... u-1 */
+extern int inclusive_flat(int lower, int upper); /* l ... u */
+extern Offset random_step(void);
+extern Coord inclusive_boxed(Coord topleft, Coord botright);
+extern Coord exclusive_boxed(Coord topleft, Coord botright);
 extern Action_cost do_command(enum game_cmd command);
 extern uint32_t convert_range(int dy, int dx);
 extern int game_finished;
@@ -195,9 +197,6 @@ extern uint32_t saved_state[5];
 extern uint32_t rng(void);
 extern void rng_init(void);
 
-/* XXX vector.c data and funcs */
-extern void compute_directions(int y1, int x1, int y2, int x2, int *pdy, int *pdx, int *psy, int *psx, int *pmelee, int *pcardinal);
-
 /* XXX u.c data and funcs */
 extern void u_init(void);
 extern void write_char_dump(void);
@@ -210,8 +209,8 @@ extern int drain_body(int amount, const char *what, int permanent);
 extern int drain_agility(int amount, const char *what, int permanent);
 extern void gain_experience(int amount);
 extern int lev_threshold(int level);
-extern Action_cost move_player(int dy, int dx);
-extern void reloc_player(int y, int x);
+extern Action_cost move_player(Offset delta);
+extern void reloc_player(Coord c);
 extern void recalc_defence(void);
 extern Pass_fail teleport_u(void);
 extern void update_player(void);