--- /dev/null
+/* \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
+++ /dev/null
-/* \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