Renaming monsters.cc to mon1.cc
authorMartin Read <mpread@chiark.greenend.org.uk>
Mon, 10 Mar 2014 16:18:43 +0000 (16:18 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Mon, 10 Mar 2014 16:18:43 +0000 (16:18 +0000)
mon1.cc [new file with mode: 0644]
monsters.cc [deleted file]

diff --git a/mon1.cc b/mon1.cc
new file mode 100644 (file)
index 0000000..04f8a59
--- /dev/null
+++ b/mon1.cc
@@ -0,0 +1,683 @@
+/*! \file monsters.cc
+ *  \brief Monster-related functions
+ */
+
+/* Copyright 2005-2014 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.
+ */
+
+#define MONSTERS_CC
+#include "obumbrata.hh"
+#include "monsters.hh"
+#include "objects.hh"
+
+const Mon_handle NO_MON = 0u;
+
+std::map<Mon_handle, Mon> monsters;
+static bool reject_mon(int pm);
+
+/*! \brief Summon some monsters
+ *
+ *  \return Number of monsters summoned
+ *  \param how_many Maximum number of monsters to summon
+ */
+int summoning(Coord c, int how_many)
+{
+    int i;
+    Offset delta;
+    Coord testpos;
+    int tryct;
+    Mon_handle mon;
+    int created = 0;
+    int pmon;
+    for (i = 0; i < how_many; i++)
+    {
+        for (tryct = 0; tryct < 20; tryct++)
+        {
+            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))
+                {
+                    /* Never summon magicians! */
+                    continue;
+                }
+                mon = create_mon(NO_PMON, testpos);
+                if (mon != NO_MON)
+                {
+                    created++;
+                    break;
+                }
+            }
+        }
+    }
+    return created;
+}
+
+int ood(int power, int ratio)
+{
+    return (depth - power + ratio - 1) / ratio;
+}
+
+int get_random_pmon(void)
+{
+    int tryct;
+    int pm;
+    for (tryct = 0; tryct < 200; tryct++)
+    {
+        pm = zero_die(NUM_OF_PERMONS);
+        if (reject_mon(pm))
+        {
+            pm = NO_PMON;
+            continue;
+        }
+        break;
+    }
+    return pm;
+}
+
+Mon_handle first_free_mon_handle = 1u;
+static Mon_handle get_first_free_mon(void)
+{
+    if (first_free_mon_handle == NO_MON)
+    {
+       return NO_MON;
+    }
+    return first_free_mon_handle++;
+}
+
+Mon_handle create_mon(int pm_idx, Coord c)
+{
+    Mon_handle mon;
+    if (lvl.mon_at(c) != NO_MON)
+    {
+        debug_create_mon_occupied(c);
+        return NO_MON;
+    }
+    if (pm_idx == NO_PMON)
+    {
+        pm_idx = get_random_pmon();
+        if (pm_idx == NO_PMON)
+        {
+            debug_pmon_select_failed();
+            return NO_MON;
+        }
+    }
+    mon = get_first_free_mon();
+    if (mon != NO_MON)
+    {
+       Mon m;
+       m.self = mon;
+       m.pm_ref = pm_idx;
+       m.flags = MF_USED | MF_ALIVE;
+       m.pos = c;
+       m.ai_lastpos = Nowhere;
+       m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1);
+       m.hpcur = m.hpmax;
+       m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3);
+       m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3);
+       m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5);
+       if (permons[pm_idx].rdam != NO_ATK)
+       {
+           m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3);
+           m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5);
+       }
+       else
+       {
+           m.rtohit = NO_ATK;
+           m.rdam = NO_ATK;
+       }
+       monsters[mon] = m;
+       lvl.set_mon_at(c, mon);
+       if (mon_visible(mon))
+       {
+           notify_new_mon_at(c, mon);
+       }
+       return mon;
+    }
+    return NO_MON;
+}
+
+/* The rules:
+ *   A monster gets eight entries.
+ */
+
+struct Death_drop_entry
+{
+    int pm_ref;
+    void (*func)(Coord c);
+};
+
+void goblin_death_drop(Coord c)
+{
+    if (!zero_die(4))
+    {
+       create_obj_near(PO_DAGGER, 1, c);
+    }
+}
+
+void thug_death_drop(Coord c)
+{
+    if (!zero_die(4))
+    {
+       create_obj_near(PO_MACE, 1, c);
+    }
+    else if (!zero_die(3))
+    {
+       create_obj_near(PO_LEATHER_ARMOUR, 1, c);
+    }
+}
+
+void hunter_death_drop(Coord c)
+{
+    if (!zero_die(6))
+    {
+       create_obj_near(PO_BOW, 1, c);
+    }
+}
+
+void duellist_death_drop(Coord c)
+{
+    if (!zero_die(6))
+    {
+       create_obj_near(PO_LONG_SWORD, 1, c);
+    }
+}
+
+void wizard_death_drop(Coord c)
+{
+    if (!zero_die(4))
+    {
+       create_obj_class_near(POCLASS_SCROLL, 1, false, c);
+    }
+    else if (!zero_die(3))
+    {
+       create_obj_class_near(POCLASS_POTION, 1, false, c);
+    }
+}
+
+void warlord_death_drop(Coord c)
+{
+    if (!zero_die(3))
+    {
+       create_obj_near(PO_RUNESWORD, 1, c);
+    }
+}
+
+void demon_death_drop(Coord c)
+{
+    if (!zero_die(100))
+    {
+       create_obj_near(PO_DEVIL_SPLEEN, 1, c);
+    }
+}
+
+Death_drop_entry death_drops[] =
+{
+    { PM_GOBLIN, goblin_death_drop },
+    { PM_THUG, thug_death_drop },
+    { PM_GOON, thug_death_drop },
+    { PM_HUNTER, hunter_death_drop },
+    { PM_DUELLIST, duellist_death_drop },
+    { PM_WIZARD, wizard_death_drop },
+    { PM_WARLORD, warlord_death_drop },
+    { PM_DEMON, demon_death_drop },
+    { NO_PMON, nullptr }
+};
+
+void death_drop(Mon_handle mon)
+{
+    Mon const *mptr = mon_snap(mon);
+    for (int i = 0; death_drops[i].func != nullptr; ++i)
+    {
+       if (death_drops[i].pm_ref == mptr->pm_ref)
+       {
+           death_drops[i].func(mptr->pos);
+       }
+    }
+}
+
+bool Mon::can_pass(Coord c) const
+{
+    Terrain terr;
+    if (!lvl.in_bounds(c))
+    {
+        return false;
+    }
+    if (lvl.mon_at(c) != NO_MON)
+    {
+        return false;
+    }
+    if (c == u.pos)
+    {
+        /* Sanity check! */
+        return false;
+    }
+    if (is_ethereal())
+    {
+        return true;
+    }
+    terr = lvl.terrain_at(c);
+    if (terrain_blocks_beings(terr))
+    {
+        /* 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)
+    {
+    case LAVA:
+        if (!can_fly() && !resists(DT_FIRE))
+        {
+            return false;
+        }
+        break;
+    case WATER:
+        if (!can_fly() && !resists(DT_DROWNING))
+        {
+            return false;
+        }
+    default:
+        break;
+    }
+    return true;
+}
+
+void heal_mon(Mon_handle mon, int amount, int cansee)
+{
+    if (amount > (monsters[mon].hpmax - monsters[mon].hpcur))
+    {
+        amount = monsters[mon].hpmax - monsters[mon].hpcur;
+    }
+    if (amount > 0)
+    {
+        if (cansee)
+        {
+            notify_mon_healed(mon);
+        }
+        monsters[mon].hpcur += amount;
+    }
+}
+
+void unplace_mon(Mon_handle mon)
+{
+    lvl.set_mon_at(monsters[mon].pos, NO_MON);
+    monsters[mon].flags &= ~MF_USED;
+}
+
+void apply_liquid(int pm, Coord c)
+{
+    if (pmon_bleeds(pm))
+    {
+       lvl.set_decal_at(c, Decal_blood);
+    }
+    else if (pmon_suppurates(pm))
+    {
+       lvl.set_decal_at(c, Decal_pus);
+    }
+}
+
+/*! \brief Handle the death of a monster
+ *
+ * \todo Support special effects on monster death
+ */
+void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse)
+{
+    Mon *mptr = mon_snapv(mon);
+    int pm = mptr->pm_ref;
+    apply_liquid(pm, mptr->pos);
+    if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse))
+    {
+       // TODO shower the surroundings with the monster's liquid
+    }
+    else if (pmon_leaves_corpse(pm))
+    {
+       create_corpse(pm, mptr->pos);
+    }
+    death_drop(mon); // phat lewt!
+    mptr->flags &= ~MF_ALIVE;
+    monsters[mon].hpcur = -1;
+    unplace_mon(mon); // cleanup
+    if (by_you)
+    {
+        notify_player_killed_mon(mon);
+        gain_experience(permons[monsters[mon].pm_ref].exp);
+    }
+    else if (mon_visible(mon))
+    {
+        notify_mon_dies(mon);
+    }
+}
+
+void damage_mon(Mon_handle mon, int amount, bool by_you)
+{
+    Mon *mptr;
+    mptr = mon_snapv(mon);
+    if (amount >= mptr->hpcur)
+    {
+        kill_mon(mon, by_you);
+    }
+    else
+    {
+        mptr->hpcur -= amount;
+    }
+}
+
+bool reject_mon(int pm)
+{
+    if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity))
+    {
+        return true;
+    }
+    return false;
+}
+
+Pass_fail teleport_mon_to_you(Mon_handle mon)
+{
+    int tryct;
+    Offset delta;
+    Coord c;
+    int success = 0;
+    Mon *mptr = mon_snapv(mon);
+    Coord oldpos = mptr->pos;
+    for (tryct = 0; tryct < 40; tryct++)
+    {
+        delta = random_step();
+        c = u.pos + delta;
+        if (mptr->can_pass(c))
+        {
+            success = 1;
+            break;
+        }
+    }
+    if (success)
+    {
+        reloc_mon(mon, c);
+        notify_mon_appears(mon);
+        return You_pass;
+    }
+    return You_fail;
+}
+
+Pass_fail teleport_mon(Mon_handle mon)
+{
+    Pass_fail rval = You_fail;
+    int cell_try;
+    Coord c;
+    for (cell_try = 0; cell_try < 200; cell_try++)
+    {
+        c = lvl.random_point(1);
+        if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
+        {
+            reloc_mon(mon, c);
+            rval = You_pass;
+            break;
+        }
+    }
+    return rval;
+}
+
+int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you)
+{
+    /* 0 = blocked, 1 = knocked, 2 = killed */
+    Mon *mptr = mon_snapv(mon);
+    Coord c = mptr->pos + step;
+    Coord savedpos = mptr->pos;
+    Terrain terr = lvl.terrain_at(c);
+
+    if (mptr->resists(DT_KNOCKBACK))
+    {
+        if (cansee)
+        {
+            notify_knockback_mon_resisted(mon);
+        }
+        return 0;
+    }
+    if (terrain_blocks_beings(terr))
+    {
+        if (cansee)
+        {
+            notify_knockback_mon_blocked(mon);
+        }
+        return 0;
+    }
+    reloc_mon(mon, c);
+    switch (terr)
+    {
+    case LAVA:
+        if (mptr->can_fly())
+        {
+            if (cansee)
+            {
+                notify_knockback_mon_hover_lava(mon);
+            }
+        }
+        else
+        {
+            if (cansee)
+            {
+                notify_knockback_mon_immersed_lava(mon);
+            }
+            else
+            {
+                notify_knockback_mon_lava_offscreen();
+            }
+            if (!mptr->resists(DT_FIRE))
+            {
+                kill_mon(mon, by_you);
+                return 2;
+            }
+        }
+        break;
+    case WATER:
+        if (mptr->can_fly())
+        {
+            if (cansee)
+            {
+                notify_knockback_mon_hover_water(mon);
+            }
+        }
+        else
+        {
+            if (cansee)
+            {
+                notify_knockback_mon_immersed_water(mon);
+            }
+            else
+            {
+                notify_knockback_mon_water_offscreen();
+            }
+            if (!mptr->resists(DT_DROWNING))
+            {
+                kill_mon(mon, by_you);
+                return 2;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+    return 1;
+}
+
+void reloc_mon(Mon_handle mon, Coord newpos)
+{
+    Mon *mptr = mon_snapv(mon);
+    lvl.set_mon_at(mptr->pos, NO_MON);
+    notify_new_mon_at(mptr->pos, NO_MON);
+    mptr->pos = newpos;
+    lvl.set_mon_at(mptr->pos, mon);
+    notify_new_mon_at(mptr->pos, mon);
+}
+
+void move_mon(Mon_handle mon, Coord c)
+{
+    Mon *mptr;
+    mptr = mon_snapv(mon);
+    if (!mptr->can_pass(c))
+    {
+        debug_mon_invalid_move(mon, c);
+        return;
+    }
+    if (lvl.mon_at(mptr->pos) != mon)
+    {
+        debug_misplaced_monster();
+        return;
+    }
+    reloc_mon(mon, c);
+}
+
+void summon_demon_near(Coord c)
+{
+    Coord c2 = c + random_step();
+    Mon_handle mon;
+    if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) &&
+        (c2 != u.pos))
+    {
+        mon = create_mon(PM_DEMON, c2);
+        notify_summon_demon(mon);
+    }
+}
+
+bool mon_visible(Mon_handle mon)
+{
+    Offset delta;
+    Mon const *mptr = mon_snap(mon);
+    if (!(mptr->flags & MF_USED))
+    {
+        return false;
+    }
+    delta = mptr->pos.delta(u.pos);
+    if (delta.len_cheb() <= MAX_FOV_RADIUS)
+    {
+        return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+void update_mon(Mon_handle mon)
+{
+    int cansee;
+    Mon *mptr = mon_snapv(mon);
+    if (mptr->hpcur < mptr->hpmax)
+    {
+        cansee = mon_visible(mon);
+        // TODO modify regen handling to use flags/fields instead of switching on pm_ref
+        switch (mptr->pm_ref)
+        {
+        case PM_TROLL:
+            if (!(game_tick % 10))
+            {
+                if (cansee)
+                {
+                    notify_mon_regenerates(mon);
+                }
+                heal_mon(mon, one_die(3) + 3, 0);
+            }
+            break;
+
+        case PM_ZOMBIE:
+            /* Zombies don't recover from their injuries. */
+            break;
+
+        default:
+            if (!(game_tick % 20))
+            {
+                heal_mon(mon, 1, cansee);
+            }
+            break;
+        }
+    }
+}
+
+bool Mon::resists(Damtyp dt) const
+{
+    switch (dt)
+    {
+    case DT_COLD:
+        return pmon_resists_cold(pm_ref);
+    case DT_FIRE:
+        return pmon_resists_fire(pm_ref);
+    case DT_POISON:
+        return pmon_resists_poison(pm_ref);
+    case DT_NECRO:
+        return pmon_resists_necro(pm_ref);
+    case DT_ELEC:
+        return pmon_resists_elec(pm_ref);
+    case DT_KNOCKBACK:
+        return pmon_resists_knockback(pm_ref);
+    case DT_DROWNING:
+        return pmon_resists_drowning(pm_ref);
+    default:
+        return false;
+    }
+}
+
+bool Mon::is_ethereal(void) const
+{
+    return pmon_is_ethereal(pm_ref);
+}
+
+bool Mon::can_fly(void) const
+{
+    return pmon_can_fly(pm_ref);
+}
+
+void asprint_mon_name(char **buf, Mon_handle mon, int article)
+{
+    Mon const *mptr = mon_snap(mon);
+    int i;
+    switch (article)
+    {
+    case 0:
+       i = asprintf(buf, "a%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
+       break;
+    case 1:
+       i = asprintf(buf, "the %s", permons[mptr->pm_ref].name);
+       break;
+    case 2:
+       i = asprintf(buf, "A%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
+       break;
+    case 3:
+       i = asprintf(buf, "The %s", permons[mptr->pm_ref].name);
+       break;
+    default:
+       i = -1;
+    }
+    if (i == -1)
+    {
+       // TODO wibble
+    }
+    return;
+}
+
+/* monsters.cc */
+// vim:cindent
diff --git a/monsters.cc b/monsters.cc
deleted file mode 100644 (file)
index 04f8a59..0000000
+++ /dev/null
@@ -1,683 +0,0 @@
-/*! \file monsters.cc
- *  \brief Monster-related functions
- */
-
-/* Copyright 2005-2014 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.
- */
-
-#define MONSTERS_CC
-#include "obumbrata.hh"
-#include "monsters.hh"
-#include "objects.hh"
-
-const Mon_handle NO_MON = 0u;
-
-std::map<Mon_handle, Mon> monsters;
-static bool reject_mon(int pm);
-
-/*! \brief Summon some monsters
- *
- *  \return Number of monsters summoned
- *  \param how_many Maximum number of monsters to summon
- */
-int summoning(Coord c, int how_many)
-{
-    int i;
-    Offset delta;
-    Coord testpos;
-    int tryct;
-    Mon_handle mon;
-    int created = 0;
-    int pmon;
-    for (i = 0; i < how_many; i++)
-    {
-        for (tryct = 0; tryct < 20; tryct++)
-        {
-            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))
-                {
-                    /* Never summon magicians! */
-                    continue;
-                }
-                mon = create_mon(NO_PMON, testpos);
-                if (mon != NO_MON)
-                {
-                    created++;
-                    break;
-                }
-            }
-        }
-    }
-    return created;
-}
-
-int ood(int power, int ratio)
-{
-    return (depth - power + ratio - 1) / ratio;
-}
-
-int get_random_pmon(void)
-{
-    int tryct;
-    int pm;
-    for (tryct = 0; tryct < 200; tryct++)
-    {
-        pm = zero_die(NUM_OF_PERMONS);
-        if (reject_mon(pm))
-        {
-            pm = NO_PMON;
-            continue;
-        }
-        break;
-    }
-    return pm;
-}
-
-Mon_handle first_free_mon_handle = 1u;
-static Mon_handle get_first_free_mon(void)
-{
-    if (first_free_mon_handle == NO_MON)
-    {
-       return NO_MON;
-    }
-    return first_free_mon_handle++;
-}
-
-Mon_handle create_mon(int pm_idx, Coord c)
-{
-    Mon_handle mon;
-    if (lvl.mon_at(c) != NO_MON)
-    {
-        debug_create_mon_occupied(c);
-        return NO_MON;
-    }
-    if (pm_idx == NO_PMON)
-    {
-        pm_idx = get_random_pmon();
-        if (pm_idx == NO_PMON)
-        {
-            debug_pmon_select_failed();
-            return NO_MON;
-        }
-    }
-    mon = get_first_free_mon();
-    if (mon != NO_MON)
-    {
-       Mon m;
-       m.self = mon;
-       m.pm_ref = pm_idx;
-       m.flags = MF_USED | MF_ALIVE;
-       m.pos = c;
-       m.ai_lastpos = Nowhere;
-       m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1);
-       m.hpcur = m.hpmax;
-       m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3);
-       m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3);
-       m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5);
-       if (permons[pm_idx].rdam != NO_ATK)
-       {
-           m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3);
-           m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5);
-       }
-       else
-       {
-           m.rtohit = NO_ATK;
-           m.rdam = NO_ATK;
-       }
-       monsters[mon] = m;
-       lvl.set_mon_at(c, mon);
-       if (mon_visible(mon))
-       {
-           notify_new_mon_at(c, mon);
-       }
-       return mon;
-    }
-    return NO_MON;
-}
-
-/* The rules:
- *   A monster gets eight entries.
- */
-
-struct Death_drop_entry
-{
-    int pm_ref;
-    void (*func)(Coord c);
-};
-
-void goblin_death_drop(Coord c)
-{
-    if (!zero_die(4))
-    {
-       create_obj_near(PO_DAGGER, 1, c);
-    }
-}
-
-void thug_death_drop(Coord c)
-{
-    if (!zero_die(4))
-    {
-       create_obj_near(PO_MACE, 1, c);
-    }
-    else if (!zero_die(3))
-    {
-       create_obj_near(PO_LEATHER_ARMOUR, 1, c);
-    }
-}
-
-void hunter_death_drop(Coord c)
-{
-    if (!zero_die(6))
-    {
-       create_obj_near(PO_BOW, 1, c);
-    }
-}
-
-void duellist_death_drop(Coord c)
-{
-    if (!zero_die(6))
-    {
-       create_obj_near(PO_LONG_SWORD, 1, c);
-    }
-}
-
-void wizard_death_drop(Coord c)
-{
-    if (!zero_die(4))
-    {
-       create_obj_class_near(POCLASS_SCROLL, 1, false, c);
-    }
-    else if (!zero_die(3))
-    {
-       create_obj_class_near(POCLASS_POTION, 1, false, c);
-    }
-}
-
-void warlord_death_drop(Coord c)
-{
-    if (!zero_die(3))
-    {
-       create_obj_near(PO_RUNESWORD, 1, c);
-    }
-}
-
-void demon_death_drop(Coord c)
-{
-    if (!zero_die(100))
-    {
-       create_obj_near(PO_DEVIL_SPLEEN, 1, c);
-    }
-}
-
-Death_drop_entry death_drops[] =
-{
-    { PM_GOBLIN, goblin_death_drop },
-    { PM_THUG, thug_death_drop },
-    { PM_GOON, thug_death_drop },
-    { PM_HUNTER, hunter_death_drop },
-    { PM_DUELLIST, duellist_death_drop },
-    { PM_WIZARD, wizard_death_drop },
-    { PM_WARLORD, warlord_death_drop },
-    { PM_DEMON, demon_death_drop },
-    { NO_PMON, nullptr }
-};
-
-void death_drop(Mon_handle mon)
-{
-    Mon const *mptr = mon_snap(mon);
-    for (int i = 0; death_drops[i].func != nullptr; ++i)
-    {
-       if (death_drops[i].pm_ref == mptr->pm_ref)
-       {
-           death_drops[i].func(mptr->pos);
-       }
-    }
-}
-
-bool Mon::can_pass(Coord c) const
-{
-    Terrain terr;
-    if (!lvl.in_bounds(c))
-    {
-        return false;
-    }
-    if (lvl.mon_at(c) != NO_MON)
-    {
-        return false;
-    }
-    if (c == u.pos)
-    {
-        /* Sanity check! */
-        return false;
-    }
-    if (is_ethereal())
-    {
-        return true;
-    }
-    terr = lvl.terrain_at(c);
-    if (terrain_blocks_beings(terr))
-    {
-        /* 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)
-    {
-    case LAVA:
-        if (!can_fly() && !resists(DT_FIRE))
-        {
-            return false;
-        }
-        break;
-    case WATER:
-        if (!can_fly() && !resists(DT_DROWNING))
-        {
-            return false;
-        }
-    default:
-        break;
-    }
-    return true;
-}
-
-void heal_mon(Mon_handle mon, int amount, int cansee)
-{
-    if (amount > (monsters[mon].hpmax - monsters[mon].hpcur))
-    {
-        amount = monsters[mon].hpmax - monsters[mon].hpcur;
-    }
-    if (amount > 0)
-    {
-        if (cansee)
-        {
-            notify_mon_healed(mon);
-        }
-        monsters[mon].hpcur += amount;
-    }
-}
-
-void unplace_mon(Mon_handle mon)
-{
-    lvl.set_mon_at(monsters[mon].pos, NO_MON);
-    monsters[mon].flags &= ~MF_USED;
-}
-
-void apply_liquid(int pm, Coord c)
-{
-    if (pmon_bleeds(pm))
-    {
-       lvl.set_decal_at(c, Decal_blood);
-    }
-    else if (pmon_suppurates(pm))
-    {
-       lvl.set_decal_at(c, Decal_pus);
-    }
-}
-
-/*! \brief Handle the death of a monster
- *
- * \todo Support special effects on monster death
- */
-void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse)
-{
-    Mon *mptr = mon_snapv(mon);
-    int pm = mptr->pm_ref;
-    apply_liquid(pm, mptr->pos);
-    if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse))
-    {
-       // TODO shower the surroundings with the monster's liquid
-    }
-    else if (pmon_leaves_corpse(pm))
-    {
-       create_corpse(pm, mptr->pos);
-    }
-    death_drop(mon); // phat lewt!
-    mptr->flags &= ~MF_ALIVE;
-    monsters[mon].hpcur = -1;
-    unplace_mon(mon); // cleanup
-    if (by_you)
-    {
-        notify_player_killed_mon(mon);
-        gain_experience(permons[monsters[mon].pm_ref].exp);
-    }
-    else if (mon_visible(mon))
-    {
-        notify_mon_dies(mon);
-    }
-}
-
-void damage_mon(Mon_handle mon, int amount, bool by_you)
-{
-    Mon *mptr;
-    mptr = mon_snapv(mon);
-    if (amount >= mptr->hpcur)
-    {
-        kill_mon(mon, by_you);
-    }
-    else
-    {
-        mptr->hpcur -= amount;
-    }
-}
-
-bool reject_mon(int pm)
-{
-    if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity))
-    {
-        return true;
-    }
-    return false;
-}
-
-Pass_fail teleport_mon_to_you(Mon_handle mon)
-{
-    int tryct;
-    Offset delta;
-    Coord c;
-    int success = 0;
-    Mon *mptr = mon_snapv(mon);
-    Coord oldpos = mptr->pos;
-    for (tryct = 0; tryct < 40; tryct++)
-    {
-        delta = random_step();
-        c = u.pos + delta;
-        if (mptr->can_pass(c))
-        {
-            success = 1;
-            break;
-        }
-    }
-    if (success)
-    {
-        reloc_mon(mon, c);
-        notify_mon_appears(mon);
-        return You_pass;
-    }
-    return You_fail;
-}
-
-Pass_fail teleport_mon(Mon_handle mon)
-{
-    Pass_fail rval = You_fail;
-    int cell_try;
-    Coord c;
-    for (cell_try = 0; cell_try < 200; cell_try++)
-    {
-        c = lvl.random_point(1);
-        if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
-        {
-            reloc_mon(mon, c);
-            rval = You_pass;
-            break;
-        }
-    }
-    return rval;
-}
-
-int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you)
-{
-    /* 0 = blocked, 1 = knocked, 2 = killed */
-    Mon *mptr = mon_snapv(mon);
-    Coord c = mptr->pos + step;
-    Coord savedpos = mptr->pos;
-    Terrain terr = lvl.terrain_at(c);
-
-    if (mptr->resists(DT_KNOCKBACK))
-    {
-        if (cansee)
-        {
-            notify_knockback_mon_resisted(mon);
-        }
-        return 0;
-    }
-    if (terrain_blocks_beings(terr))
-    {
-        if (cansee)
-        {
-            notify_knockback_mon_blocked(mon);
-        }
-        return 0;
-    }
-    reloc_mon(mon, c);
-    switch (terr)
-    {
-    case LAVA:
-        if (mptr->can_fly())
-        {
-            if (cansee)
-            {
-                notify_knockback_mon_hover_lava(mon);
-            }
-        }
-        else
-        {
-            if (cansee)
-            {
-                notify_knockback_mon_immersed_lava(mon);
-            }
-            else
-            {
-                notify_knockback_mon_lava_offscreen();
-            }
-            if (!mptr->resists(DT_FIRE))
-            {
-                kill_mon(mon, by_you);
-                return 2;
-            }
-        }
-        break;
-    case WATER:
-        if (mptr->can_fly())
-        {
-            if (cansee)
-            {
-                notify_knockback_mon_hover_water(mon);
-            }
-        }
-        else
-        {
-            if (cansee)
-            {
-                notify_knockback_mon_immersed_water(mon);
-            }
-            else
-            {
-                notify_knockback_mon_water_offscreen();
-            }
-            if (!mptr->resists(DT_DROWNING))
-            {
-                kill_mon(mon, by_you);
-                return 2;
-            }
-        }
-        break;
-    default:
-        break;
-    }
-    return 1;
-}
-
-void reloc_mon(Mon_handle mon, Coord newpos)
-{
-    Mon *mptr = mon_snapv(mon);
-    lvl.set_mon_at(mptr->pos, NO_MON);
-    notify_new_mon_at(mptr->pos, NO_MON);
-    mptr->pos = newpos;
-    lvl.set_mon_at(mptr->pos, mon);
-    notify_new_mon_at(mptr->pos, mon);
-}
-
-void move_mon(Mon_handle mon, Coord c)
-{
-    Mon *mptr;
-    mptr = mon_snapv(mon);
-    if (!mptr->can_pass(c))
-    {
-        debug_mon_invalid_move(mon, c);
-        return;
-    }
-    if (lvl.mon_at(mptr->pos) != mon)
-    {
-        debug_misplaced_monster();
-        return;
-    }
-    reloc_mon(mon, c);
-}
-
-void summon_demon_near(Coord c)
-{
-    Coord c2 = c + random_step();
-    Mon_handle mon;
-    if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) &&
-        (c2 != u.pos))
-    {
-        mon = create_mon(PM_DEMON, c2);
-        notify_summon_demon(mon);
-    }
-}
-
-bool mon_visible(Mon_handle mon)
-{
-    Offset delta;
-    Mon const *mptr = mon_snap(mon);
-    if (!(mptr->flags & MF_USED))
-    {
-        return false;
-    }
-    delta = mptr->pos.delta(u.pos);
-    if (delta.len_cheb() <= MAX_FOV_RADIUS)
-    {
-        return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central;
-    }
-    else
-    {
-        return false;
-    }
-}
-
-void update_mon(Mon_handle mon)
-{
-    int cansee;
-    Mon *mptr = mon_snapv(mon);
-    if (mptr->hpcur < mptr->hpmax)
-    {
-        cansee = mon_visible(mon);
-        // TODO modify regen handling to use flags/fields instead of switching on pm_ref
-        switch (mptr->pm_ref)
-        {
-        case PM_TROLL:
-            if (!(game_tick % 10))
-            {
-                if (cansee)
-                {
-                    notify_mon_regenerates(mon);
-                }
-                heal_mon(mon, one_die(3) + 3, 0);
-            }
-            break;
-
-        case PM_ZOMBIE:
-            /* Zombies don't recover from their injuries. */
-            break;
-
-        default:
-            if (!(game_tick % 20))
-            {
-                heal_mon(mon, 1, cansee);
-            }
-            break;
-        }
-    }
-}
-
-bool Mon::resists(Damtyp dt) const
-{
-    switch (dt)
-    {
-    case DT_COLD:
-        return pmon_resists_cold(pm_ref);
-    case DT_FIRE:
-        return pmon_resists_fire(pm_ref);
-    case DT_POISON:
-        return pmon_resists_poison(pm_ref);
-    case DT_NECRO:
-        return pmon_resists_necro(pm_ref);
-    case DT_ELEC:
-        return pmon_resists_elec(pm_ref);
-    case DT_KNOCKBACK:
-        return pmon_resists_knockback(pm_ref);
-    case DT_DROWNING:
-        return pmon_resists_drowning(pm_ref);
-    default:
-        return false;
-    }
-}
-
-bool Mon::is_ethereal(void) const
-{
-    return pmon_is_ethereal(pm_ref);
-}
-
-bool Mon::can_fly(void) const
-{
-    return pmon_can_fly(pm_ref);
-}
-
-void asprint_mon_name(char **buf, Mon_handle mon, int article)
-{
-    Mon const *mptr = mon_snap(mon);
-    int i;
-    switch (article)
-    {
-    case 0:
-       i = asprintf(buf, "a%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
-       break;
-    case 1:
-       i = asprintf(buf, "the %s", permons[mptr->pm_ref].name);
-       break;
-    case 2:
-       i = asprintf(buf, "A%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
-       break;
-    case 3:
-       i = asprintf(buf, "The %s", permons[mptr->pm_ref].name);
-       break;
-    default:
-       i = -1;
-    }
-    if (i == -1)
-    {
-       // TODO wibble
-    }
-    return;
-}
-
-/* monsters.cc */
-// vim:cindent