Renaming objects.cc to obj1.cc
authorMartin Read <mpread@chiark.greenend.org.uk>
Mon, 10 Mar 2014 17:32:22 +0000 (17:32 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Mon, 10 Mar 2014 17:32:22 +0000 (17:32 +0000)
obj1.cc [new file with mode: 0644]
objects.cc [deleted file]

diff --git a/obj1.cc b/obj1.cc
new file mode 100644 (file)
index 0000000..0d5c014
--- /dev/null
+++ b/obj1.cc
@@ -0,0 +1,915 @@
+/* \file objects.cc
+ * \brief Object handling for Obumbrata et Velata
+ */
+
+/* Copyright 2005-2013 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 OBJECTS_CC
+#include "obumbrata.hh"
+#include "objects.hh"
+#include "monsters.hh"
+
+#include <string.h>
+
+std::map<Obj_handle, Obj> objects;
+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)
+{
+    Obj *optr = obj_snapv(obj);
+    switch (optr->po_ref)
+    {
+    case PO_TELEPORT_SCROLL:
+        teleport_u();
+        break;
+    case PO_FIRE_SCROLL:
+        notify_item_explodes_flames(obj);
+        if (u.resistances[DT_FIRE])
+        {
+            notify_player_ignore_damage(DT_FIRE);
+        }
+        else
+        {
+            damage_u(dice(4, 10), DEATH_KILLED, "searing flames");
+        }
+#if 0
+       // TODO iterate over visible monsters
+        for (i = 0; i < MONSTERS_IN_PLAY; ++i)
+        {
+            if (mon_visible(i))
+            {
+                if (!pmon_resists_fire(monsters[i].pm_ref))
+                {
+                    notify_fireitem_hit(i);
+                    damage_mon(i, dice(4, 10), true);
+                }
+            }
+        }
+#endif
+        break;
+    case PO_PROTECTION_SCROLL:
+        notify_read_scroll_protection();
+        if (!u.protection)
+        {
+            /* Do not prolong existing protection, only grant
+             * protection to the unprotected. */
+            u.protection = 100;
+        }
+        if (u.withering)
+        {
+            u.withering = 0;
+            notify_wither_recovered();
+        }
+        if (u.armourmelt)
+        {
+            u.armourmelt = 0;
+            notify_armourmelt_recovered();
+        }
+        if (u.leadfoot)
+        {
+            u.leadfoot = 0;
+            notify_leadfoot_recovered();
+        }
+        recalc_defence();
+        break;
+    default:
+        debug_read_non_scroll();
+        return Cost_none;
+    }
+    consume_obj(obj);
+    return Cost_std;
+}
+
+bool consume_obj(Obj_handle obj)
+{
+    int i;
+    Obj *optr = obj_snapv(obj);
+    optr->quan--;
+    if (optr->quan == 0)
+    {
+        optr->flags &= ~OF_USED;
+        if (optr->flags & OF_WITH_YOU)
+        {
+            if (obj == u.armour)
+            {
+                u.armour = NO_OBJ;
+                recalc_defence();
+                notify_armour_broke(obj);
+            }
+            else if (obj == u.weapon)
+            {
+                u.weapon = NO_OBJ;
+                recalc_defence();
+                notify_weapon_broke(obj);
+            }
+            else if (obj == u.ring)
+            {
+                u.ring = NO_OBJ;
+                recalc_defence();
+            }
+           for (i = 0; i < 19; i++)
+           {
+               if (u.inventory[i] == obj)
+               {
+                   u.inventory[i] = NO_OBJ;
+                   break;
+               }
+           }
+        }
+        return true;
+    }
+    return false;
+}
+
+/*! \brief Consume a food */
+Action_cost eat_food(Obj_handle obj)
+{
+    Obj *optr = obj_snapv(obj);
+    bool ravenous = (u.food < 0);
+    u.food += 1500;
+    if (permobjs[optr->po_ref].poclass != POCLASS_FOOD)
+    {
+        debug_eat_non_food(obj);
+        return Cost_none;
+    }
+    if (optr->po_ref == PO_DEVIL_SPLEEN)
+    {
+        notify_ingest_spleen();
+        if (zero_die(2))
+        {
+            gain_body(1);
+        }
+        else
+        {
+            gain_agility(1);
+        }
+        // TODO add more tracking state to this item so we don't have to randomize the fell power choice
+        ++u.sympathy[zero_die(TOTAL_FELL_POWERS)];
+    }
+    else
+    {
+        notify_eat_food(ravenous);
+    }
+    consume_obj(obj);
+    return Cost_std;
+}
+
+/*! \brief Effect of quaffing a body potion */
+static void body_potion_quaff(void)
+{
+    gain_body(1);
+}
+
+/*! \brief Effect of quaffing a agility potion */
+static void agility_potion_quaff(void)
+{
+    gain_agility(1);
+}
+
+/*! \brief Effect of quaffing a healing potion */
+static void healing_potion_quaff(void)
+{
+    int healpercent = inc_flat(30, 50);
+    int healamount = (healpercent * ((u.hpmax > 60) ? u.hpmax : 60)) / 100;
+    heal_u(healamount, 1, 1);
+}
+
+/*! \brief Effect of quaffing a restoration potion */
+static void restoration_potion_quaff(void)
+{
+    notify_quaff_potion_restoration();
+    if (u.bdam && ((!u.adam) || zero_die(2)))
+    {
+       u.bdam = 0;
+       notify_body_restore();
+    }
+    else if (u.adam)
+    {
+       u.adam = 0;
+       notify_agility_restore();
+    }
+}
+
+struct Potion_table_entry
+{
+    int pobj;
+    void (*quaff_func)(void);
+};
+
+static Potion_table_entry quaff_table[] =
+{
+    { PO_BODY_POTION, body_potion_quaff },
+    { PO_AGILITY_POTION, agility_potion_quaff },
+    { PO_HEALING_POTION, healing_potion_quaff },
+    { PO_RESTORATION_POTION, restoration_potion_quaff },
+    { NO_POBJ, nullptr },
+};
+
+/*! \brief Consume a potion */
+Action_cost quaff_potion(Obj_handle obj)
+{
+    Obj *optr = obj_snapv(obj);
+    int i;
+    for (i = 0; quaff_table[i].pobj != NO_POBJ; ++i)
+    {
+       if (quaff_table[i].pobj == optr->po_ref)
+       {
+           quaff_table[i].quaff_func();
+           consume_obj(obj);
+           return Cost_std;
+       }
+    }
+    debug_quaff_non_potion();
+    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)
+{
+    if (first_free_obj_handle == 0u)
+    {
+       return NO_OBJ;
+    }
+    return first_free_obj_handle++;
+}
+
+Obj_handle create_obj_class_near(enum poclass_num po_class, int quantity, bool with_you, Coord c)
+{
+    int po_idx;
+    int tryct;
+    for (tryct = 0; tryct < 200; tryct++)
+    {
+        switch (po_class)
+        {
+        case POCLASS_POTION:
+            po_idx = inc_flat(PO_FIRST_POTION, PO_LAST_POTION);
+            break;
+        case POCLASS_SCROLL:
+            po_idx = inc_flat(PO_FIRST_SCROLL, PO_LAST_SCROLL);
+            break;
+        case POCLASS_RING:
+            po_idx = inc_flat(PO_FIRST_RING, PO_LAST_RING);
+            break;
+        default:
+            /* No getting armour/weapons by class... yet. */
+            return NO_OBJ;
+        }
+        if (zero_die(100) < permobjs[po_idx].rarity)
+        {
+            continue;
+        }
+        break;
+    }
+    return (with_you ? create_obj(po_idx, quantity, with_you, c) : create_obj_near(po_idx, quantity, c));
+}
+
+int get_random_pobj(void)
+{
+    int tryct;
+    int po_idx;
+    for (tryct = 0; tryct < 200; tryct++)
+    {
+        po_idx = zero_die(NUM_OF_PERMOBJS);
+        if (zero_die(100) < permobjs[po_idx].rarity)
+        {
+            po_idx = NO_POBJ;
+            continue;
+        }
+        if (depth < permobjs[po_idx].depth)
+        {
+            po_idx = NO_POBJ;
+            continue;
+        }
+        break;
+    }
+    return po_idx;
+}
+
+Obj_handle create_corpse(int pm_idx, Coord c)
+{
+    Obj_handle obj = create_obj_near(PO_CORPSE, 1, c);
+    if (obj != NO_OBJ)
+    {
+       Obj *o = obj_snapv(obj);
+       o->meta[0] = pm_idx;
+       o->meta[1] = 0; // reserved
+       o->meta[2] = 0; // reserved
+       o->meta[3] = 0; // reserved
+    }
+    return obj;
+}
+
+Obj_handle create_obj_near(int po_idx, int quantity, Coord c)
+{
+    Offset delta;
+    Coord nc;
+    int panic_count = 200;
+    while ((lvl.obj_at(c) != NO_OBJ) && (panic_count > 0))
+    {
+       do
+       {
+           delta = random_step();
+           nc = c + delta;
+       }
+       while (terrain_blocks_beings(lvl.terrain_at(nc)));
+       c = nc;
+       --panic_count;
+    }
+    if (panic_count < 1)
+    {
+       return NO_OBJ;
+    }
+    Obj_handle obj = create_obj(po_idx, quantity, false, c);
+    return obj;
+}
+
+Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c)
+{
+    Obj_handle obj = get_first_free_obj();
+    if (obj == NO_OBJ)
+    {
+        debug_object_pool_exhausted();
+        return NO_OBJ;
+    }
+    if (po_idx == NO_POBJ)
+    {
+        po_idx = get_random_pobj();
+        if (po_idx == NO_POBJ)
+        {
+            debug_pobj_select_failed();
+            return NO_OBJ;
+        }
+    }
+    Obj o;
+    memset(o.meta, '\0', sizeof o.meta);
+    o.self = obj;
+    o.po_ref = po_idx;
+    o.flags = OF_USED | (with_you ? OF_WITH_YOU : 0);
+    o.pos = c;
+    o.quan = quantity;
+    switch (permobjs[po_idx].poclass)
+    {
+    case POCLASS_WEAPON:
+    case POCLASS_ARMOUR:
+        o.durability = OBJ_MAX_DUR;
+        break;
+    default:
+        break;
+    }
+    objects[obj] = o;
+    if (!(o.flags & OF_WITH_YOU))
+    {
+        lvl.set_obj_at(c, obj);
+       notify_new_obj_at(c, obj);
+    }
+    return obj;
+}
+
+void asprint_obj_name(char **buf, Obj_handle obj)
+{
+    Obj *optr;
+    Permobj *poptr;
+    int i;
+    optr = obj_snapv(obj);
+    poptr = permobjs + optr->po_ref;
+    if (optr->quan > 1)
+    {
+        i = asprintf(buf, "%d %s", optr->quan, poptr->plural);
+    }
+    else if (po_is_stackable(optr->po_ref))
+    {
+        i = asprintf(buf, "1 %s", poptr->name);
+    }
+    else if ((poptr->poclass == POCLASS_WEAPON) ||
+             (poptr->poclass == POCLASS_ARMOUR))
+    {
+        i = asprintf(buf, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+    }
+    else if (optr->po_ref == PO_CORPSE)
+    {
+       Permon *pmptr = permons + optr->meta[0];
+       i = asprintf(buf, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
+    }
+    else
+    {
+        i = asprintf(buf, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+    }
+    if (i == -1)
+    {
+       // TODO wibble
+    }
+    return;
+}
+
+void sprint_obj_name(char *buf, Obj_handle obj, int len)
+{
+    Obj *optr;
+    Permobj *poptr;
+    optr = obj_snapv(obj);
+    poptr = permobjs + optr->po_ref;
+    if (optr->quan > 1)
+    {
+        snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
+    }
+    else if (po_is_stackable(optr->po_ref))
+    {
+        snprintf(buf, len, "1 %s", poptr->name);
+    }
+    else if ((poptr->poclass == POCLASS_WEAPON) ||
+             (poptr->poclass == POCLASS_ARMOUR))
+    {
+        snprintf(buf, len, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+    }
+    else if (optr->po_ref == PO_CORPSE)
+    {
+       Permon *pmptr = permons + optr->meta[0];
+       snprintf(buf, len, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
+    }
+    else
+    {
+        snprintf(buf, len, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+    }
+}
+
+void fprint_obj_name(FILE *fp, Obj_handle obj)
+{
+    char *s;
+    asprint_obj_name(&s, obj);
+    fputs(s, fp);
+    free(s);
+}
+
+Action_cost drop_obj(int inv_idx)
+{
+    Obj *optr;
+    optr = obj_snapv(u.inventory[inv_idx]);
+    if (lvl.obj_at(u.pos) == NO_OBJ)
+    {
+        optr->pos = u.pos;
+        lvl.set_obj_at(u.pos, u.inventory[inv_idx]);
+        if (u.weapon == u.inventory[inv_idx])
+        {
+            u.weapon = NO_OBJ;
+        }
+        u.inventory[inv_idx] = NO_OBJ;
+        optr->flags &= ~OF_WITH_YOU;
+        notify_drop_item(lvl.obj_at(u.pos));
+        return Cost_std;
+    }
+    else
+    {
+        notify_drop_blocked();
+        return Cost_none;
+    }
+}
+
+bool po_is_stackable(int po)
+{
+    switch (permobjs[po].poclass)
+    {
+    default:
+        return false;
+    case POCLASS_POTION:
+    case POCLASS_SCROLL:
+    case POCLASS_FOOD:
+        return true;
+    }
+}
+
+void attempt_pickup(void)
+{
+    int i;
+    int stackable;
+    Obj_handle oh = lvl.obj_at(u.pos);
+    Obj *optr = obj_snapv(oh);
+    Obj *invptr;
+    stackable = po_is_stackable(optr->po_ref);
+    if (stackable)
+    {
+        for (i = 0; i < 19; i++)
+        {
+           invptr = obj_snapv(u.inventory[i]);
+           if (invptr && (invptr->po_ref == optr->po_ref))
+           {
+               invptr->quan += optr->quan;
+               optr->flags &= ~OF_USED;
+                lvl.set_obj_at(u.pos, NO_OBJ);
+                notify_get_item(oh, i);
+                return;
+            }
+        }
+    }
+    for (i = 0; i < 19; i++)
+    {
+        if (u.inventory[i] == NO_OBJ)
+        {
+            break;
+        }
+    }
+    if (i == 19)
+    {
+        notify_pack_full();
+        return;
+    }
+    u.inventory[i] = lvl.obj_at(u.pos);
+    lvl.set_obj_at(u.pos, NO_OBJ);
+    optr->flags |= OF_WITH_YOU;
+    optr->pos = Nowhere;
+    notify_get_item(NO_OBJ, i);
+}
+
+void break_reaction(Obj_handle obj)
+{
+    switch (objects[obj].po_ref)
+    {
+    case PO_TORMENTORS_LASH:
+        if (u.food < 500)
+        {
+            int shortfall = (u.food < 0) ? 500 : 500 - u.food;
+            int damage = (shortfall + 24) / 25;
+            u.food = (u.food < 0) ? u.food : 0;
+            notify_lash_activation(3);
+            damage_u(damage, DEATH_LASH, "");
+        }
+        else
+        {
+            u.food -= 500;
+            objects[obj].durability = 100;
+            notify_lash_activation(1);
+        }
+        break;
+
+    default:
+        if (permobjs[objects[obj].po_ref].flags[0] & POF_DRESS)
+        {
+            objects[obj].durability = 50 + zero_die(51);
+            objects[obj].po_ref = PO_RAGGED_SHIFT;
+            notify_dress_shredded();
+            recalc_defence();
+        }
+        else
+        {
+            debug_unimplemented_break_reaction(objects[obj].po_ref);
+        }
+        break;
+    }
+}
+
+void damage_obj(Obj_handle obj)
+{
+    /* Only weapons and armour have non-zero durability. */
+    if (objects[obj].durability == 0)
+    {
+        /* Break the object. Weapons and armour don't stack. */
+        consume_obj(obj);
+    }
+    else
+    {
+        objects[obj].durability--;
+        if ((objects[obj].durability == 0) && (permobjs[objects[obj].po_ref].flags[0] & POF_BREAK_REACT))
+        {
+            break_reaction(obj);
+        }
+    }
+} 
+
+int evasion_penalty(Obj_handle obj)
+{
+    if (permobjs[objects[obj].po_ref].poclass == POCLASS_ARMOUR)
+    {
+        return permobjs[objects[obj].po_ref].power2;
+    }
+    return 100;
+}
+
+Action_cost magic_ring(void)
+{
+    Obj *optr = obj_snapv(u.ring);
+    switch (optr->po_ref)
+    {
+    case PO_TELEPORT_RING:
+        if (u.food >= 50)
+        {
+            u.food -= 50;
+            notify_telering_activation(1);
+            teleport_u();
+            return Cost_std;
+        }
+        else
+        {
+            notify_telering_activation(0);
+        }
+        return Cost_none;
+    default:
+        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+        {
+            debug_unimplemented_activation(optr->po_ref);
+        }
+        else
+        {
+            notify_magic_powerless_ring();
+        }
+        return Cost_none;
+    }
+}
+
+Action_cost emanate_armour(void)
+{
+    Obj *optr = obj_snapv(u.armour);
+    switch (optr->po_ref)
+    {
+    default:
+        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+        {
+            debug_unimplemented_activation(optr->po_ref);
+        }
+        else
+        {
+            notify_emanate_powerless_armour();
+        }
+        break;
+    }
+    return Cost_none;
+}
+
+Action_cost zap_weapon(void)
+{
+    Obj *optr = obj_snapv(u.weapon);
+    switch (optr->po_ref)
+    {
+    case PO_STAFF_OF_FIRE:
+        if (u.food > 150)
+        {
+            Coord c;
+            u.food -= 150;
+            notify_firestaff_activation(1);
+            for (c.y = u.pos.y - 1; c.y <= u.pos.y + 1; ++c.y)
+            {
+                if ((c.y < lvl.min_y()) || (c.y >= lvl.max_y()))
+                {
+                    continue;
+                }
+                for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
+                {
+                    Mon_handle mon;
+                    if ((c.x < lvl.min_x()) || (c.x >= lvl.max_x()))
+                    {
+                        continue;
+                    }
+                    mon = lvl.mon_at(c);
+                    if (mon != NO_MON)
+                    {
+                        Mon *mptr = mon_snapv(mon);
+                        if (!pmon_resists_fire(mptr->pm_ref))
+                        {
+                            notify_fireitem_hit(mon);
+                            damage_mon(mon, dice(4, 10), true);
+                        }
+                    }
+                }
+            }
+            damage_obj(u.weapon);
+        }
+        else
+        {
+            notify_firestaff_activation(0);
+            return Cost_none;
+        }
+        return Cost_std;
+    default:
+        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+        {
+            debug_unimplemented_activation(optr->po_ref);
+        }
+        else
+        {
+            notify_zap_powerless_weapon();
+        }
+        return Cost_none;
+    }
+}
+
+/*! \brief Unwield the player's currently equipped weapon
+ *
+ *  \todo Stickycurse?
+ *  \todo Life-essential resistances?
+ */
+Action_cost player_unwield(Noisiness noisy)
+{
+    int saved_weapon = u.weapon;
+    if (u.weapon == NO_OBJ)
+    {
+        debug_unwield_nothing();
+        return Cost_none;
+    }
+    u.weapon = NO_OBJ;
+    recalc_defence();
+    notify_unwield(saved_weapon, Noise_std);
+    return Cost_std;
+}
+
+Action_cost player_wield(Obj_handle obj, Noisiness noisy)
+{
+    if (u.weapon != NO_OBJ)
+    {
+        player_unwield(Noise_low);
+    }
+    u.weapon = obj;
+    notify_wield_weapon(u.weapon);
+    recalc_defence();
+    return Cost_std;
+}
+
+Action_cost wear_armour(Obj_handle obj)
+{
+    if (u.armour != NO_OBJ)
+    {
+        debug_wear_while_wearing();
+        return Cost_none;
+    }
+    if (!(objects[obj].flags & OF_WITH_YOU))
+    {
+        debug_wear_uncarried_armour();
+        return Cost_none;
+    }
+    u.armour = obj;
+    recalc_defence();
+    notify_armour_equip(u.armour);
+    return Cost_std;
+}
+
+Action_cost put_on_ring(Obj_handle obj)
+{
+    if (u.ring != NO_OBJ)
+    {
+        debug_put_on_second_ring();
+        return Cost_none;
+    }
+    if (!(objects[obj].flags & OF_WITH_YOU))
+    {
+        debug_put_on_uncarried_ring();
+        return Cost_none;
+    }
+    u.ring = obj;
+    notify_ring_equip(u.ring);
+    recalc_defence();
+    return Cost_std;
+}
+
+Action_cost remove_ring(void)
+{
+    int saved_ring = u.ring;
+    if (u.ring == NO_OBJ)
+    {
+        debug_remove_no_ring();
+        return Cost_none;
+    }
+    u.ring = NO_OBJ;
+    recalc_defence();
+    notify_ring_unequip(saved_ring);
+    return Cost_std;
+}
+
+Pass_fail ring_removal_unsafe(Noisiness noisy)
+{
+    if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
+    {
+        if (noisy != Noise_silent)
+        {
+            notify_lava_blocks_unequip();
+        }
+        return You_fail;
+    }
+    else if ((objects[u.ring].po_ref == PO_FROST_RING) && (lvl.terrain_at(u.pos) == WATER))
+    {
+        if (noisy != Noise_silent)
+        {
+            notify_water_blocks_unequip();
+        }
+        return You_fail;
+    }
+    return You_pass;
+}
+
+/* objects.cc */
+// vim:cindent
diff --git a/objects.cc b/objects.cc
deleted file mode 100644 (file)
index 0d5c014..0000000
+++ /dev/null
@@ -1,915 +0,0 @@
-/* \file objects.cc
- * \brief Object handling for Obumbrata et Velata
- */
-
-/* Copyright 2005-2013 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 OBJECTS_CC
-#include "obumbrata.hh"
-#include "objects.hh"
-#include "monsters.hh"
-
-#include <string.h>
-
-std::map<Obj_handle, Obj> objects;
-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)
-{
-    Obj *optr = obj_snapv(obj);
-    switch (optr->po_ref)
-    {
-    case PO_TELEPORT_SCROLL:
-        teleport_u();
-        break;
-    case PO_FIRE_SCROLL:
-        notify_item_explodes_flames(obj);
-        if (u.resistances[DT_FIRE])
-        {
-            notify_player_ignore_damage(DT_FIRE);
-        }
-        else
-        {
-            damage_u(dice(4, 10), DEATH_KILLED, "searing flames");
-        }
-#if 0
-       // TODO iterate over visible monsters
-        for (i = 0; i < MONSTERS_IN_PLAY; ++i)
-        {
-            if (mon_visible(i))
-            {
-                if (!pmon_resists_fire(monsters[i].pm_ref))
-                {
-                    notify_fireitem_hit(i);
-                    damage_mon(i, dice(4, 10), true);
-                }
-            }
-        }
-#endif
-        break;
-    case PO_PROTECTION_SCROLL:
-        notify_read_scroll_protection();
-        if (!u.protection)
-        {
-            /* Do not prolong existing protection, only grant
-             * protection to the unprotected. */
-            u.protection = 100;
-        }
-        if (u.withering)
-        {
-            u.withering = 0;
-            notify_wither_recovered();
-        }
-        if (u.armourmelt)
-        {
-            u.armourmelt = 0;
-            notify_armourmelt_recovered();
-        }
-        if (u.leadfoot)
-        {
-            u.leadfoot = 0;
-            notify_leadfoot_recovered();
-        }
-        recalc_defence();
-        break;
-    default:
-        debug_read_non_scroll();
-        return Cost_none;
-    }
-    consume_obj(obj);
-    return Cost_std;
-}
-
-bool consume_obj(Obj_handle obj)
-{
-    int i;
-    Obj *optr = obj_snapv(obj);
-    optr->quan--;
-    if (optr->quan == 0)
-    {
-        optr->flags &= ~OF_USED;
-        if (optr->flags & OF_WITH_YOU)
-        {
-            if (obj == u.armour)
-            {
-                u.armour = NO_OBJ;
-                recalc_defence();
-                notify_armour_broke(obj);
-            }
-            else if (obj == u.weapon)
-            {
-                u.weapon = NO_OBJ;
-                recalc_defence();
-                notify_weapon_broke(obj);
-            }
-            else if (obj == u.ring)
-            {
-                u.ring = NO_OBJ;
-                recalc_defence();
-            }
-           for (i = 0; i < 19; i++)
-           {
-               if (u.inventory[i] == obj)
-               {
-                   u.inventory[i] = NO_OBJ;
-                   break;
-               }
-           }
-        }
-        return true;
-    }
-    return false;
-}
-
-/*! \brief Consume a food */
-Action_cost eat_food(Obj_handle obj)
-{
-    Obj *optr = obj_snapv(obj);
-    bool ravenous = (u.food < 0);
-    u.food += 1500;
-    if (permobjs[optr->po_ref].poclass != POCLASS_FOOD)
-    {
-        debug_eat_non_food(obj);
-        return Cost_none;
-    }
-    if (optr->po_ref == PO_DEVIL_SPLEEN)
-    {
-        notify_ingest_spleen();
-        if (zero_die(2))
-        {
-            gain_body(1);
-        }
-        else
-        {
-            gain_agility(1);
-        }
-        // TODO add more tracking state to this item so we don't have to randomize the fell power choice
-        ++u.sympathy[zero_die(TOTAL_FELL_POWERS)];
-    }
-    else
-    {
-        notify_eat_food(ravenous);
-    }
-    consume_obj(obj);
-    return Cost_std;
-}
-
-/*! \brief Effect of quaffing a body potion */
-static void body_potion_quaff(void)
-{
-    gain_body(1);
-}
-
-/*! \brief Effect of quaffing a agility potion */
-static void agility_potion_quaff(void)
-{
-    gain_agility(1);
-}
-
-/*! \brief Effect of quaffing a healing potion */
-static void healing_potion_quaff(void)
-{
-    int healpercent = inc_flat(30, 50);
-    int healamount = (healpercent * ((u.hpmax > 60) ? u.hpmax : 60)) / 100;
-    heal_u(healamount, 1, 1);
-}
-
-/*! \brief Effect of quaffing a restoration potion */
-static void restoration_potion_quaff(void)
-{
-    notify_quaff_potion_restoration();
-    if (u.bdam && ((!u.adam) || zero_die(2)))
-    {
-       u.bdam = 0;
-       notify_body_restore();
-    }
-    else if (u.adam)
-    {
-       u.adam = 0;
-       notify_agility_restore();
-    }
-}
-
-struct Potion_table_entry
-{
-    int pobj;
-    void (*quaff_func)(void);
-};
-
-static Potion_table_entry quaff_table[] =
-{
-    { PO_BODY_POTION, body_potion_quaff },
-    { PO_AGILITY_POTION, agility_potion_quaff },
-    { PO_HEALING_POTION, healing_potion_quaff },
-    { PO_RESTORATION_POTION, restoration_potion_quaff },
-    { NO_POBJ, nullptr },
-};
-
-/*! \brief Consume a potion */
-Action_cost quaff_potion(Obj_handle obj)
-{
-    Obj *optr = obj_snapv(obj);
-    int i;
-    for (i = 0; quaff_table[i].pobj != NO_POBJ; ++i)
-    {
-       if (quaff_table[i].pobj == optr->po_ref)
-       {
-           quaff_table[i].quaff_func();
-           consume_obj(obj);
-           return Cost_std;
-       }
-    }
-    debug_quaff_non_potion();
-    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)
-{
-    if (first_free_obj_handle == 0u)
-    {
-       return NO_OBJ;
-    }
-    return first_free_obj_handle++;
-}
-
-Obj_handle create_obj_class_near(enum poclass_num po_class, int quantity, bool with_you, Coord c)
-{
-    int po_idx;
-    int tryct;
-    for (tryct = 0; tryct < 200; tryct++)
-    {
-        switch (po_class)
-        {
-        case POCLASS_POTION:
-            po_idx = inc_flat(PO_FIRST_POTION, PO_LAST_POTION);
-            break;
-        case POCLASS_SCROLL:
-            po_idx = inc_flat(PO_FIRST_SCROLL, PO_LAST_SCROLL);
-            break;
-        case POCLASS_RING:
-            po_idx = inc_flat(PO_FIRST_RING, PO_LAST_RING);
-            break;
-        default:
-            /* No getting armour/weapons by class... yet. */
-            return NO_OBJ;
-        }
-        if (zero_die(100) < permobjs[po_idx].rarity)
-        {
-            continue;
-        }
-        break;
-    }
-    return (with_you ? create_obj(po_idx, quantity, with_you, c) : create_obj_near(po_idx, quantity, c));
-}
-
-int get_random_pobj(void)
-{
-    int tryct;
-    int po_idx;
-    for (tryct = 0; tryct < 200; tryct++)
-    {
-        po_idx = zero_die(NUM_OF_PERMOBJS);
-        if (zero_die(100) < permobjs[po_idx].rarity)
-        {
-            po_idx = NO_POBJ;
-            continue;
-        }
-        if (depth < permobjs[po_idx].depth)
-        {
-            po_idx = NO_POBJ;
-            continue;
-        }
-        break;
-    }
-    return po_idx;
-}
-
-Obj_handle create_corpse(int pm_idx, Coord c)
-{
-    Obj_handle obj = create_obj_near(PO_CORPSE, 1, c);
-    if (obj != NO_OBJ)
-    {
-       Obj *o = obj_snapv(obj);
-       o->meta[0] = pm_idx;
-       o->meta[1] = 0; // reserved
-       o->meta[2] = 0; // reserved
-       o->meta[3] = 0; // reserved
-    }
-    return obj;
-}
-
-Obj_handle create_obj_near(int po_idx, int quantity, Coord c)
-{
-    Offset delta;
-    Coord nc;
-    int panic_count = 200;
-    while ((lvl.obj_at(c) != NO_OBJ) && (panic_count > 0))
-    {
-       do
-       {
-           delta = random_step();
-           nc = c + delta;
-       }
-       while (terrain_blocks_beings(lvl.terrain_at(nc)));
-       c = nc;
-       --panic_count;
-    }
-    if (panic_count < 1)
-    {
-       return NO_OBJ;
-    }
-    Obj_handle obj = create_obj(po_idx, quantity, false, c);
-    return obj;
-}
-
-Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c)
-{
-    Obj_handle obj = get_first_free_obj();
-    if (obj == NO_OBJ)
-    {
-        debug_object_pool_exhausted();
-        return NO_OBJ;
-    }
-    if (po_idx == NO_POBJ)
-    {
-        po_idx = get_random_pobj();
-        if (po_idx == NO_POBJ)
-        {
-            debug_pobj_select_failed();
-            return NO_OBJ;
-        }
-    }
-    Obj o;
-    memset(o.meta, '\0', sizeof o.meta);
-    o.self = obj;
-    o.po_ref = po_idx;
-    o.flags = OF_USED | (with_you ? OF_WITH_YOU : 0);
-    o.pos = c;
-    o.quan = quantity;
-    switch (permobjs[po_idx].poclass)
-    {
-    case POCLASS_WEAPON:
-    case POCLASS_ARMOUR:
-        o.durability = OBJ_MAX_DUR;
-        break;
-    default:
-        break;
-    }
-    objects[obj] = o;
-    if (!(o.flags & OF_WITH_YOU))
-    {
-        lvl.set_obj_at(c, obj);
-       notify_new_obj_at(c, obj);
-    }
-    return obj;
-}
-
-void asprint_obj_name(char **buf, Obj_handle obj)
-{
-    Obj *optr;
-    Permobj *poptr;
-    int i;
-    optr = obj_snapv(obj);
-    poptr = permobjs + optr->po_ref;
-    if (optr->quan > 1)
-    {
-        i = asprintf(buf, "%d %s", optr->quan, poptr->plural);
-    }
-    else if (po_is_stackable(optr->po_ref))
-    {
-        i = asprintf(buf, "1 %s", poptr->name);
-    }
-    else if ((poptr->poclass == POCLASS_WEAPON) ||
-             (poptr->poclass == POCLASS_ARMOUR))
-    {
-        i = asprintf(buf, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
-    }
-    else if (optr->po_ref == PO_CORPSE)
-    {
-       Permon *pmptr = permons + optr->meta[0];
-       i = asprintf(buf, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
-    }
-    else
-    {
-        i = asprintf(buf, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
-    }
-    if (i == -1)
-    {
-       // TODO wibble
-    }
-    return;
-}
-
-void sprint_obj_name(char *buf, Obj_handle obj, int len)
-{
-    Obj *optr;
-    Permobj *poptr;
-    optr = obj_snapv(obj);
-    poptr = permobjs + optr->po_ref;
-    if (optr->quan > 1)
-    {
-        snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
-    }
-    else if (po_is_stackable(optr->po_ref))
-    {
-        snprintf(buf, len, "1 %s", poptr->name);
-    }
-    else if ((poptr->poclass == POCLASS_WEAPON) ||
-             (poptr->poclass == POCLASS_ARMOUR))
-    {
-        snprintf(buf, len, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
-    }
-    else if (optr->po_ref == PO_CORPSE)
-    {
-       Permon *pmptr = permons + optr->meta[0];
-       snprintf(buf, len, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
-    }
-    else
-    {
-        snprintf(buf, len, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
-    }
-}
-
-void fprint_obj_name(FILE *fp, Obj_handle obj)
-{
-    char *s;
-    asprint_obj_name(&s, obj);
-    fputs(s, fp);
-    free(s);
-}
-
-Action_cost drop_obj(int inv_idx)
-{
-    Obj *optr;
-    optr = obj_snapv(u.inventory[inv_idx]);
-    if (lvl.obj_at(u.pos) == NO_OBJ)
-    {
-        optr->pos = u.pos;
-        lvl.set_obj_at(u.pos, u.inventory[inv_idx]);
-        if (u.weapon == u.inventory[inv_idx])
-        {
-            u.weapon = NO_OBJ;
-        }
-        u.inventory[inv_idx] = NO_OBJ;
-        optr->flags &= ~OF_WITH_YOU;
-        notify_drop_item(lvl.obj_at(u.pos));
-        return Cost_std;
-    }
-    else
-    {
-        notify_drop_blocked();
-        return Cost_none;
-    }
-}
-
-bool po_is_stackable(int po)
-{
-    switch (permobjs[po].poclass)
-    {
-    default:
-        return false;
-    case POCLASS_POTION:
-    case POCLASS_SCROLL:
-    case POCLASS_FOOD:
-        return true;
-    }
-}
-
-void attempt_pickup(void)
-{
-    int i;
-    int stackable;
-    Obj_handle oh = lvl.obj_at(u.pos);
-    Obj *optr = obj_snapv(oh);
-    Obj *invptr;
-    stackable = po_is_stackable(optr->po_ref);
-    if (stackable)
-    {
-        for (i = 0; i < 19; i++)
-        {
-           invptr = obj_snapv(u.inventory[i]);
-           if (invptr && (invptr->po_ref == optr->po_ref))
-           {
-               invptr->quan += optr->quan;
-               optr->flags &= ~OF_USED;
-                lvl.set_obj_at(u.pos, NO_OBJ);
-                notify_get_item(oh, i);
-                return;
-            }
-        }
-    }
-    for (i = 0; i < 19; i++)
-    {
-        if (u.inventory[i] == NO_OBJ)
-        {
-            break;
-        }
-    }
-    if (i == 19)
-    {
-        notify_pack_full();
-        return;
-    }
-    u.inventory[i] = lvl.obj_at(u.pos);
-    lvl.set_obj_at(u.pos, NO_OBJ);
-    optr->flags |= OF_WITH_YOU;
-    optr->pos = Nowhere;
-    notify_get_item(NO_OBJ, i);
-}
-
-void break_reaction(Obj_handle obj)
-{
-    switch (objects[obj].po_ref)
-    {
-    case PO_TORMENTORS_LASH:
-        if (u.food < 500)
-        {
-            int shortfall = (u.food < 0) ? 500 : 500 - u.food;
-            int damage = (shortfall + 24) / 25;
-            u.food = (u.food < 0) ? u.food : 0;
-            notify_lash_activation(3);
-            damage_u(damage, DEATH_LASH, "");
-        }
-        else
-        {
-            u.food -= 500;
-            objects[obj].durability = 100;
-            notify_lash_activation(1);
-        }
-        break;
-
-    default:
-        if (permobjs[objects[obj].po_ref].flags[0] & POF_DRESS)
-        {
-            objects[obj].durability = 50 + zero_die(51);
-            objects[obj].po_ref = PO_RAGGED_SHIFT;
-            notify_dress_shredded();
-            recalc_defence();
-        }
-        else
-        {
-            debug_unimplemented_break_reaction(objects[obj].po_ref);
-        }
-        break;
-    }
-}
-
-void damage_obj(Obj_handle obj)
-{
-    /* Only weapons and armour have non-zero durability. */
-    if (objects[obj].durability == 0)
-    {
-        /* Break the object. Weapons and armour don't stack. */
-        consume_obj(obj);
-    }
-    else
-    {
-        objects[obj].durability--;
-        if ((objects[obj].durability == 0) && (permobjs[objects[obj].po_ref].flags[0] & POF_BREAK_REACT))
-        {
-            break_reaction(obj);
-        }
-    }
-} 
-
-int evasion_penalty(Obj_handle obj)
-{
-    if (permobjs[objects[obj].po_ref].poclass == POCLASS_ARMOUR)
-    {
-        return permobjs[objects[obj].po_ref].power2;
-    }
-    return 100;
-}
-
-Action_cost magic_ring(void)
-{
-    Obj *optr = obj_snapv(u.ring);
-    switch (optr->po_ref)
-    {
-    case PO_TELEPORT_RING:
-        if (u.food >= 50)
-        {
-            u.food -= 50;
-            notify_telering_activation(1);
-            teleport_u();
-            return Cost_std;
-        }
-        else
-        {
-            notify_telering_activation(0);
-        }
-        return Cost_none;
-    default:
-        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
-        {
-            debug_unimplemented_activation(optr->po_ref);
-        }
-        else
-        {
-            notify_magic_powerless_ring();
-        }
-        return Cost_none;
-    }
-}
-
-Action_cost emanate_armour(void)
-{
-    Obj *optr = obj_snapv(u.armour);
-    switch (optr->po_ref)
-    {
-    default:
-        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
-        {
-            debug_unimplemented_activation(optr->po_ref);
-        }
-        else
-        {
-            notify_emanate_powerless_armour();
-        }
-        break;
-    }
-    return Cost_none;
-}
-
-Action_cost zap_weapon(void)
-{
-    Obj *optr = obj_snapv(u.weapon);
-    switch (optr->po_ref)
-    {
-    case PO_STAFF_OF_FIRE:
-        if (u.food > 150)
-        {
-            Coord c;
-            u.food -= 150;
-            notify_firestaff_activation(1);
-            for (c.y = u.pos.y - 1; c.y <= u.pos.y + 1; ++c.y)
-            {
-                if ((c.y < lvl.min_y()) || (c.y >= lvl.max_y()))
-                {
-                    continue;
-                }
-                for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
-                {
-                    Mon_handle mon;
-                    if ((c.x < lvl.min_x()) || (c.x >= lvl.max_x()))
-                    {
-                        continue;
-                    }
-                    mon = lvl.mon_at(c);
-                    if (mon != NO_MON)
-                    {
-                        Mon *mptr = mon_snapv(mon);
-                        if (!pmon_resists_fire(mptr->pm_ref))
-                        {
-                            notify_fireitem_hit(mon);
-                            damage_mon(mon, dice(4, 10), true);
-                        }
-                    }
-                }
-            }
-            damage_obj(u.weapon);
-        }
-        else
-        {
-            notify_firestaff_activation(0);
-            return Cost_none;
-        }
-        return Cost_std;
-    default:
-        if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
-        {
-            debug_unimplemented_activation(optr->po_ref);
-        }
-        else
-        {
-            notify_zap_powerless_weapon();
-        }
-        return Cost_none;
-    }
-}
-
-/*! \brief Unwield the player's currently equipped weapon
- *
- *  \todo Stickycurse?
- *  \todo Life-essential resistances?
- */
-Action_cost player_unwield(Noisiness noisy)
-{
-    int saved_weapon = u.weapon;
-    if (u.weapon == NO_OBJ)
-    {
-        debug_unwield_nothing();
-        return Cost_none;
-    }
-    u.weapon = NO_OBJ;
-    recalc_defence();
-    notify_unwield(saved_weapon, Noise_std);
-    return Cost_std;
-}
-
-Action_cost player_wield(Obj_handle obj, Noisiness noisy)
-{
-    if (u.weapon != NO_OBJ)
-    {
-        player_unwield(Noise_low);
-    }
-    u.weapon = obj;
-    notify_wield_weapon(u.weapon);
-    recalc_defence();
-    return Cost_std;
-}
-
-Action_cost wear_armour(Obj_handle obj)
-{
-    if (u.armour != NO_OBJ)
-    {
-        debug_wear_while_wearing();
-        return Cost_none;
-    }
-    if (!(objects[obj].flags & OF_WITH_YOU))
-    {
-        debug_wear_uncarried_armour();
-        return Cost_none;
-    }
-    u.armour = obj;
-    recalc_defence();
-    notify_armour_equip(u.armour);
-    return Cost_std;
-}
-
-Action_cost put_on_ring(Obj_handle obj)
-{
-    if (u.ring != NO_OBJ)
-    {
-        debug_put_on_second_ring();
-        return Cost_none;
-    }
-    if (!(objects[obj].flags & OF_WITH_YOU))
-    {
-        debug_put_on_uncarried_ring();
-        return Cost_none;
-    }
-    u.ring = obj;
-    notify_ring_equip(u.ring);
-    recalc_defence();
-    return Cost_std;
-}
-
-Action_cost remove_ring(void)
-{
-    int saved_ring = u.ring;
-    if (u.ring == NO_OBJ)
-    {
-        debug_remove_no_ring();
-        return Cost_none;
-    }
-    u.ring = NO_OBJ;
-    recalc_defence();
-    notify_ring_unequip(saved_ring);
-    return Cost_std;
-}
-
-Pass_fail ring_removal_unsafe(Noisiness noisy)
-{
-    if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
-    {
-        if (noisy != Noise_silent)
-        {
-            notify_lava_blocks_unequip();
-        }
-        return You_fail;
-    }
-    else if ((objects[u.ring].po_ref == PO_FROST_RING) && (lvl.terrain_at(u.pos) == WATER))
-    {
-        if (noisy != Noise_silent)
-        {
-            notify_water_blocks_unequip();
-        }
-        return You_fail;
-    }
-    return You_pass;
-}
-
-/* objects.cc */
-// vim:cindent