You guessed it, another ball of mud.
authorMartin Read <mpread@chiark.greenend.org.uk>
Thu, 13 Mar 2014 23:26:13 +0000 (23:26 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Thu, 13 Mar 2014 23:26:13 +0000 (23:26 +0000)
* Spell selectors moved into per-PM functions
* Some ... infelicities in cave map generation fixed.
* Flight works! Wielding a windsword lets you fly over hazardous terrain.
* All manner of tentacular refactoring.

28 files changed:
MANIFEST
Makefile
cave.cc
combat.cc
coord.hh
core.hh
deeds.cc
default.permobjs
default.permons
display-nc.cc
log.cc
main.cc
map.cc
map.hh
mapgen.hh
mon1.cc
notes.txt
notify-local-tty.cc
notify.hh
obj1.cc
objects.hh
obumbrata.hh
permobj.hh
player.hh
pobj_comp
sorcery.cc
sorcery.hh
u.cc

index a532a90..2afa367 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -16,6 +16,7 @@ default.permobjs
 default.permons
 display.hh
 display-nc.cc
+dungeon.cc
 fov.cc
 fov.hh
 log.cc
index ae3feac..6790a9f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ vpath %.o .
 GENERATED_OBJS:=permobj.o permons.o 
 GENERATED_SOURCE:=permobj.cc pobj_id.hh permons.cc pmon_id.hh
 GENERATED_MAKES:=dirs.mk features.mk
-HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o fov.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o rng.o role.o shrine.o sorcery.o u.o util.o
+HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o dungeon.o fov.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o rng.o role.o shrine.o sorcery.o u.o util.o
 OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS)
 GAME:=obumbrata
 MAJVERS:=1
diff --git a/cave.cc b/cave.cc
index fff560b..a26e0a4 100644 (file)
--- a/cave.cc
+++ b/cave.cc
@@ -63,7 +63,14 @@ void build_level_cave(Level *l)
     int walk_data[4] = { 1, FLOOR, WALL, FLOOR };
 
     initialize_chunks(l, GUIDE_EDGE_CHUNKS, GUIDE_EDGE_CHUNKS, true);
+    try
+    {
     run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
     //run_random_walk_unbounded(c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
     if ((l->theme != THEME_UNDEAD) && (depth > 20) && !zero_die(4))
     {
@@ -87,7 +94,14 @@ void build_level_cave(Level *l)
         do {
             c = l->random_point(2);
         } while (l->terrain_at(c) != FLOOR);
-        run_random_walk(l, c, excavation_write, walk_data, pool_size);
+        try 
+        {
+            run_random_walk(l, c, excavation_write, walk_data, pool_size);
+        }
+        catch (Obumb_levgen_excep p)
+        {
+            DEBUG_ARBITRARY(p.str);
+        }
         --num_pools;
     }
     place_cave_stairs(l);
@@ -105,10 +119,75 @@ void build_level_intrusions(Level *l)
     {
         place_random_intrusion(l, WALL);
     }
-    run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    try
+    {
+        run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
     /* and now the stairs */
     place_cave_stairs(l);
 }
 
+/*! \brief Excavate a "pits" level. */
+void build_level_pits(Level *l, Terrain pit_type)
+{
+    Coord c = { GUIDE_EDGE_SIZE / 2, GUIDE_EDGE_SIZE / 2 };
+    int walk_data[4] = { 1, FLOOR, WALL, pit_type };
+
+    initialize_chunks(l, GUIDE_EDGE_CHUNKS, GUIDE_EDGE_CHUNKS, true);
+    walk_data[1] = pit_type;
+    c.y = GUIDE_EDGE_SIZE / 6;
+    c.x = GUIDE_EDGE_SIZE / 6;
+    try
+    {
+        run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
+    c.x = GUIDE_EDGE_SIZE - (GUIDE_EDGE_SIZE / 6);
+    try
+    {
+        run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
+    c.y = GUIDE_EDGE_SIZE - (GUIDE_EDGE_SIZE / 6);
+    try
+    {
+    run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
+    c.x = (GUIDE_EDGE_SIZE / 6);
+    try
+    {
+    run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
+    walk_data[0] = 2;
+    walk_data[1] = FLOOR;
+    try
+    {
+    run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
+    }
+    catch (Obumb_levgen_excep p)
+    {
+        DEBUG_ARBITRARY(p.str);
+    }
+    place_cave_stairs(l);
+}
+
 /* cave.cc */
 // vim:cindent:ts=8:sw=4:expandtab
index 010b79a..fee2098 100644 (file)
--- a/combat.cc
+++ b/combat.cc
@@ -59,32 +59,27 @@ bool ring_effectiveness(Mon_handle mon, int ring_pobj, int damage, int *bonus_da
     bool rv = false;
     int pm = monsters[mon].pm_ref;
     *vamp_healing = 0;
-    switch (ring_pobj)
+    // Testing order is fire, vampire, frost.
+    if (u.damage_amp[DT_FIRE] && !pmon_resists_fire(pm))
     {
-    case PO_FIRE_RING:
-        if (!pmon_resists_fire(pm))
-        {
-            *bonus_damage = dice(2, 4) + ((damage + 1) / 2);
-            rv = true;
-        }
-        break;
-    case PO_VAMPIRE_RING:
-        if (!pmon_resists_necro(pm))
-        {
-            *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
-            *vamp_healing = std::min(monsters[mon].hpcur, (damage + 5) / 6);
-            rv = true;
-        }
-        break;
-    case PO_FROST_RING:
-        if (!pmon_resists_cold(pm))
-        {
-            *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
-            rv = true;
-        }
-        break;
-    default:
-        break;
+       *bonus_damage = dice(2, 4) + ((damage + 1) / 2);
+       rv = true;
+    }
+    else if (u.damage_amp[DT_COLD] && !pmon_resists_cold(pm))
+    {
+       *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
+       rv = true;
+    }
+    else if (u.damage_amp[DT_NECRO] && !pmon_resists_necro(pm))
+    {
+       *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
+       *vamp_healing = std::min(monsters[mon].hpcur, (damage + 5) / 6);
+       rv = true;
+    }
+    else
+    {
+       *bonus_damage = 0;
+       *vamp_healing = 0;
     }
     return rv;
 }
index c69dda2..3a25f4f 100644 (file)
--- a/coord.hh
+++ b/coord.hh
@@ -97,6 +97,7 @@ public:
     Offset delta(Coord const& right) const { Offset d = { y - right.y, x - right.x }; return d; }
     Coord operator +(Offset const& right) const { Coord c = { y + right.y, x + right.x}; return c; }
     Coord operator -(Offset const& right) const { Coord c = { y - right.y, x - right.x}; return c; }
+    Offset operator -(Coord const& right) const { Offset o = { y - right.y, x - right.x}; return o; }
     Coord const& operator +=(Offset const& right) { y += right.y; x += right.x; return *this;}
     Coord const& operator -=(Offset const& right) { y -= right.y; x -= right.x; return *this;}
     bool operator !=(Coord const& right) const { return (y != right.y) || (x != right.x); }
diff --git a/core.hh b/core.hh
index 9aa0701..90aa341 100644 (file)
--- a/core.hh
+++ b/core.hh
@@ -103,11 +103,13 @@ enum Gamecolour
 
 /*! \brief Identification code for damage types */
 enum Damtyp {
+    DT_NONE = -1,
     DT_PHYS = 0, DT_COLD, DT_FIRE, DT_NECRO,
     DT_ELEC, DT_HELLFIRE, DT_POISON,
     DT_KNOCKBACK, DT_DROWNING
 };
-#define DT_COUNT (1 + DT_DROWNING)
+#define LAST_DAMTYPE (DT_DROWNING)
+#define NUM_DAMTYPES (1 + LAST_DAMTYPE)
 
 /*! \brief Identification code for player actions */
 enum Game_cmd {
@@ -148,7 +150,7 @@ enum Combo_state
  */
 enum Death {
     DEATH_KILLED, DEATH_KILLED_MON, DEATH_BODY, DEATH_AGILITY,
-    DEATH_LASH, DEATH_RIBBONS
+    DEATH_LASH
 };
 
 /*! \brief Fell powers from forbidden places
@@ -162,10 +164,11 @@ enum Fell_powers {
 
 #define TOTAL_FELL_POWERS (1 + FePo_flesh)
 
-#define RESIST_MASK_TEMPORARY   0x0000FFFFu
-#define RESIST_MASK_PERM_EQUIP  0xFFFF0000u
-#define RESIST_RING     0x00010000u
-#define RESIST_ARMOUR   0x00020000u
+#define BONUS_MASK_TEMPORARY   0x0000FFFFu
+#define BONUS_MASK_PERM_EQUIP  0xFFFF0000u
+#define BONUS_RING     0x00010000u
+#define BONUS_ARMOUR   0x00020000u
+#define BONUS_WEAPON   0x00040000u
 
 /*! \brief Represent an in-game action by the player
  *
index 309ad0a..1634648 100644 (file)
--- a/deeds.cc
+++ b/deeds.cc
@@ -157,16 +157,14 @@ static Action_cost deed_armour_off(Action const *act)
     {
         /* this actually belongs in a sensible place. */
         int saved_armour = u.armour;
-        if ((u.resistances[DT_FIRE] == RESIST_ARMOUR) &&
-            (lvl.terrain_at(u.pos) == LAVA))
+        if (unequip_safety_check(BONUS_ARMOUR, Noise_std) == You_pass)
         {
-            notify_lava_blocks_unequip();
-            return Cost_none;
+            u.armour = NO_OBJ;
+            recalc_defence();
+            notify_armour_unequip(saved_armour);
+            return Cost_std;
         }
-        u.armour = NO_OBJ;
-        recalc_defence();
-        notify_armour_unequip(saved_armour);
-        return Cost_std;
+        return Cost_none;
     }
     else
     {
index 5878364..0db5f1f 100644 (file)
@@ -116,6 +116,7 @@ NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
 MELEE_WEAPON
+DEMONIC
 
 WEAPON plague scythe
 PLURAL plague sycthes
@@ -129,6 +130,7 @@ POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
 MELEE_WEAPON
+DEMONIC
 
 WEAPON tormentor's lash
 PLURAL tormentor's lashes
@@ -144,6 +146,7 @@ NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
 MELEE_WEAPON
+DEMONIC
 
 WEAPON death staff
 PLURAL death staves
@@ -365,6 +368,7 @@ POWER 8
 POWER2 0
 DEPTH 8
 DAMAGEABLE
+SPEED
 
 ARMOUR robe of shadows
 PLURAL robes of shadows
@@ -471,6 +475,7 @@ NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
 RES_POISON
+DEMONIC
 
 ARMOUR lich's robe
 PLURAL lich's robes
@@ -497,6 +502,7 @@ POWER2 20
 DEPTH 30
 NOTIFY_EQUIP
 RES_FIRE
+DEMONIC
 
 RING regeneration ring
 PLURAL regeneration rings
@@ -571,6 +577,7 @@ POWER 0
 POWER2 0
 DEPTH 15
 RES_POISON
+DEMONIC
 
 RING protection ring
 PLURAL protection rings
@@ -586,7 +593,7 @@ PROTECTIVE
 
 RING imperial seal
 PLURAL imperial seals
-DESC This rarest and most ancient of magical rings was forged in the days before the Archon Inferni was imprisoned in this dismal place. Of the nature of its power, there are not even rumours.
+DESC This rarest and most ancient of magical rings was forged in the days before the Archon Inferni was banished to the Abyss. Of the nature of its power, there are not even rumours.
 RARITY 100
 ASCII '='
 UTF8 "="
@@ -643,6 +650,7 @@ POWER 0
 POWER2 0
 DEPTH 1
 STACKABLE
+DEMONIC
 
 CARRION corpse
 PLURAL corpses
index 2239a69..c160c07 100644 (file)
@@ -218,6 +218,25 @@ MADE_OF_MEAT
 CONTAINS_BLOOD
 LEAVES_CORPSE
 
+monster sybarite
+desc A thrill-seeking devotee of fell powers.
+ascii 'f'
+utf8 "f"
+colour l_purple
+rarity 30
+power 18
+hp 120
+mhit 30
+mdam 18
+defence 20
+experience 600
+speed 1
+SMART
+HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
+
 # goblins
 monster goblin
 desc A short, scrawny humanoid with no love for surface folk.
index 790b52b..2ca0636 100644 (file)
@@ -178,7 +178,7 @@ cchar_t const *back_buffer[DISP_HEIGHT][DISP_WIDTH];
 cchar_t const *front_buffer[DISP_HEIGHT][DISP_WIDTH];
 
 /*! \brief Printable English-language names for damage types */
-char const *damtype_names[DT_COUNT] = {
+char const *damtype_names[NUM_DAMTYPES] = {
     "physical damage",
     "cold",
     "fire",
@@ -415,17 +415,18 @@ static void load_unicode_tiles(void)
 {
     int i;
     int j;
+    wchar_t wch[2];
     {
-        wchar_t wch[2];
         wch[0] = L'@';
         wch[1] = 0;
         setcchar(&player_tile, wch, 0, 0, nullptr);
-        wch[0] = L' ';
-        setcchar(&blank_tile, wch, 0, 0, nullptr);
+        wch[0] = L'â–‘';
+       setcchar(&blank_tile, wch,
+                colour_data[Gcol_d_grey].attr,
+                colour_data[Gcol_d_grey].cpair, nullptr);
     }
     for (i = 0; i < NUM_OF_PERMONS; ++i)
     {
-        wchar_t wch[2];
         wch[0] = permons[i].sym;
         wch[1] = 0;
         setcchar(permon_tiles + i, wch,
@@ -434,7 +435,6 @@ static void load_unicode_tiles(void)
     }
     for (i = 0; i < NUM_TERRAINS; ++i)
     {
-        wchar_t wch[2];
         /* policy decision: for now we don't support use of combining
          * characters for terrain. */
         j = mbtowc(wch, terrain_props[i].unicode, 4);
@@ -448,7 +448,6 @@ static void load_unicode_tiles(void)
     }
     for (i = 0; i < NUM_OF_PERMOBJS; ++i)
     {
-        wchar_t wch[2];
         /* policy decision: for now we don't support use of combining
          * characters for items. */
         j = mbtowc(wch, permobjs[i].unicode, 4);
@@ -471,7 +470,9 @@ static void load_ascii_tiles(void)
     wch[1] = 0;
     setcchar(&player_tile, wch, 0, 0, nullptr);
     wch[0] = L' ';
-    setcchar(&blank_tile, wch, 0, 0, nullptr);
+    setcchar(&blank_tile, wch,
+            colour_data[Gcol_d_grey].attr,
+            colour_data[Gcol_d_grey].cpair, nullptr);
     for (i = 0; i < NUM_OF_PERMONS; ++i)
     {
         wch[0] = permons[i].sym;
@@ -1307,7 +1308,7 @@ void get_player_action(Action *act)
             {
                 print_msg("You are not wearing a ring.\n");
             }
-            else if (ring_removal_unsafe(Noise_std) == You_pass)
+            else if (unequip_safety_check(BONUS_RING, Noise_std) == You_pass)
             {
                 act->cmd = REMOVE_RING;
                 return;
@@ -1625,6 +1626,10 @@ static void run_main_menu(void)
             mvwprintw(fullscreen_window, 12, 1, "\n");
             mvwprintw(fullscreen_window, 13, 19, "Welcome. Remind me of thy name?\n");
             mvwprintw(fullscreen_window, 14, 1, "\n");
+           mvwprintw(fullscreen_window, 15, 1, "\n");
+           mvwprintw(fullscreen_window, 16, 1, "\n");
+           mvwprintw(fullscreen_window, 17, 1, "\n");
+           mvwprintw(fullscreen_window, 18, 1, "\n");
             wmove(fullscreen_window, 14, 30);
             update_panels();
             doupdate();
@@ -1751,8 +1756,9 @@ static void examine_square(Offset o)
     Mon_handle mon;
     Obj_handle obj;
     Terrain terr;
-    if ((c.y < 0) || (c.x < 0) || (c.y >= (lvl.chunks_high << CHUNK_SHIFT)) ||
-        (c.x >= (lvl.chunks_wide << CHUNK_SHIFT)))
+    if ((c.y < 0) || (c.x < 0) ||
+       (c.y >= int32_t(lvl.chunks_high << CHUNK_SHIFT)) ||
+        (c.x >= int32_t(lvl.chunks_wide << CHUNK_SHIFT)))
     {
         print_msg("That square is beyond the bounds of the level.\n");
         return;
diff --git a/log.cc b/log.cc
index 5de2342..41ae374 100644 (file)
--- a/log.cc
+++ b/log.cc
@@ -31,6 +31,7 @@
 #include "monsters.hh"
 #include "player.hh"
 #include "map.hh"
+#include "mapgen.hh"
 #include "util.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -343,10 +344,14 @@ static void deserialize_player(FILE *fp, Player *player)
     deserialize_int(fp, &(player->withering));
     deserialize_int(fp, &(player->armourmelt));
     deserialize_int(fp, &(player->speed));
-    for (int i = 0; i < DT_COUNT; ++i)
+    for (int i = 0; i < NUM_DAMTYPES; ++i)
     {
         deserialize_uint32(fp, &(player->resistances[i]));
+        deserialize_uint32(fp, &(player->damage_amp[i]));
     }
+    deserialize_uint32(fp, &(player->passwater));
+    deserialize_uint32(fp, &(player->flight));
+    deserialize_uint32(fp, &(player->protective_gear));
     for (int i = 0; i < INVENTORY_SIZE; ++i)
     {
         deserialize_objhandle(fp, &(player->inventory[i]));
@@ -386,10 +391,14 @@ static void serialize_player(FILE *fp, Player const& player)
     serialize_int(fp, player.withering);
     serialize_int(fp, player.armourmelt);
     serialize_int(fp, player.speed);
-    for (int i = 0; i < DT_COUNT; ++i)
+    for (int i = 0; i < NUM_DAMTYPES; ++i)
     {
         serialize_uint32(fp, player.resistances[i]);
+        serialize_uint32(fp, player.damage_amp[i]);
     }
+    serialize_uint32(fp, player.passwater);
+    serialize_uint32(fp, player.flight);
+    serialize_uint32(fp, player.protective_gear);
     for (int i = 0; i < INVENTORY_SIZE; ++i)
     {
         serialize_objhandle(fp, player.inventory[i]);
@@ -403,6 +412,145 @@ static void serialize_player(FILE *fp, Player const& player)
     }
 }
 
+/*! \brief Read a Chunk from a FILE.  */
+void deserialize_chunk(FILE *fp, Chunk *c)
+{
+    static uint32_t deserializable_terrain[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t deserializable_flags[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t deserializable_regions[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t deserializable_decals[CHUNK_EDGE][CHUNK_EDGE];
+    int i;
+    int j;
+    wrapped_fread(deserializable_terrain, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    wrapped_fread(deserializable_flags, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    wrapped_fread(deserializable_regions, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    wrapped_fread(deserializable_decals, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    for (i = 0; i < CHUNK_EDGE; ++i)
+    {
+        for (j = 0; j < CHUNK_EDGE; ++j)
+        {
+            c->terrain[i][j] = (Terrain) ntohl(deserializable_terrain[i][j]);
+            c->flags[i][j] = ntohl(deserializable_flags[i][j]);
+            c->region_number[i][j] = ntohl(deserializable_regions[i][j]);
+            c->decals[i][j] = (Decal_tag) ntohl(deserializable_decals[i][j]);
+            /* objs and mons will get accurately set once we've loaded the
+             * objs and mons. */
+            c->objs[i][j] = NO_OBJ;
+            c->mons[i][j] = NO_MON;
+        }
+    }
+}
+
+/*! \brief Write a Chunk out to a FILE.  */
+void serialize_chunk(FILE *fp, Chunk const *c)
+{
+    static uint32_t serializable_terrain[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t serializable_flags[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t serializable_regions[CHUNK_EDGE][CHUNK_EDGE];
+    static uint32_t serializable_decals[CHUNK_EDGE][CHUNK_EDGE];
+    int i;
+    int j;
+    for (i = 0; i < CHUNK_EDGE; ++i)
+    {
+        for (j = 0; j < CHUNK_EDGE; ++j)
+        {
+            serializable_terrain[i][j] = htonl(c->terrain[i][j]);
+            serializable_flags[i][j] = htonl(c->flags[i][j]);
+            serializable_regions[i][j] = htonl(c->region_number[i][j]);
+            serializable_decals[i][j] = htonl(c->decals[i][j]);
+        }
+    }
+    fwrite(serializable_terrain, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    fwrite(serializable_flags, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    fwrite(serializable_regions, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+    fwrite(serializable_decals, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
+}
+
+/*! \brief Deserialize a Level */
+void deserialize_level(FILE *fp, Level *l)
+{
+    uint32_t tmp;
+    uint32_t tmp_pair[2];
+    uint16_t tmp_shorts[2];
+    uint32_t i;
+    uint32_t j;
+    wrapped_fread(tmp_shorts, sizeof tmp_shorts[0], 2, fp);
+    l->self.dungeon = ntohs(tmp_shorts[0]);
+    l->self.depth = (int16_t) ntohs(tmp_shorts[1]);
+    wrapped_fread(tmp_pair, sizeof tmp_pair[0], 2, fp);
+    l->origin_off.y = (int32_t) ntohl(tmp_pair[0]);
+    l->origin_off.x = (int32_t) ntohl(tmp_pair[1]);
+    wrapped_fread(&tmp, sizeof tmp, 1, fp);
+    l->dead_space = Terrain(ntohl(tmp));
+    wrapped_fread(&tmp, sizeof tmp, 1, fp);
+    l->theme = Level_theme(ntohl(tmp));
+    wrapped_fread(&tmp, sizeof tmp, 1, fp);
+    l->layout = Level_layout(ntohl(tmp));
+    wrapped_fread(&tmp, sizeof tmp, 1, fp);
+    l->chunks_high = ntohl(tmp);
+    wrapped_fread(&tmp, sizeof tmp, 1, fp);
+    l->chunks_wide = ntohl(tmp);
+    initialize_chunks(l, l->chunks_high, l->chunks_wide, false);
+    do
+    {
+        wrapped_fread(&tmp_pair, sizeof tmp_pair[0], 2, fp);
+        i = ntohl(tmp_pair[0]);
+        j = ntohl(tmp_pair[1]);
+        if (i == ~0u)
+        {
+            break;
+        }
+        if ((i >= l->chunks_high) || (j >= l->chunks_wide))
+        {
+            throw Obumb_dataexcep("Out-of-bounds chunk indices while loading level - save probably corrupt");
+        }
+        l->chunks[i][j] = new Chunk(l->dead_space);
+        deserialize_chunk(fp, l->chunks[i][j]);
+    }
+    while (1);
+}
+
+/*! \brief Serialize a Level */
+void serialize_level(FILE *fp, Level const *l)
+{
+    uint32_t tmp;
+    uint32_t tmp_pair[2];
+    uint16_t tmp_shorts[2];
+    uint32_t i;
+    uint32_t j;
+    tmp_shorts[0] = htons(l->self.dungeon);
+    tmp_shorts[1] = htons(l->self.depth);
+    fwrite(tmp_shorts, sizeof tmp_shorts[0], 2, fp);
+    tmp_pair[0] = htonl(l->origin_off.y);
+    tmp_pair[1] = htonl(l->origin_off.x);
+    fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
+    tmp = htonl(l->dead_space);
+    fwrite(&tmp, sizeof tmp, 1, fp);
+    tmp = htonl(l->theme);
+    fwrite(&tmp, sizeof tmp, 1, fp);
+    tmp = htonl(l->layout);
+    fwrite(&tmp, sizeof tmp, 1, fp);
+    tmp = htonl(l->chunks_high);
+    fwrite(&tmp, sizeof tmp, 1, fp);
+    tmp = htonl(l->chunks_wide);
+    fwrite(&tmp, sizeof tmp, 1, fp);
+    for (i = 0; i < l->chunks_high; ++i)
+    {
+        tmp_pair[0] = htonl(i);
+        for (j = 0; j < l->chunks_wide; ++j)
+        {
+            if (l->chunks[i][j])
+            {
+                tmp_pair[1] = htonl(j);
+                fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
+                serialize_chunk(fp, l->chunks[i][j]);
+            }
+        }
+    }
+    tmp_pair[0] = tmp_pair[1] = ~0u;
+    fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
+}
+
 /*! \brief Save the game; set game_finished flag if successful */
 void save_game(void)
 {
@@ -729,9 +877,6 @@ void log_death(Death d, char const *what)
         case DEATH_LASH:
             fprintf(fp, "%s tasted the lash one time too many.\n", u.name);
             break;
-        case DEATH_RIBBONS:
-            fprintf(fp, "%s looked good in ribbons.\n", u.name);
-            break;
         }
         fprintf(fp, "    %s died after %d ticks, with %d XP, on dungeon level %d.\n\n", u.name, game_tick, u.experience, depth);
         fflush(fp);
diff --git a/main.cc b/main.cc
index d42e244..49c0f63 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -102,7 +102,7 @@ void main_loop(void)
         }
         for (auto iter = monsters.begin(); iter != monsters.end(); ++iter)
         {
-            if (!(iter->second.flags & MF_USED))
+            if (!(iter->second.flags & MF_ALIVE))
             {
                 continue;
             }
diff --git a/map.cc b/map.cc
index c715194..b91eaca 100644 (file)
--- a/map.cc
+++ b/map.cc
@@ -37,8 +37,8 @@ int depth;
 
 void drop_all_chunks(Level *l)
 {
-    int i;
-    int j;
+    uint32_t i;
+    uint32_t j;
     if (l->chunks)
     {
         for (i = 0; i < l->chunks_high; ++i)
@@ -107,7 +107,7 @@ void Level::vivify(Coord c)
         grow(North);
         rc = c + origin_off;
     }
-    while (rc.y > (chunks_high << CHUNK_SHIFT))
+    while (rc.y > int32_t(chunks_high << CHUNK_SHIFT))
     {
         grow(South);
     }
@@ -116,7 +116,7 @@ void Level::vivify(Coord c)
         grow(West);
         rc = c + origin_off;
     }
-    while (rc.x > (chunks_wide << CHUNK_SHIFT))
+    while (rc.x > int32_t(chunks_wide << CHUNK_SHIFT))
     {
         grow(East);
     }
@@ -130,11 +130,10 @@ void Level::grow(Offset o, bool dense)
 {
     Chunk ***new_chunk_grid;
     Chunk **new_chunk_row;
-    int i;
-    int j;
+    uint32_t i;
+    uint32_t j;
     if (o.y < 0)
     {
-        int i;
         origin_off.y += CHUNK_EDGE;
         ++chunks_high;
         new_chunk_grid = new Chunk **[chunks_high];
@@ -205,8 +204,8 @@ void Level::grow(Offset o, bool dense)
  */
 Coord Level::random_point(int margin) const
 {
-    Coord tl = { margin, margin };
-    Coord br = { GUIDE_EDGE_SIZE - (margin + 1), GUIDE_EDGE_SIZE - (margin + 1) };
+    Coord tl = { min_y() + margin, min_x() + margin };
+    Coord br = { max_y() - margin, max_x() - margin };
     return inc_boxed(tl, br);
 }
 
@@ -315,16 +314,10 @@ Coord run_random_walk(Level *l, Coord oc, rwalk_mod_funcptr func,
     for (i = 0; (i < cells) && (bailout > 0); --bailout)
     {
         oc = c;
-        if (zero_die(2))
-        {
-            c.y += (zero_die(2) ? -1 : 1);
-            c.y = std::min(l->max_y() - 2, std::max(c.y, l->min_y() + 2));
-        }
-        else
-        {
-            c.x += (zero_die(2) ? -1 : 1);
-            c.x = std::min(l->max_x() - 2, std::max(c.x, l->min_x() + 2));
-        }
+        c += zero_die(2) ?
+            (zero_die(2) ? North : South) :
+            (zero_die(2) ? East : West);
+        c.clamp(l->min_y() + 2, l->min_x() + 2, l->max_y() - 2, l->max_x() - 2);
         switch (func(l, c, priv_ptr))
         {
         case 0:
@@ -342,7 +335,7 @@ Coord run_random_walk(Level *l, Coord oc, rwalk_mod_funcptr func,
     }
     if (bailout < 1)
     {
-        debug_excavation_bailout();
+        throw Obumb_levgen_excep("run_random_walk exceeded bailout limit");
     }
     return c;
 }
@@ -363,14 +356,9 @@ Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
     for (i = 0; (i < cells) && (bailout > 0); --bailout)
     {
         oc = c;
-        if (zero_die(2))
-        {
-            c.y += (zero_die(2) ? -1 : 1);
-        }
-        else
-        {
-            c.x += (zero_die(2) ? -1 : 1);
-        }
+        c += zero_die(2) ?
+            (zero_die(2) ? North : South) :
+            (zero_die(2) ? East : West);
         if (c.y <= l->min_y())
         {
             l->grow(North, true);
@@ -404,7 +392,7 @@ Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
     }
     if (bailout < 1)
     {
-        debug_excavation_bailout();
+        throw Obumb_levgen_excep("run_random_walk exceeded bailout limit");
     }
     return c;
 }
@@ -421,11 +409,15 @@ void build_level(void)
     lvl.stairs.clear();
     rng.extract_serialization(saved_state_buffer, saved_state_size);
     theme_roll = zero_die(depth + 50);
-    if (!zero_die(4))
+    if (!zero_die(6))
+    {
+        lvl.layout = LAYOUT_CAVE_PITS;
+    }
+    else if (!zero_die(5))
     {
         lvl.layout = LAYOUT_CAVE_INTRUSIONS;
     }
-    else if ((depth > 1) && !zero_die(6))
+    else if ((depth > 8) && !zero_die(6))
     {
         lvl.layout = LAYOUT_CAVE_SHRINE;
     }
@@ -463,34 +455,30 @@ void build_level(void)
     case LAYOUT_CLASSIC_CAVE:
         build_level_cave(&lvl);
         break;
-    }
-}
-
-/*! \brief Build a dungeonbash-style rooms-and-corridors level */
-void build_level_dungeonbash(Level *l)
-{
-    int chy;
-    int chx;
-    /* We know we're going to use all nine chunks, so create them
-     * immediately. */
-    initialize_chunks(&lvl, GUIDE_EDGE_CHUNKS, GUIDE_EDGE_CHUNKS, true);
-    /* One room per chunk. */
-    for (chy = 0; chy < l->chunks_high; ++chy)
-    {
-        for (chx = 0; chx < l->chunks_wide; ++ chx)
+    case LAYOUT_CAVE_PITS:
+        if (depth > 5)
         {
-            /* Smallest allowed room has a 2x2 interior. */
-            Offset room_size = { MIN_ROOM_EDGE + zero_die(5), MIN_ROOM_EDGE + zero_die(5) };
-            /* Each dimension has a 1-in-3 chance to get another boost */
             if (!zero_die(3))
             {
-                room_size.y += zero_die(5);
+                build_level_pits(&lvl, LAVA);
             }
-            if (!zero_die(3))
+            else if (!zero_die(2))
+            {
+                build_level_pits(&lvl, WATER);
+            }
+            else
             {
-                room_size.x += zero_die(5);
+                build_level_pits(&lvl, CHASM);
             }
         }
+        else
+        {
+            build_level_pits(&lvl, CHASM);
+        }
+        break;
+    case LAYOUT_CLAUSTROPHOBIA:
+        build_level_claustrophobia(&lvl);
+        break;
     }
 }
 
@@ -507,6 +495,10 @@ int excavation_write(Level *l, Coord c, void const *data)
          * them either. */
         return 2;
     }
+    if (l->terrain_at(c) == newterr)
+    {
+        return 0;
+    }
     for (j = 0; j < data_as_ints[0]; ++j)
     {
         if (l->terrain_at(c) == overwrite[j])
@@ -515,17 +507,26 @@ int excavation_write(Level *l, Coord c, void const *data)
             return 1;
         }
     }
-    return 0;
+    return 2;
 }
 
 /*! \brief "Intrusion" function for use with random walks */
 int intrusion_write(Level *l, Coord c, void const *data)
 {
     Terrain const *tptr = (Terrain const *) data;
-    if ((l->terrain_at(c) != WALL) || (l->flags_at(c) & MAPFLAG_HARDWALL))
+    Terrain old_terr = l->terrain_at(c);
+    // Meander across MAPFLAG_HARDWALL and across whatever terrain we
+    // might be overwriting dead space with.
+    if ((l->flags_at(c) & MAPFLAG_HARDWALL) ||
+        (tptr && (*tptr == old_terr)))
     {
         return 0;
     }
+    // Bounce off anything that isn't "dead space"
+    if (old_terr != l->dead_space)
+    {
+        return 2;
+    }
     /* Don't intrude too closely on the centre of the level */
     if ((c.y > ((GUIDE_EDGE_SIZE / 2) - 4)) && (c.y < ((GUIDE_EDGE_SIZE / 2) - 4)))
     {
@@ -553,7 +554,14 @@ void place_random_intrusion(Level *l, Terrain new_wall)
         c.x = zero_die(2) ? inc_flat(1, GUIDE_EDGE_SIZE / 3) : inc_flat((2 * GUIDE_EDGE_SIZE) / 3, GUIDE_EDGE_SIZE - 2);
         c.y = zero_die(2) ? inc_flat(1, GUIDE_EDGE_SIZE / 3) : inc_flat((2 * GUIDE_EDGE_SIZE) / 3, GUIDE_EDGE_SIZE - 2);
     } while (l->flags_at(c) & MAPFLAG_HARDWALL);
-    run_random_walk(l, c, intrusion_write, &new_wall, intrusion_size);
+    try
+    {
+        run_random_walk(l, c, intrusion_write, &new_wall, intrusion_size);
+    }
+    catch (...)
+    {
+        // undersize intrusions are not a problem
+    }
 }
 
 /*! \brief Get a valid square to generate a monster on */
@@ -587,7 +595,7 @@ void populate_level(void)
     Coord c;
     int ic;
     /* Generate some random monsters */
-    for (i = 0; i < 10; i++)
+    for (i = 0; i < (10 + depth); i++)
     {
         pf = get_levgen_mon_floor(&lvl, &c);
         if (pf == You_fail)
@@ -661,12 +669,24 @@ Terrain_props terrain_props[NUM_TERRAINS] =
     { "downward stairs", '>', ">", Gcol_l_grey, TFLAG_descend | TFLAG_block_items },
     { "inert portal", '^', "☊", Gcol_d_grey, TFLAG_portal | TFLAG_ascend | TFLAG_block_items },
     { "active portal", '^', "☊", Gcol_white, TFLAG_portal | TFLAG_descend | TFLAG_block_items },
-    { "chasm", ':', "↓", Gcol_d_grey, TFLAG_fall_hazard | TFLAG_block_items },
+    { "chasm", ' ', " ", Gcol_d_grey, TFLAG_fall_hazard | TFLAG_block_items },
     { "lava", '}', "≈", Gcol_red, TFLAG_fire_hazard | TFLAG_block_items },
     { "water", '}', "≈", Gcol_blue, TFLAG_drown_hazard | TFLAG_block_items },
 };
 
 /*! \brief self-explanatory */
+bool terrain_is_floor(Terrain terr)
+{
+    return terrain_props[terr].flags & TFLAG_floor;
+}
+
+/*! \brief self-explanatory */
+bool terrain_is_wall(Terrain terr)
+{
+    return terrain_props[terr].flags & TFLAG_wall;
+}
+
+/*! \brief self-explanatory */
 bool terrain_is_opaque(Terrain terr)
 {
     return terrain_props[terr].flags & TFLAG_opaque;
@@ -705,149 +725,6 @@ bool terrain_blocks_missiles(Terrain terr)
     return terrain_props[terr].flags & TFLAG_block_missile;
 }
 
-/*! \brief Read a Chunk from a FILE.
- *
- *  Yes, I know "throw(errno)" is tacky and stupid, but it achieves the
- *  desired result: the save is obviously garbage, so there's no point
- *  in finishing loading it.
- */
-void deserialize_chunk(FILE *fp, Chunk *c)
-{
-    static uint32_t deserializable_terrain[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t deserializable_flags[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t deserializable_regions[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t deserializable_decals[CHUNK_EDGE][CHUNK_EDGE];
-    int i;
-    int j;
-    wrapped_fread(deserializable_terrain, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    wrapped_fread(deserializable_flags, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    wrapped_fread(deserializable_regions, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    wrapped_fread(deserializable_decals, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    for (i = 0; i < CHUNK_EDGE; ++i)
-    {
-        for (j = 0; j < CHUNK_EDGE; ++j)
-        {
-            c->terrain[i][j] = (Terrain) ntohl(deserializable_terrain[i][j]);
-            c->flags[i][j] = ntohl(deserializable_flags[i][j]);
-            c->region_number[i][j] = ntohl(deserializable_regions[i][j]);
-            c->decals[i][j] = (Decal_tag) ntohl(deserializable_decals[i][j]);
-            /* objs and mons will get accurately set once we've loaded the
-             * objs and mons. */
-            c->objs[i][j] = NO_OBJ;
-            c->mons[i][j] = NO_MON;
-        }
-    }
-}
-
-/*! \brief Write a Chunk out to a FILE.  */
-void serialize_chunk(FILE *fp, Chunk const *c)
-{
-    static uint32_t serializable_terrain[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t serializable_flags[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t serializable_regions[CHUNK_EDGE][CHUNK_EDGE];
-    static uint32_t serializable_decals[CHUNK_EDGE][CHUNK_EDGE];
-    int i;
-    int j;
-    for (i = 0; i < CHUNK_EDGE; ++i)
-    {
-        for (j = 0; j < CHUNK_EDGE; ++j)
-        {
-            serializable_terrain[i][j] = htonl(c->terrain[i][j]);
-            serializable_flags[i][j] = htonl(c->flags[i][j]);
-            serializable_regions[i][j] = htonl(c->region_number[i][j]);
-            serializable_decals[i][j] = htonl(c->decals[i][j]);
-        }
-    }
-    fwrite(serializable_terrain, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    fwrite(serializable_flags, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    fwrite(serializable_regions, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-    fwrite(serializable_decals, 4, CHUNK_EDGE * CHUNK_EDGE, fp);
-}
-
-/*! \brief Serialize a Level */
-void serialize_level(FILE *fp, Level const *l)
-{
-    uint32_t tmp;
-    uint32_t tmp_pair[2];
-    uint16_t tmp_shorts[2];
-    int i;
-    int j;
-    tmp_shorts[0] = htons(l->self.dungeon);
-    tmp_shorts[1] = htons(l->self.depth);
-    fwrite(tmp_shorts, sizeof tmp_shorts[0], 2, fp);
-    tmp_pair[0] = htonl(l->origin_off.y);
-    tmp_pair[1] = htonl(l->origin_off.x);
-    fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
-    tmp = htonl(l->dead_space);
-    fwrite(&tmp, sizeof tmp, 1, fp);
-    tmp = htonl(l->theme);
-    fwrite(&tmp, sizeof tmp, 1, fp);
-    tmp = htonl(l->layout);
-    fwrite(&tmp, sizeof tmp, 1, fp);
-    tmp = htonl(l->chunks_high);
-    fwrite(&tmp, sizeof tmp, 1, fp);
-    tmp = htonl(l->chunks_wide);
-    fwrite(&tmp, sizeof tmp, 1, fp);
-    for (i = 0; i < l->chunks_high; ++i)
-    {
-        tmp_pair[0] = htonl(i);
-        for (j = 0; j < l->chunks_wide; ++j)
-        {
-            if (l->chunks[i][j])
-            {
-                tmp_pair[1] = htonl(j);
-                fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
-                serialize_chunk(fp, l->chunks[i][j]);
-            }
-        }
-    }
-    tmp_pair[0] = tmp_pair[1] = ~0u;
-    fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
-}
-
-/*! \brief Deserialize a Level
- *
- * \todo Throw an exception if the level is malformed e.g. OOB chunk indices.
- */
-void deserialize_level(FILE *fp, Level *l)
-{
-    uint32_t tmp;
-    uint32_t tmp_pair[2];
-    uint16_t tmp_shorts[2];
-    uint32_t i;
-    uint32_t j;
-    wrapped_fread(tmp_shorts, sizeof tmp_shorts[0], 2, fp);
-    l->self.dungeon = ntohs(tmp_shorts[0]);
-    l->self.depth = (int16_t) ntohs(tmp_shorts[1]);
-    wrapped_fread(tmp_pair, sizeof tmp_pair[0], 2, fp);
-    l->origin_off.y = (int32_t) ntohl(tmp_pair[0]);
-    l->origin_off.x = (int32_t) ntohl(tmp_pair[1]);
-    wrapped_fread(&tmp, sizeof tmp, 1, fp);
-    l->dead_space = Terrain(ntohl(tmp));
-    wrapped_fread(&tmp, sizeof tmp, 1, fp);
-    l->theme = level_theme(ntohl(tmp));
-    wrapped_fread(&tmp, sizeof tmp, 1, fp);
-    l->layout = level_layout(ntohl(tmp));
-    wrapped_fread(&tmp, sizeof tmp, 1, fp);
-    l->chunks_high = ntohl(tmp);
-    wrapped_fread(&tmp, sizeof tmp, 1, fp);
-    l->chunks_wide = ntohl(tmp);
-    initialize_chunks(l, l->chunks_high, l->chunks_wide, false);
-    do
-    {
-        wrapped_fread(&tmp_pair, sizeof tmp_pair[0], 2, fp);
-        i = ntohl(tmp_pair[0]);
-        j = ntohl(tmp_pair[1]);
-        if (i == ~0u)
-        {
-            break;
-        }
-        l->chunks[i][j] = new Chunk(l->dead_space);
-        deserialize_chunk(fp, l->chunks[i][j]);
-    }
-    while (1);
-}
-
 /*! \brief Rotate a connection bitmask */
 uint32_t rotate_connection_mask(uint32_t val, int clockwise_steps)
 {
diff --git a/map.hh b/map.hh
index 78d6875..8bf450d 100644 (file)
--- a/map.hh
+++ b/map.hh
@@ -43,16 +43,18 @@ enum Terrain {
 
 #define MAPFLAG_EXPLORED 0x00000001
 #define MAPFLAG_HARDWALL 0x00000002
-enum level_theme {
+enum Level_theme {
     THEME_NORMAL = 0, THEME_DRAGONS, THEME_DEMONS, THEME_UNDEAD
 };
 
-enum level_layout
+enum Level_layout
 {
     LAYOUT_CLASSIC_CAVE = 0,
     LAYOUT_CAVE_INTRUSIONS, /* the cave has hardened intrusions */
     LAYOUT_CAVE_SHRINE, /* the cave contains a shrine */
-    LAYOUT_DUNGEONBASH /* maybe not for this version: dungeonbash-style room grid */
+    LAYOUT_CAVE_PITS, /* "pits"-style cave. */
+    LAYOUT_DUNGEONBASH, /* dungeonbash-style room grid */
+    LAYOUT_CLAUSTROPHOBIA
 };
 
 #define NO_REGION 0xffffffffu
@@ -87,11 +89,12 @@ extern Terrain_props terrain_props[NUM_TERRAINS];
 #define TFLAG_block_beings  0x00000002u
 #define TFLAG_block_ether   0x00000004u
 #define TFLAG_block_missile 0x00000008u
-#define TFLAG_portal        0x00000010u
-#define TFLAG_ascend        0x00000020u
-#define TFLAG_descend       0x00000040u
-#define TFLAG_floor         0x00000080u
-#define TFLAG_block_items   0x00000100u
+#define TFLAG_block_items   0x00000010u
+#define TFLAG_portal        0x00000020u
+#define TFLAG_ascend        0x00000040u
+#define TFLAG_descend       0x00000080u
+#define TFLAG_floor         0x00000100u
+#define TFLAG_wall          0x00000200u
 #define TFLAG_fire_hazard   0x00010000u
 #define TFLAG_fall_hazard   0x00020000u
 #define TFLAG_drown_hazard  0x00040000u
@@ -166,15 +169,24 @@ extern Stair_detail const Bad_stairs;
 class Level
 {
 public:
+    Level_key self;
     Chunk ***chunks; //!< 16x16 subsections of the level, not necessarily dense
     Offset origin_off; //!< Don't force a map size change to recalculate all Coords
-    int chunks_high; //!< Chunkwise size of level in the y-direction
-    int chunks_wide; //!< Chunkwise size of level in the x-direction
+    uint32_t chunks_high; //!< Chunkwise size of level in the y-direction
+    uint32_t chunks_wide; //!< Chunkwise size of level in the x-direction
     Terrain dead_space; //!< Terrain to fill new chunks with and return for Coords in unpopulated chunks
-    level_theme theme; //!< Will affect monster and maybe item generation
-    level_layout layout; //!< Determines generation algorithm
-    Level_key self;
+    Level_theme theme; //!< Will affect monster and maybe item generation
+    Level_layout layout; //!< Determines generation algorithm
     std::deque<Stair_detail> stairs;
+    void *layout_data; //!< auxiliary data required by layout
+    void *theme_data; //!< auxiliary data required by layout
+    /* Member functions only past this point, please. */
+    Level() :
+        self(No_level), chunks(nullptr), origin_off(Stationary),
+        chunks_high(0), chunks_wide(0), dead_space(WALL), layout_data(nullptr),
+        theme_data(nullptr)
+    {
+    }
     Terrain terrain_at(Coord c) const
     {
         if (in_bounds(c))
@@ -324,12 +336,40 @@ public:
             }
         }
     }
+    uint32_t region_at(Coord c) const
+    {
+        if (in_bounds(c))
+        {
+
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->region_at(c2) : NO_REGION;
+        }
+        else
+        {
+            return NO_REGION;
+        }
+    }
+    void set_region_at(Coord c, uint32_t r)
+    {
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_region_at(c2, r);
+            }
+        }
+    }
     Coord random_point(int margin) const;
     bool in_bounds(Coord c) const
     {
         c += origin_off;
-        return !((c.y < 0) || (c.x < 0) || (c.y >= (chunks_high << CHUNK_SHIFT)) ||
-                 (c.x >= (chunks_wide << CHUNK_SHIFT)));
+        return !((c.y < 0) || (c.x < 0) || (c.y >= int32_t(chunks_high << CHUNK_SHIFT)) ||
+                 (c.x >= int32_t(chunks_wide << CHUNK_SHIFT)));
     }
     int min_y() const
     {
@@ -359,8 +399,6 @@ public:
 extern Level lvl;
 
 extern int depth;
-extern enum level_theme current_theme;
-extern enum level_layout current_layout;
 
 void leave_level(void);
 void make_new_level(void);
@@ -368,6 +406,8 @@ void build_level(void);
 void populate_level(void);
 void inject_player(Level *l, Level_key from);
 void look_around_you(void);
+bool terrain_is_wall(Terrain terr);
+bool terrain_is_floor(Terrain terr);
 bool terrain_is_opaque(Terrain terr);
 bool terrain_is_hot(Terrain terr);
 bool terrain_gapes(Terrain terr);
index 4f16841..8d14745 100644 (file)
--- a/mapgen.hh
+++ b/mapgen.hh
 #define GUIDE_EDGE_CHUNKS 3
 #define GUIDE_EDGE_SIZE (GUIDE_EDGE_CHUNKS << CHUNK_SHIFT)
 
-extern int depth;
-extern enum level_theme current_theme;
-extern enum level_layout current_layout;
-
 void build_level_shrine(Level *l);
 void build_level_intrusions(Level *l);
 void build_level_cave(Level *l);
+void build_level_pits(Level *l, Terrain t = CHASM);
 void build_level_dungeonbash(Level *l);
+void build_level_claustrophobia(Level *l);
 Pass_fail get_levgen_mon_floor(Level *l, Coord *c);
 int excavation_write(Level *l, Coord c, void const *data);
 int intrusion_write(Level *l, Coord c, void const *data);
@@ -67,6 +65,13 @@ Coord run_random_walk(Level *l, Coord oc, rwalk_mod_funcptr func,
 Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
                                 void const *priv_ptr, int cells);
 
+struct Obumb_levgen_excep
+{
+    char const *str;
+    Obumb_levgen_excep() = delete;
+    Obumb_levgen_excep(char const *s) : str(s) { }
+};
+
 #endif
 
 /* mapgen.hh */
diff --git a/mon1.cc b/mon1.cc
index 1564368..f4dbf84 100644 (file)
--- a/mon1.cc
+++ b/mon1.cc
@@ -201,23 +201,17 @@ bool Mon::can_pass(Coord c) const
         /* Let's *not* stuff all the wall types into a switch, eh? */
         return false;
     }
-    /* Keep the switch, so that we can maintain a convenient distinction
-     * between floor hazards and volumetric hazards. */
-    switch (terr)
+    if (terrain_is_hot(terr) && !can_fly() && !resists(DT_FIRE))
     {
-    case LAVA:
-        if (!can_fly() && !resists(DT_FIRE))
-        {
-            return false;
-        }
-        break;
-    case WATER:
-        if (!can_fly() && !resists(DT_DROWNING))
-        {
-            return false;
-        }
-    default:
-        break;
+       return false;
+    }
+    if (terrain_drowns(terr) && !can_fly() && !resists(DT_DROWNING))
+    {
+       return false;
+    }
+    if (terrain_gapes(terr) && !can_fly())
+    {
+       return false;
     }
     return true;
 }
index be90bf1..fb5df2e 100644 (file)
--- a/notes.txt
+++ b/notes.txt
@@ -46,11 +46,11 @@ is cheating, and cheating sucks.
 
 ABOUT THE GAME
 --------------
-Vana salus.
-
 Obumbrata et Velata is Martin Read's entry in the 2014 Seven Day Roguelike
 Challenge.
 
+It is, however you look at it, not even remotely fair and balanced.
+
 REPORTING BUGS
 --------------
 Report bugs by e-mail to martin (at) blackswordsonics dot com.
index 2e60718..20cf9f7 100644 (file)
@@ -85,9 +85,6 @@ void notify_death(Death d, char const *what)
     case DEATH_LASH:
         print_msg("You tasted the lash one time too many.\n");
         break;
-    case DEATH_RIBBONS:
-        print_msg("You looked good in ribbons.\n");
-        break;
     }
     print_msg("Your game lasted %d ticks.\n", game_tick);
     print_msg("You killed monsters worth %d experience.\n", u.experience);
@@ -239,7 +236,7 @@ void notify_start_waterwalk(Terrain t)
 
 void notify_blocked_water(Terrain t)
 {
-    print_msg("The idiot who raised you never taught you to swim.\n");
+    print_msg("You never learned to swim.\n");
 }
 
 void notify_portal_underfoot(void)
@@ -685,16 +682,21 @@ void notify_ring_unequip(Obj_handle obj)
     print_msg("You remove your ring.\n");
 }
 
-void notify_lava_blocks_unequip(void)
+void notify_hot_blocks_unequip(void)
 {
-    print_msg(Msg_prio::Warn, "That item is your only current source of fire resistance; setting it aside here would incinerate you.\n");
+    print_msg(Msg_prio::Warn, "Setting that item aside here would incinerate you.\n");
 }
 
-void notify_water_blocks_unequip(void)
+void notify_wet_blocks_unequip(void)
 {
     print_msg(Msg_prio::Warn, "Setting that item aside here would cause your death by drowning.\n");
 }
 
+void notify_pit_blocks_unequip(void)
+{
+    print_msg(Msg_prio::Warn, "Setting that item aside here would send you plummetting to your death.\n");
+}
+
 void notify_player_touch_effect(Damtyp dt)
 {
     switch (dt)
@@ -728,7 +730,7 @@ void notify_player_ignore_damage(Damtyp dt)
     switch (dt)
     {
     case DT_FIRE:
-        print_msg("The feel a pleasant warmth.\n");
+        print_msg("You feel a pleasant warmth.\n");
         break;
     case DT_COLD:
         print_msg("You feel a pleasant chill.\n");
index 134bfdb..833000c 100644 (file)
--- a/notify.hh
+++ b/notify.hh
@@ -82,8 +82,9 @@ void notify_ring_equip(Obj_handle obj);
 void notify_ring_unequip(Obj_handle obj);
 void notify_armour_equip(Obj_handle obj);
 void notify_armour_unequip(Obj_handle obj);
-void notify_lava_blocks_unequip(void);
-void notify_water_blocks_unequip(void);
+void notify_hot_blocks_unequip(void);
+void notify_wet_blocks_unequip(void);
+void notify_pit_blocks_unequip(void);
 void notify_nothing_to_get(void);
 void notify_unwield(Obj_handle obj, Noisiness noisy);
 void notify_wield_weapon(Obj_handle obj);
diff --git a/obj1.cc b/obj1.cc
index 5e202d9..802e1b1 100644 (file)
--- a/obj1.cc
+++ b/obj1.cc
@@ -38,27 +38,6 @@ const Obj_handle NO_OBJ = 0u;
 
 int get_random_pobj(void);
 
-char const ring_colours[20][16] = {
-    "gold", "ruby", "sapphire", "ivory", "coral",
-    "amethyst", "silver", "iron", "copper", "jade",
-    "haematite", "bone", "crystal", "platinum", "lead",
-    "diamond", "topaz", "emerald", "electrum", "smoky quartz"
-};
-
-char const scroll_titles[20][16] = {
-    "grem pho", "terra terrax", "phong", "ateh malkuth", "xixaxa",
-    "aku ryo tai san", "qoph shin tau", "ythek shri", "ia ia", "cthulhu fhtagn",
-    "arifech malex", "DOOM", "leme athem", "hail smkznrf", "rorrim foo",
-    "ad aerarium", "ligemrom", "asher ehiyeh", "YELLOW SIGN", "ELDER SIGN"
-};
-
-char const potion_colours[20][16] = {
-    "purple", "red", "blue", "green", "yellow",
-    "orange", "white", "black", "brown", "fizzy",
-    "grey", "silver", "gold", "shimmering", "glowing",
-    "navy blue", "bottle green", "amber", "lilac", "ivory"
-};
-
 /*! \brief Read a magic scroll */
 Action_cost read_scroll(Obj_handle obj)
 {
@@ -169,79 +148,6 @@ Action_cost quaff_potion(Obj_handle obj)
     return Cost_none;
 }
 
-void flavours_init(void)
-{
-    int colour_choices[10];
-    int i;
-    int j;
-    int done;
-    /* Flavoured items use "power" to track their flavour.  This is a
-     * gross and unforgiveable hack. */
-    /* Rings */
-    for (i = 0; i < 10;)
-    {
-        colour_choices[i] = zero_die(20);
-        done = 1;
-        for (j = 0; j < i; j++)
-        {
-            if (colour_choices[i] == colour_choices[j])
-            {
-                done = 0;
-            }
-        }
-        if (done)
-        {
-            i++;
-        }
-    }
-    permobjs[PO_REGENERATION_RING].power = colour_choices[0];
-    permobjs[PO_FIRE_RING].power = colour_choices[1];
-    permobjs[PO_VAMPIRE_RING].power = colour_choices[2];
-    permobjs[PO_FROST_RING].power = colour_choices[3];
-    permobjs[PO_TELEPORT_RING].power = colour_choices[4];
-    /* Scrolls */
-    for (i = 0; i < 10;)
-    {
-        colour_choices[i] = zero_die(20);
-        done = 1;
-        for (j = 0; j < i; j++)
-        {
-            if (colour_choices[i] == colour_choices[j])
-            {
-                done = 0;
-            }
-        }
-        if (done)
-        {
-            i++;
-        }
-    }
-    permobjs[PO_FIRE_SCROLL].power = colour_choices[0];
-    permobjs[PO_TELEPORT_SCROLL].power = colour_choices[1];
-    permobjs[PO_PROTECTION_SCROLL].power = colour_choices[2];
-    /* Potions */
-    for (i = 0; i < 10;)
-    {
-        colour_choices[i] = zero_die(20);
-        done = 1;
-        for (j = 0; j < i; j++)
-        {
-            if (colour_choices[i] == colour_choices[j])
-            {
-                done = 0;
-            }
-        }
-        if (done)
-        {
-            i++;
-        }
-    }
-    permobjs[PO_HEALING_POTION].power = colour_choices[0];
-    permobjs[PO_BODY_POTION].power = colour_choices[1];
-    permobjs[PO_AGILITY_POTION].power = colour_choices[2];
-    permobjs[PO_RESTORATION_POTION].power = colour_choices[3];
-}
-
 Obj_handle first_free_obj_handle = 1u;
 
 Obj_handle get_first_free_obj(void)
@@ -487,17 +393,59 @@ Action_cost drop_obj(int inv_idx)
     }
 }
 
-bool po_is_stackable(int po)
+Damtyp po_resistance(int po)
 {
-    switch (permobjs[po].poclass)
+    if ((po < 0) || (po >= NUM_OF_PERMOBJS))
     {
-    default:
-        return false;
-    case POCLASS_POTION:
-    case POCLASS_SCROLL:
-    case POCLASS_FOOD:
-        return true;
+       return DT_NONE;
+    }
+    uint32_t res = permobjs[po].flags[1] & POF_RES_MASK;
+    // Items never grant physical damage resistance
+    if ((res > 0) && (res <= LAST_DAMTYPE))
+    {
+       return Damtyp(res);
+    }
+    return DT_NONE;
+}
+
+Damtyp po_damage_amp(int po)
+{
+    if ((po < 0) || (po >= NUM_OF_PERMOBJS))
+    {
+       return DT_NONE;
+    }
+    uint32_t dt = (permobjs[po].flags[1] & POF_DMG_MASK) >> POF_DMG_SHIFT;
+    // Items never grant physical damage amplification
+    if ((dt > 0) && (dt <= LAST_DAMTYPE))
+    {
+       return Damtyp(dt);
     }
+    return DT_NONE;
+}
+
+bool po_is_stackable(int po)
+{
+    return (permobjs[po].flags[0] & POF_STACKABLE);
+}
+
+bool po_grants_speed(int po)
+{
+    return (permobjs[po].flags[1] & POF_SPEED);
+}
+
+bool po_grants_flight(int po)
+{
+    return (permobjs[po].flags[1] & POF_FLIGHT);
+}
+
+bool po_grants_protective(int po)
+{
+    return (permobjs[po].flags[1] & POF_PROTECTIVE);
+}
+
+bool po_grants_passwater(int po)
+{
+    return (permobjs[po].flags[1] & POF_PASS_WATER);
 }
 
 void attempt_pickup(void)
@@ -762,21 +710,35 @@ Action_cost remove_ring(void)
     return Cost_std;
 }
 
-Pass_fail ring_removal_unsafe(Noisiness noisy)
+Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy)
 {
-    if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
+    Terrain t = lvl.terrain_at(u.pos);
+    if (terrain_is_hot(t) &&
+       (!(u.resistances[DT_FIRE] & ~mask)) &&
+       (!(u.flight & ~mask)))
+    {
+        if (noisy != Noise_silent)
+        {
+            notify_hot_blocks_unequip();
+        }
+        return You_fail;
+    }
+    else if (terrain_drowns(t) &&
+            (!(u.passwater & ~mask)) &&
+            (!(u.flight & ~mask)))
     {
         if (noisy != Noise_silent)
         {
-            notify_lava_blocks_unequip();
+            notify_wet_blocks_unequip();
         }
         return You_fail;
     }
-    else if ((objects[u.ring].po_ref == PO_FROST_RING) && (lvl.terrain_at(u.pos) == WATER))
+    else if (terrain_gapes(t) &&
+            (!(u.flight & ~mask)))
     {
         if (noisy != Noise_silent)
         {
-            notify_water_blocks_unequip();
+            notify_pit_blocks_unequip();
         }
         return You_fail;
     }
index 0844e2d..1a38c82 100644 (file)
@@ -121,8 +121,7 @@ Action_cost player_unwield(Noisiness noisy = Noise_std);
 Action_cost player_wield(Obj_handle obj, Noisiness noisy = Noise_std);
 void attempt_pickup(void);
 
-Pass_fail ring_removal_unsafe(Noisiness noisy = Noise_std);
-Pass_fail armour_removal_unsafe(Noisiness noisy = Noise_std);
+Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy = Noise_std);
 
 #endif
 
index f0838b4..126a35e 100644 (file)
@@ -42,7 +42,7 @@
 #include "core.hh"
 
 /* change WIZARD_MODE to 1 if you want the wizard mode commands. */
-#define WIZARD_MODE 0
+#define WIZARD_MODE 1
 
 #ifndef PLAYER_HH
 #include "player.hh"
@@ -73,7 +73,7 @@ void new_game(char const *name, Role_id role);
 void main_loop(void);
 
 /* XXX misc.c data and funcs */
-extern char const *damtype_names[DT_COUNT];
+extern char const *damtype_names[NUM_DAMTYPES];
 
 /* XXX  log.cc */
 extern void log_death(Death d, char const *what);
index 36bb17b..386d1c2 100644 (file)
@@ -70,23 +70,26 @@ enum poclass_num {
 #define POF_DRESS           0x00010000u
 #define POF_MELEE_WEAPON    0x00010000u
 #define POF_RANGED_WEAPON   0x00020000u
+#define POF_DEMONIC         0x10000000u
 
 // POF field 1
-#define POF_RES_FIRE    0x00000001u
-#define POF_RES_COLD    0x00000002u
-#define POF_RES_ELEC    0x00000003u
-#define POF_RES_NECRO   0x00000004u
-#define POF_RES_POISON  0x00000005u
+#define POF_RES_FIRE    ((uint32_t) DT_FIRE)
+#define POF_RES_COLD    ((uint32_t) DT_COLD)
+#define POF_RES_ELEC    ((uint32_t) DT_NECRO)
+#define POF_RES_NECRO   ((uint32_t) DT_ELEC)
+#define POF_RES_POISON  ((uint32_t) DT_POISON)
 #define POF_RES_MASK    0x000000ffu
-#define POF_DMG_FIRE    0x00000100u
-#define POF_DMG_COLD    0x00000200u
-#define POF_DMG_ELEC    0x00000300u
-#define POF_DMG_NECRO   0x00000400u
-#define POF_DMG_POISON  0x00000500u
-#define POF_DMG_MASK    0x0000ff00u
+#define POF_DMG_FIRE    (((uint32_t) DT_FIRE) << POF_DMG_SHIFT)
+#define POF_DMG_COLD    (((uint32_t) DT_COLD) << POF_DMG_SHIFT)
+#define POF_DMG_ELEC    (((uint32_t) DT_ELEC) << POF_DMG_SHIFT)
+#define POF_DMG_NECRO   (((uint32_t) DT_NECRO) << POF_DMG_SHIFT)
+#define POF_DMG_POISON  (((uint32_t) DT_POISON) << POF_DMG_SHIFT)
+#define POF_DMG_SHIFT   8
+#define POF_DMG_MASK    (0xffu << POF_DMG_SHIFT)
 #define POF_PASS_WATER  0x00010000u
 #define POF_FLIGHT      0x00020000u
 #define POF_PROTECTIVE  0x00100000u // all damage from enemy attacks halved
+#define POF_SPEED       0x01000000u // bumps you up one speed band
 
 /*! \brief The 'permanent object' database */
 class Permobj {
@@ -108,7 +111,14 @@ public:
 
 extern const int NUM_OF_PERMOBJS;
 extern Permobj permobjs[];
-extern bool po_is_stackable(int po);
+
+bool po_is_stackable(int po);
+Damtyp po_resistance(int po);
+Damtyp po_damage_amp(int po);
+bool po_grants_flight(int po);
+bool po_grants_passwater(int po);
+bool po_grants_protective(int po);
+bool po_grants_speed(int po);
 
 #endif
 
index bcdf25c..a8301c7 100644 (file)
--- a/player.hh
+++ b/player.hh
@@ -83,7 +83,11 @@ public:
     int withering;  //!< Vile withering curse.
     int armourmelt; //!< Armour-like-dust curse.
     int speed;      //!< Controls how often you act.
-    uint32_t resistances[DT_COUNT]; //!< Resistance masks per damage type
+    uint32_t resistances[NUM_DAMTYPES]; //!< Resistance masks per damage type
+    uint32_t damage_amp[NUM_DAMTYPES]; //!< Damage amplification masks per damage type
+    uint32_t passwater; //!< Can currently walk on water tiles
+    uint32_t flight;   //!< Ignore all hazardfloors and pits
+    uint32_t protective_gear;   //!< Reduce all damage taken
     int level;      //!< Current experience level.
     Obj_handle inventory[INVENTORY_SIZE];  //!< currently carried items.
     Obj_handle weapon;     //!< currently equipped weapon.
index 3e89549..759778f 100755 (executable)
--- a/pobj_comp
+++ b/pobj_comp
@@ -30,6 +30,7 @@ our %flag_indices =
     'DRESS' => 0,
     'RANGED_WEAPON' => 0,
     'MELEE_WEAPON' => 0,
+    'DEMONIC' => 0,
     'RES_FIRE' => 1,
     'RES_COLD' => 1,
     'RES_ELEC' => 1,
@@ -42,7 +43,8 @@ our %flag_indices =
     'DMG_POISON' => 1,
     'PASS_WATER' => 1,
     'FLIGHT' => 1,
-    'PROTECTIVE' => 1
+    'PROTECTIVE' => 1,
+    'SPEED' => 1
 );
 
 
index 021be67..5382d82 100644 (file)
@@ -2,7 +2,7 @@
  *  \brief evil magic used by monsters
  */
 
-/* Copyright 2005-2013 Martin Read
+/* Copyright Â© 2005-2014 Martin Read
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * with high enough agility.
  */
 
-int mon_use_sorcery(int mon)
+typedef void (*Monspell_cast_func)(Mon_handle mon);
+
+static void cast_strike_staff(Mon_handle mon)
 {
-    /* Returns zero for no spell selected, -1 for unsupported spell
-     * selected, 1 for supported spell selected. */
-    Mon *mptr = mon_snapv(mon);
-    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;
-    bool cansee = mon_visible(mon);
-    int i;
+    mhitu(mon, DT_PHYS);
+}
+
+static void cast_necro_staff(Mon_handle mon)
+{
+    mhitu(mon, DT_NECRO);
+}
+
+static void cast_chilling_touch(Mon_handle mon)
+{
+    mhitu(mon, DT_COLD);
+}
 
-    switch (monsters[mon].pm_ref)
+static void cast_lightning_bolt(Mon_handle mon)
+{
+    mshootu(mon);
+}
+
+static void cast_necro_bolt(Mon_handle mon)
+{
+    mshootu(mon);
+}
+
+static void cast_necro_smite(Mon_handle mon)
+{
+    notify_monster_cursing(mon);
+    if (u.resists(DT_NECRO))
     {
-    case PM_ARCHMAGE:
-        if (cansee)
-        {
-            /* We have LOS; choose a spell on that basis. */
-            if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 2))
-            {
-                to_cast = zero_die(3) ? MS_TELEPORT_ESCAPE : MS_TELEPORT_AND_SUMMON;
-            }
-            else if (meleerange && (zero_die(10) > 3))
-            {
-                to_cast = MS_STRIKE_STAFF;
-            }
-            else if (oncardinal)
-            {
-                to_cast = MS_LIGHTNING;
-            }
-        }
-        else if (!zero_die(40))
-        {
-            /* 
-             * We lack LOS, but pass the 1-in-40 chance; use
-             * sorcery to relocate us to the player's location.
-             */
-            to_cast = MS_TELEPORT_ASSAULT;
-        }
-        break;
+       notify_necrosmite_fail();
+    }
+    else
+    {
+       damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
+    }
+}
 
-    case PM_WIZARD:
-        if (cansee)
-        {
-            if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 2))
-            {
-                to_cast = MS_TELEPORT_ESCAPE;
-            }
-            else if (meleerange && (zero_die(10) > 2))
-            {
-                to_cast = MS_STRIKE_STAFF;
-            }
-            else if (oncardinal)
-            {
-                to_cast = MS_LIGHTNING;
-            }
-        }
-        else if (!zero_die(80))
-        {
-            /* we lack LOS, but passed the 1-in-80 chance to
-             * close with the player by means of sorcery. */
-            to_cast = MS_TELEPORT_ASSAULT;
-        }
-        break;
+static void cast_fire_column(Mon_handle mon)
+{
+    notify_monster_cursing(mon);
+    notify_hellfire_hit(u.resists(DT_FIRE));
+    if (u.resists(DT_FIRE))
+    {
+       damage_u(dice(1, 5), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
+    }
+    else
+    {
+       damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
+    }
+}
+
+static void cast_teleport_escape(Mon_handle mon)
+{
+    teleport_mon(mon);
+    notify_mon_disappear(mon);
+}
 
-    case PM_MASTER_LICH:
-        if (cansee)
-        {
-            if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 4))
-            {
-                to_cast = ((mptr->next_summon < game_tick) || !zero_die(3)) ? MS_TELEPORT_ESCAPE : MS_TELEPORT_AND_SUMMON;
-            }
-            else if (meleerange)
-            {
-                switch (zero_die(7))
-                {
-                case 6:
-                    if (!u.withering)
-                    {
-                        to_cast = MS_CURSE_WITHERING;
-                        break;
-                    }
-                case 5:
-                    if (!u.leadfoot)
-                    {
-                        to_cast = MS_CURSE_LEADFOOT;
-                        break;
-                    }
-                    /* fall through */
-                case 4:
-                    if (!u.armourmelt)
-                    {
-                        to_cast = MS_CURSE_ARMOURMELT;
-                        break;
-                    }
-                    /* fall through */
-                default:
-                    to_cast = zero_die(2) ? MS_CHILLING_TOUCH : MS_STRIKE_STAFF;
-                    break;
-                }
-            }
-            else if (range < 3)
-            {
-                switch (zero_die(10))
-                {
-                case 9:
-                    if (!u.withering)
-                    {
-                        to_cast = MS_CURSE_WITHERING;
-                        break;
-                    }
-                case 8:
-                    if (!u.leadfoot)
-                    {
-                        to_cast = MS_CURSE_LEADFOOT;
-                        break;
-                    }
-                    /* fall through */
-                case 7:
-                    if (!u.armourmelt)
-                    {
-                        to_cast = MS_CURSE_ARMOURMELT;
-                        break;
-                    }
-                    /* fall through */
-                default:
-                    to_cast = MS_NECRO_SMITE;
-                    break;
-                }
-            }
-            else if (range < 8)
-            {
-                switch (zero_die(7))
-                {
-                case 6:
-                    if (!u.withering)
-                    {
-                        to_cast = MS_CURSE_WITHERING;
-                        break;
-                    }
-                case 4:
-                    if (!u.leadfoot)
-                    {
-                        to_cast = MS_CURSE_LEADFOOT;
-                        break;
-                    }
-                    /* fall through */
-                case 5:
-                    if (!u.armourmelt)
-                    {
-                        to_cast = MS_CURSE_ARMOURMELT;
-                        break;
-                    }
-                    /* fall through */
-                default:
-                    to_cast = MS_NECRO_SMITE;
-                    break;
-                }
-            }
-        }
-        else if (!zero_die(40))
-        {
-            /* we lack LOS, but passed the 1-in-40 chance to
-             * close with the player by means of sorcery. */
-            to_cast = MS_TELEPORT_ASSAULT;
-        }
-        break;
-    case PM_LICH:
-        if (cansee)
-        {
-            if (meleerange)
-            {
-                dieroll = zero_die(6);
-                switch (dieroll)
-                {
-                case 4:
-                    if (!u.leadfoot)
-                    {
-                        to_cast = MS_CURSE_LEADFOOT;
-                        break;
-                    }
-                    /* fall through */
-                case 5:
-                    if (!u.armourmelt)
-                    {
-                        to_cast = MS_CURSE_ARMOURMELT;
-                        break;
-                    }
-                    /* fall through */
-                default:
-                    to_cast = MS_NECRO_STAFF;
-                    break;
-                }
-            }
-            else if (oncardinal)
-            {
-                if (range < 3)
-                {
-                    switch (zero_die(6))
-                    {
-                    case 4:
-                        if (!u.leadfoot)
-                        {
-                            to_cast = MS_CURSE_LEADFOOT;
-                            break;
-                        }
-                        /* fall through */
-                    case 5:
-                        if (!u.armourmelt)
-                        {
-                            to_cast = MS_CURSE_ARMOURMELT;
-                            break;
-                        }
-                        /* fall through */
-                    default:
-                        to_cast = MS_NECRO_BOLT;
-                        break;
-                    }
-                }
-                else
-                {
-                    to_cast = MS_NECRO_BOLT;
-                }
-            }
-            break;
-        }
-        break;
-    case PM_DEFILER:
-        if (cansee)
-        {
-            if (!meleerange || !zero_die(3))
-            {
-                // 1-in-7 chance of cursing you when in melee range
-                // 3-in-7 chance when out of melee range
-                switch (zero_die(7))
-                {
-                case 6:
-                    if (!u.withering)
-                    {
-                        to_cast = MS_CURSE_WITHERING;
-                        break;
-                    }
-                case 4:
-                    if (!u.leadfoot)
-                    {
-                        to_cast = MS_CURSE_LEADFOOT;
-                        break;
-                    }
-                    /* fall through */
-                case 5:
-                    if (!u.armourmelt)
-                    {
-                        to_cast = MS_CURSE_ARMOURMELT;
-                        break;
-                    }
-                    /* fall through */
-                default:
-                    to_cast = MS_REJECT;
-                    break;
-                }
-            }
-        }
-        break;
+static void cast_teleport_summon(Mon_handle mon)
+{
+    Mon *mptr = mon_snapv(mon);
+    int i = summoning(mptr->pos, dice(2, 3));
+    mptr->next_summon = game_tick + 1000;
+    notify_summon_help(mon, (i > 0));
+    teleport_mon(mon);
+    notify_mon_disappear(mon);
+}
+
+static void cast_teleport_assault(Mon_handle mon)
+{
+    teleport_mon_to_you(mon);
+}
 
-    default:
-        break;
+static void cast_withering(Mon_handle mon)
+{
+    notify_monster_cursing(mon);
+    if (u.protection)
+    {
+       notify_moncurse_fail();
     }
-    switch (to_cast)
+    else
     {
-    default:
-        /* If this happens, we're trying to cast an unimplemented
-         * spell. */
-        debug_bad_monspell(to_cast);
-        rval = -1;
-        break;
+       u.withering = 10 + one_die(10);
+       recalc_defence();
+       notify_start_withering();
+    }
+}
 
-    case MS_REJECT:
-        /* No usable spell available. */
-        rval = 0;
-        break;
+static void cast_leadfoot(Mon_handle mon)
+{
+    notify_monster_cursing(mon);
+    if (u.protection)
+    {
+       notify_moncurse_fail();
+    }
+    else
+    {
+       u.leadfoot = 10 + one_die(10);
+       recalc_defence();
+       notify_start_leadfoot();
+    }
+}
 
-    case MS_STRIKE_STAFF:
-        mhitu(mon, DT_PHYS);
-        break;
+static void cast_armourmelt(Mon_handle mon)
+{
+    notify_monster_cursing(mon);
+    if (u.protection)
+    {
+       notify_moncurse_fail();
+    }
+    else
+    {
+       u.armourmelt = 10 + one_die(10);
+       recalc_defence();
+       notify_start_armourmelt();
+    }
+}
 
-    case MS_NECRO_STAFF:
-        mhitu(mon, DT_NECRO);
-        break;
+static void cast_animate_dead(Mon_handle mon)
+{
+}
 
-    case MS_CHILLING_TOUCH:
-        mhitu(mon, DT_COLD);
-        break;
+Monspell_cast_func sorcery_funcs[NUM_MONSPELLS] =
+{
+    cast_strike_staff,
+    cast_necro_staff,
+    cast_chilling_touch,
+    cast_lightning_bolt,
+    cast_necro_bolt,
+    cast_necro_smite,
+    cast_fire_column,
+    cast_armourmelt,
+    cast_leadfoot,
+    cast_withering,
+    cast_teleport_escape,
+    cast_teleport_summon,
+    cast_teleport_assault,
+    cast_animate_dead
+};
 
-    case MS_LIGHTNING:
-    case MS_NECRO_BOLT:
-        mshootu(mon);
-        break;
+typedef Monspell (*Monspell_select_func)(int mon);
+struct Monspell_selector
+{
+    int pm;
+    Monspell_select_func func;
+};
 
-    case MS_TELEPORT_AND_SUMMON:
-        mptr->next_summon = game_tick + 1000;
-        /* (Try to) summon 2-6 monsters. */
-        i = summoning(mptr->pos, dice(2, 3));
-        notify_summon_help(mon, (i > 0));
-        /* ... and fall through. */
-    case MS_TELEPORT_ESCAPE:
-        teleport_mon(mon);
-        notify_mon_disappear(mon);
-        break;
+Monspell wizard_spell_select(int mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    Offset delta = u.pos.delta(mptr->pos);
+    if (mon_visible(mon))
+    {
+       if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 2))
+       {
+           return MS_TELEPORT_ESCAPE;
+       }
+       else if ((delta.len_cheb() == 1) && (zero_die(10) > 2))
+       {
+           return MS_STRIKE_STAFF;
+       }
+       else if (delta.rcardinal())
+       {
+           return MS_LIGHTNING;
+       }
+    }
+    else if (!zero_die(80))
+    {
+       /* we lack LOS, but passed the 1-in-80 chance to
+        * close with the player by means of sorcery. */
+       return MS_TELEPORT_ASSAULT;
+    }
+    return MS_REJECT;
+}
 
-    case MS_TELEPORT_ASSAULT:
-        /* It is rare that a monster will cast this spell, but not
-         * unheard of. */
-        teleport_mon_to_you(mon);
-        break;
+Monspell archmage_spell_select(int mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    Offset delta = u.pos.delta(mptr->pos);
+    if (mon_visible(mon))
+    {
+       /* We have LOS; choose a spell on that basis. */
+       if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 2))
+       {
+           return zero_die(3) ? MS_TELEPORT_ESCAPE : MS_TELEPORT_AND_SUMMON;
+       }
+       else if ((delta.len_cheb() == 1) && (zero_die(10) > 3))
+       {
+           return MS_STRIKE_STAFF;
+       }
+       else if (delta.rcardinal())
+       {
+           return MS_LIGHTNING;
+       }
+    }
+    else if (!zero_die(40))
+    {
+       /* 
+        * We lack LOS, but pass the 1-in-40 chance; use
+        * sorcery to relocate us to the player's location.
+        */
+       return MS_TELEPORT_ASSAULT;
+    }
+    return MS_REJECT;
+}
 
-    case MS_CURSE_ARMOURMELT:
-        notify_monster_cursing(mon);
-        if (u.protection)
-        {
-            notify_moncurse_fail();
-        }
-        else
-        {
-            u.armourmelt = 10 + one_die(10);
-            recalc_defence();
-            notify_start_armourmelt();
-        }
-        break;
+Monspell lich_spell_select(int mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    Offset delta = u.pos.delta(mptr->pos);
+    int range = delta.len_cheb();
+    if (mon_visible(mon))
+    {
+       if (range == 1)
+       {
+           switch (zero_die(6))
+           {
+           case 4:
+               if (!u.leadfoot)
+               {
+                   return MS_CURSE_LEADFOOT;
+               }
+               /* fall through */
+           case 5:
+               if (!u.armourmelt)
+               {
+                   return MS_CURSE_ARMOURMELT;
+               }
+               /* fall through */
+           default:
+               return MS_NECRO_STAFF;
+           }
+       }
+       else if (delta.rcardinal())
+       {
+           if (range < 3)
+           {
+               switch (zero_die(6))
+               {
+               case 4:
+                   if (!u.leadfoot)
+                   {
+                       return MS_CURSE_LEADFOOT;
+                   }
+                   /* fall through */
+               case 5:
+                   if (!u.armourmelt)
+                   {
+                       return MS_CURSE_ARMOURMELT;
+                   }
+                   /* fall through */
+               default:
+                   return MS_NECRO_BOLT;
+               }
+           }
+           else
+           {
+               return MS_NECRO_BOLT;
+           }
+       }
+    }
+    return MS_REJECT;
+}
 
-    case MS_CURSE_LEADFOOT:
-        notify_monster_cursing(mon);
-        if (u.protection)
-        {
-            notify_moncurse_fail();
-        }
-        else
-        {
-            u.leadfoot = 10 + one_die(10);
-            recalc_defence();
-            notify_start_leadfoot();
-        }
-        break;
+Monspell master_lich_spell_select(int mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    Offset delta = u.pos.delta(mptr->pos);
+    int range = delta.len_cheb();
+    if (mon_visible(mon))
+    {
+       if ((mptr->hpcur < (mptr->hpmax * 25 / 100)) && (zero_die(10) < 4))
+       {
+           return ((mptr->next_summon < game_tick) || !zero_die(3)) ? MS_TELEPORT_ESCAPE : MS_TELEPORT_AND_SUMMON;
+       }
+       else if (range == 1)
+       {
+           switch (zero_die(7))
+           {
+           case 6:
+               if (!u.withering)
+               {
+                   return MS_CURSE_WITHERING;
+               }
+           case 5:
+               if (!u.leadfoot)
+               {
+                   return MS_CURSE_LEADFOOT;
+               }
+               /* fall through */
+           case 4:
+               if (!u.armourmelt)
+               {
+                   return MS_CURSE_ARMOURMELT;
+               }
+               /* fall through */
+           default:
+               return zero_die(2) ? MS_CHILLING_TOUCH : MS_STRIKE_STAFF;
+           }
+       }
+       else if (range < 3)
+       {
+           switch (zero_die(10))
+           {
+           case 9:
+               if (!u.withering)
+               {
+                   return MS_CURSE_WITHERING;
+               }
+           case 8:
+               if (!u.leadfoot)
+               {
+                   return MS_CURSE_LEADFOOT;
+               }
+               /* fall through */
+           case 7:
+               if (!u.armourmelt)
+               {
+                   return MS_CURSE_ARMOURMELT;
+               }
+               /* fall through */
+           default:
+               return MS_NECRO_SMITE;
+           }
+       }
+       else if (range < 8)
+       {
+           switch (zero_die(7))
+           {
+           case 6:
+               if (!u.withering)
+               {
+                   return MS_CURSE_WITHERING;
+               }
+           case 4:
+               if (!u.leadfoot)
+               {
+                   return MS_CURSE_LEADFOOT;
+               }
+               /* fall through */
+           case 5:
+               if (!u.armourmelt)
+               {
+                   return MS_CURSE_ARMOURMELT;
+               }
+               /* fall through */
+           default:
+               return MS_NECRO_SMITE;
+           }
+       }
+    }
+    else if (!zero_die(40))
+    {
+       /* we lack LOS, but passed the 1-in-40 chance to
+        * close with the player by means of sorcery. */
+       return MS_TELEPORT_ASSAULT;
+    }
+    return MS_REJECT;
+}
 
-    case MS_CURSE_WITHERING:
-        notify_monster_cursing(mon);
-        if (u.protection)
-        {
-            notify_moncurse_fail();
-        }
-        else
-        {
-            u.withering = 10 + one_die(10);
-            recalc_defence();
-            notify_start_withering();
-        }
-        break;
+Monspell defiler_spell_select(int mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    Offset delta = u.pos.delta(mptr->pos);
+    if (mon_visible(mon))
+    {
+       if (!(delta.len_cheb() != 1) || !zero_die(3))
+       {
+           // 1-in-7 chance of cursing you when in melee range
+           // 3-in-7 chance when out of melee range
+           switch (zero_die(7))
+           {
+           case 6:
+               if (!u.withering)
+               {
+                   return MS_CURSE_WITHERING;
+               }
+           case 4:
+               if (!u.leadfoot)
+               {
+                   return MS_CURSE_LEADFOOT;
+               }
+               /* fall through */
+           case 5:
+               if (!u.armourmelt)
+               {
+                   return MS_CURSE_ARMOURMELT;
+               }
+               /* fall through */
+           default:
+               break;
+           }
+       }
+    }
+    return MS_REJECT;
+}
 
-    case MS_NECRO_SMITE:
-        notify_monster_cursing(mon);
-        if (u.resists(DT_NECRO))
-        {
-            notify_necrosmite_fail();
-        }
-        else
-        {
-            damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
-        }
-        break;
+Monspell_selector monspell_selectors[] = 
+{
+    { PM_WIZARD, wizard_spell_select },
+    { PM_ARCHMAGE, archmage_spell_select },
+    { PM_LICH, lich_spell_select },
+    { PM_MASTER_LICH, master_lich_spell_select },
+    { PM_DEFILER, defiler_spell_select },
+    { NO_PMON, nullptr }
+};
 
-    case MS_FIRE_COLUMN:
-        notify_monster_cursing(mon);
-        notify_hellfire_hit(u.resists(DT_FIRE));
-        if (u.resists(DT_FIRE))
-        {
-            damage_u(dice(1, 5), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
-        }
-        else
-        {
-            damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].pm_ref].name);
-        }
-        break;
+int mon_use_sorcery(int mon)
+{
+    /* Returns zero for no spell selected, -1 for unsupported spell
+     * selected, 1 for supported spell selected. */
+    Mon const *mptr = mon_snap(mon);
+    Monspell to_cast = MS_REJECT;
+    int rval;
+    for (int i = 0; monspell_selectors[i].func; ++i)
+    {
+       if (monspell_selectors[i].pm == mptr->pm_ref)
+       {
+           to_cast = monspell_selectors[i].func(mon);
+       }
+    }
+    if (to_cast == MS_REJECT)
+    {
+       rval = 0;
+    }
+    else if (!sorcery_funcs[to_cast])
+    {
+       debug_bad_monspell(to_cast);
+       rval = -1;
+    }
+    else
+    {
+       sorcery_funcs[to_cast](mon);
+       rval = 1;
     }
     return rval;
 }
index dfd647b..8da9b7a 100644 (file)
@@ -31,7 +31,7 @@
 
 /* XXX DATA TYPES XXX */
 
-enum monspell {
+enum Monspell {
     MS_REJECT = -1,         /* Rejection tag. */
     /* "Melee" attacks */
     MS_STRIKE_STAFF,        /* Wizard */
@@ -50,8 +50,13 @@ enum monspell {
     MS_TELEPORT_ESCAPE,     /* Wizard, Archmage, Master Lich */
     MS_TELEPORT_AND_SUMMON, /* Archmage */
     MS_TELEPORT_ASSAULT,    /* Wizard, Archmage, Master Lich */
+    /* Other */
+    MS_ANIMATE_DEAD,   /* Lich, Master Lich */
 };
 
+#define LAST_MONSPELL (MS_ANIMATE_DEAD)
+#define NUM_MONSPELLS (LAST_MONSPELL + 1)
+
 enum Monspell_mode
 {
     Msmode_melee,
diff --git a/u.cc b/u.cc
index a90f301..8afe1ae 100644 (file)
--- a/u.cc
+++ b/u.cc
@@ -47,52 +47,61 @@ bool wielding_melee_weapon(void)
     return (u.weapon != NO_OBJ) && (permobjs[objects[u.weapon].po_ref].flags[0] & POF_MELEE_WEAPON);
 }
 
-void recalc_defence(void)
+static void apply_bonuses(Obj_handle obj, uint32_t bonusmask)
 {
-    int i;
-    for (i = 0; i < DT_COUNT; i++)
+    if (obj != NO_OBJ)
     {
-        u.resistances[i] &= RESIST_MASK_TEMPORARY;
-    }
-    u.speed = (u.leadfoot ? 0 : 1);
-    if (u.armour != NO_OBJ)
-    {
-        Obj const *armour = obj_snap(u.armour);
-        u.defence = u.armourmelt ? 0 : permobjs[armour->po_ref].power;
-        u.defence += u.withering ? (u.agility / 10) : (u.agility / 5);
-        switch (armour->po_ref)
+        Obj const *optr = obj_snap(obj);
+        int resistance = po_resistance(optr->po_ref);
+        int dmg_amp = po_damage_amp(optr->po_ref);
+        if (resistance != DT_NONE)
+        {
+            u.resistances[resistance] |= BONUS_ARMOUR;
+        }
+        if (dmg_amp != DT_NONE)
+        {
+            u.damage_amp[dmg_amp] |= BONUS_ARMOUR;
+        }
+        if (po_grants_speed(optr->po_ref))
+        {
+            ++u.speed;
+        }
+        if (po_grants_passwater(optr->po_ref))
+        {
+            u.passwater |= bonusmask;
+        }
+        if (po_grants_flight(optr->po_ref))
         {
-        case PO_DRAGONHIDE_ARMOUR:
-        case PO_METEORIC_PLATE_ARMOUR:
-            u.resistances[DT_FIRE] |= RESIST_ARMOUR;
-            break;
-        case PO_ROBE_OF_SWIFTNESS:
-            u.speed++;
-            break;
-        default:
-            break;
+            u.flight |= bonusmask;
+        }
+        if (po_grants_protective(optr->po_ref))
+        {
+            u.protective_gear |= bonusmask;
         }
     }
-    else
+}
+
+void recalc_defence(void)
+{
+    int i;
+    for (i = 0; i < NUM_DAMTYPES; i++)
     {
-        u.defence = u.withering ? (u.agility / 10) : (u.agility / 5);
+        u.resistances[i] &= BONUS_MASK_TEMPORARY;
+        u.damage_amp[i] &= BONUS_MASK_TEMPORARY;
     }
-    if (u.ring != NO_OBJ)
+    u.passwater &= BONUS_MASK_TEMPORARY;
+    u.flight &= BONUS_MASK_TEMPORARY;
+    u.protective_gear &= BONUS_MASK_TEMPORARY;
+    u.speed = (u.leadfoot ? 0 : 1);
+    u.defence = u.withering ? (u.agility / 10) : (u.agility / 5);
+    if (u.armour != NO_OBJ)
     {
-        Obj const *ring = obj_snap(u.ring);
-        switch (ring->po_ref)
-        {
-        case PO_FIRE_RING:
-            u.resistances[DT_FIRE] |= RESIST_RING;
-            break;
-        case PO_FROST_RING:
-            u.resistances[DT_COLD] |= RESIST_RING;
-            break;
-        case PO_VAMPIRE_RING:
-            u.resistances[DT_NECRO] |= RESIST_RING;
-            break;
-        }
+        Obj const *aptr = obj_snap(u.armour);
+        u.defence += u.armourmelt ? 0 : permobjs[aptr->po_ref].power;
     }
+    apply_bonuses(u.armour, BONUS_ARMOUR);
+    apply_bonuses(u.ring, BONUS_RING);
+    apply_bonuses(u.weapon, BONUS_WEAPON);
     notify_defence_recalc();
 }
 
@@ -125,7 +134,7 @@ Action_cost move_player(Offset delta)
         notify_cant_go();
         return Cost_none;
     }
-    else if (terrain_is_hot(t))
+    else if ((!u.flight) && terrain_is_hot(t))
     {
         if (u.resistances[DT_FIRE])
         {
@@ -140,17 +149,13 @@ Action_cost move_player(Offset delta)
             return Cost_none;
         }
     }
-    else if (terrain_drowns(t))
+    else if ((!u.flight) && terrain_drowns(t))
     {
-        if (u.ring != NO_OBJ)
+        if (u.passwater)
         {
-            Obj const *ring = obj_snap(u.ring);
-            if (ring->po_ref == PO_FROST_RING)
+            if (!terrain_drowns(lvl.terrain_at(u.pos)))
             {
-                if (!terrain_drowns(lvl.terrain_at(u.pos)))
-                {
-                    notify_start_waterwalk(t);
-                }
+                notify_start_waterwalk(t);
             }
         }
         else
@@ -159,7 +164,7 @@ Action_cost move_player(Offset delta)
             return Cost_none;
         }
     }
-    else if (terrain_gapes(t))
+    else if ((!u.flight) && terrain_gapes(t))
     {
         notify_cant_go();
         return Cost_none;
@@ -296,8 +301,8 @@ void heal_u(int amount, int boost, int loud)
     else
     {
         boost = 0;
-        u.hpcur += amount;
     }
+    u.hpcur += amount;
     notify_player_heal(amount, boost, loud);
     return;
 }
@@ -642,6 +647,7 @@ void player_cleanup(void)
     int i;
     memset(u.sympathy, '\0', sizeof u.sympathy);
     memset(u.resistances, '\0', sizeof u.resistances);
+    memset(u.damage_amp, '\0', sizeof u.damage_amp);
     u.mptr = nullptr;
     u.mh = NO_MON;
     u.experience = u.level = 0;
@@ -653,6 +659,7 @@ void player_cleanup(void)
     u.food = 0;
     u.leadfoot = u.protection = u.withering = u.armourmelt = 0;
     u.level = 0;
+    u.flight = u.passwater = u.protective_gear = 0;
     u.weapon = u.armour = u.ring = NO_OBJ;
     for (i = 0; i < 19; ++i)
     {