summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Feinberg <pleasingfung@gmail.com>2014-07-01 21:58:07 -0700
committerNicholas Feinberg <pleasingfung@gmail.com>2014-07-01 23:24:48 -0700
commitd20dde44ce56c0dfcf7ade7f691b479748ebcafe (patch)
tree9a4532191645b68ee7a6b0885735f811bbcd4721
parentfc78f0c065fcd4d19bddc74daaf1d70cc5c68792 (diff)
downloadcrawl-ref-d20dde44ce56c0dfcf7ade7f691b479748ebcafe.tar.gz
crawl-ref-d20dde44ce56c0dfcf7ade7f691b479748ebcafe.zip
Move blood rotting into a new file
-rw-r--r--crawl-ref/source/Makefile.obj1
-rw-r--r--crawl-ref/source/acquire.cc2
-rw-r--r--crawl-ref/source/dungeon.cc1
-rw-r--r--crawl-ref/source/effects.cc1
-rw-r--r--crawl-ref/source/item_use.cc1
-rw-r--r--crawl-ref/source/items.cc1
-rw-r--r--crawl-ref/source/makeitem.cc2
-rw-r--r--crawl-ref/source/misc.cc555
-rw-r--r--crawl-ref/source/misc.h9
-rw-r--r--crawl-ref/source/mon-act.cc1
-rw-r--r--crawl-ref/source/mon-death.cc1
-rw-r--r--crawl-ref/source/monster.cc1
-rw-r--r--crawl-ref/source/ng-setup.cc2
-rw-r--r--crawl-ref/source/rot.cc572
-rw-r--r--crawl-ref/source/rot.h20
-rw-r--r--crawl-ref/source/shopping.cc2
-rw-r--r--crawl-ref/source/spl-other.cc1
-rw-r--r--crawl-ref/source/throw.cc1
18 files changed, 607 insertions, 567 deletions
diff --git a/crawl-ref/source/Makefile.obj b/crawl-ref/source/Makefile.obj
index d0e861f889..19ffbbb653 100644
--- a/crawl-ref/source/Makefile.obj
+++ b/crawl-ref/source/Makefile.obj
@@ -182,6 +182,7 @@ random.o \
random-var.o \
ranged_attack.o \
ray.o \
+rot.o \
religion.o \
shopping.o \
shout.o \
diff --git a/crawl-ref/source/acquire.cc b/crawl-ref/source/acquire.cc
index d8576f0de2..98c95bddf6 100644
--- a/crawl-ref/source/acquire.cc
+++ b/crawl-ref/source/acquire.cc
@@ -29,11 +29,11 @@
#include "libutil.h"
#include "makeitem.h"
#include "message.h"
-#include "misc.h"
#include "player.h"
#include "random.h"
#include "random-weight.h"
#include "religion.h"
+#include "rot.h"
#include "skills2.h"
#include "spl-book.h"
#include "spl-util.h"
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index e8cba91ef9..c0a4e64f93 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -68,6 +68,7 @@
#include "random.h"
#include "random-weight.h"
#include "religion.h"
+#include "rot.h"
#include "show.h"
#include "spl-book.h"
#include "spl-transloc.h"
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 5b583d3ef5..10bb01fef1 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -65,6 +65,7 @@
#include "player-stats.h"
#include "player.h"
#include "religion.h"
+#include "rot.h"
#include "shopping.h"
#include "shout.h"
#include "skills.h"
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index b87450f7f4..7425cd4fce 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -45,6 +45,7 @@
#include "potion.h"
#include "random.h"
#include "religion.h"
+#include "rot.h"
#include "shout.h"
#include "skills.h"
#include "skills2.h"
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index a1b19fa542..008d2c3d04 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -56,6 +56,7 @@
#include "player-equip.h"
#include "quiver.h"
#include "religion.h"
+#include "rot.h"
#include "shopping.h"
#include "showsymb.h"
#include "spl-book.h"
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index 83f78ddc02..91b42e5b79 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -25,10 +25,10 @@
#include "itemprop.h"
#include "items.h"
#include "libutil.h"
-#include "misc.h"
#include "mon-util.h"
#include "player.h"
#include "random.h"
+#include "rot.h"
#include "spl-book.h"
#include "state.h"
#include "stuff.h"
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index c58b31df45..2c7e5ebcc8 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -64,6 +64,7 @@
#include "player-stats.h"
#include "random.h"
#include "religion.h"
+#include "rot.h"
#include "godconduct.h"
#include "shopping.h"
#include "skills.h"
@@ -258,246 +259,6 @@ void butcher_corpse(item_def &item, maybe_bool skeleton, bool chunks)
}
}
-// Initialise blood potions with a vector of timers.
-void init_stack_blood_potions(item_def &stack, int age)
-{
- ASSERT(is_blood_potion(stack));
-
- CrawlHashTable &props = stack.props;
- props.clear(); // sanity measure
- props["timer"].new_vector(SV_INT, SFLAG_CONST_TYPE);
- CrawlVector &timer = props["timer"].get_vector();
-
- if (age == -1)
- {
- if (stack.sub_type == POT_BLOOD)
- age = 2500;
- else // coagulated blood
- age = 500;
- }
- // For a newly created stack, all potions use the same timer.
- const int max_age = you.num_turns + age;
-#ifdef DEBUG_BLOOD_POTIONS
- mprf(MSGCH_DIAGNOSTICS, "newly created stack will time out at turn %d",
- max_age);
-#endif
- for (int i = 0; i < stack.quantity; i++)
- timer.push_back(max_age);
-
- stack.special = 0;
- ASSERT(timer.size() == stack.quantity);
- props.assert_validity();
-}
-
-// Sort a CrawlVector<int>, should probably be done properly with templates.
-static void _int_sort(CrawlVector &vec)
-{
- vector<int> help;
- while (!vec.empty())
- {
- help.push_back(vec[vec.size()-1].get_int());
- vec.pop_back();
- }
-
- sort(help.begin(), help.end());
-
- while (!help.empty())
- {
- vec.push_back(help[help.size()-1]);
- help.pop_back();
- }
-}
-
-static void _compare_blood_quantity(item_def &stack, int timer_size)
-{
- if (timer_size != stack.quantity)
- {
- mprf(MSGCH_WARN,
- "ERROR: blood potion quantity (%d) doesn't match timer (%d)",
- stack.quantity, timer_size);
-
- // sanity measure
- stack.quantity = timer_size;
- }
-}
-
-static void _init_coagulated_blood(item_def &stack, int count, item_def &old,
- vector<int> &age_timer)
-{
- stack.base_type = OBJ_POTIONS;
- stack.sub_type = POT_BLOOD_COAGULATED;
- stack.quantity = count;
- stack.plus = 0;
- stack.plus2 = 0;
- stack.special = 0;
- stack.flags = old.flags & (ISFLAG_DROPPED | ISFLAG_THROWN
- | ISFLAG_NO_PICKUP | ISFLAG_SUMMONED
- | ISFLAG_DROPPED_BY_ALLY);
- stack.inscription = old.inscription;
- item_colour(stack);
-
- CrawlHashTable &props_new = stack.props;
- props_new["timer"].new_vector(SV_INT, SFLAG_CONST_TYPE);
- CrawlVector &timer_new = props_new["timer"].get_vector();
-
- int val;
- while (!age_timer.empty())
- {
- val = age_timer[age_timer.size() - 1];
- age_timer.pop_back();
- timer_new.push_back(val);
- }
- ASSERT(timer_new.size() == count);
- props_new.assert_validity();
-}
-
-void maybe_coagulate_blood_potions_floor(int obj)
-{
- item_def &blood = mitm[obj];
- ASSERT(blood.defined());
- ASSERT(is_blood_potion(blood));
-
- CrawlHashTable &props = blood.props;
- if (!props.exists("timer"))
- init_stack_blood_potions(blood, blood.special);
-
- ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"].get_vector();
- ASSERT(!timer.empty());
- _compare_blood_quantity(blood, timer.size());
-
- // blood.sub_type could be POT_BLOOD or POT_BLOOD_COAGULATED
- // -> need different handling
- int rot_limit = you.num_turns;
- int coag_limit = you.num_turns + 500; // check 500 turns later
-
- // First count whether coagulating is even necessary.
- int rot_count = 0;
- int coag_count = 0;
- vector<int> age_timer;
- int current;
- while (!timer.empty())
- {
- current = timer[timer.size()-1].get_int();
- if (current > coag_limit
- || blood.sub_type == POT_BLOOD_COAGULATED && current > rot_limit)
- {
- // Still some time until rotting/coagulating.
- break;
- }
-
- timer.pop_back();
- if (current <= rot_limit)
- rot_count++;
- else if (blood.sub_type == POT_BLOOD && current <= coag_limit)
- {
- coag_count++;
- age_timer.push_back(current);
- }
- }
-
- if (!rot_count && !coag_count)
- return; // Nothing to be done.
-
-#ifdef DEBUG_BLOOD_POTIONS
- mprf(MSGCH_DIAGNOSTICS, "in maybe_coagulate_blood_potions_FLOOR "
- "(turns: %d)", you.num_turns);
-
- mprf(MSGCH_DIAGNOSTICS, "Something happened at pos (%d, %d)!",
- blood.x, blood.y);
- mprf(MSGCH_DIAGNOSTICS, "coagulated: %d, rotted: %d, total: %d",
- coag_count, rot_count, blood.quantity);
- more();
-#endif
-
- if (!coag_count) // Some potions rotted away.
- {
- dec_mitm_item_quantity(obj, rot_count);
- // Timer is already up to date.
- return;
- }
-
- // Coagulated blood cannot coagulate any further...
- ASSERT(blood.sub_type == POT_BLOOD);
-
- if (!blood.held_by_monster())
- {
- // Now that coagulating is necessary, check square for
- // !coagulated blood.
- ASSERT(blood.pos.x >= 0);
- ASSERT(blood.pos.y >= 0);
- for (stack_iterator si(blood.pos); si; ++si)
- {
- if (si->base_type == OBJ_POTIONS
- && si->sub_type == POT_BLOOD_COAGULATED)
- {
- // Merge with existing stack.
- CrawlHashTable &props2 = si->props;
- if (!props2.exists("timer"))
- init_stack_blood_potions(*si, si->special);
-
- ASSERT(props2.exists("timer"));
- CrawlVector &timer2 = props2["timer"].get_vector();
- ASSERT(timer2.size() == si->quantity);
-
- // Update timer -> push(pop).
- while (!age_timer.empty())
- {
- const int val = age_timer.back();
- age_timer.pop_back();
- timer2.push_back(val);
- }
- _int_sort(timer2);
- inc_mitm_item_quantity(si.link(), coag_count);
- ASSERT(timer2.size() == si->quantity);
- dec_mitm_item_quantity(obj, rot_count + coag_count);
- return;
- }
- }
- }
- // If we got here, nothing was found! (Or it's in a monster's
- // inventory.)
-
- // Entire stack is gone, rotted or coagulated.
- // -> Change potions to coagulated type.
- if (rot_count + coag_count == blood.quantity)
- {
- ASSERT(timer.empty());
-
- // Update subtype.
- blood.sub_type = POT_BLOOD_COAGULATED;
- item_colour(blood);
-
- // Re-fill vector.
- int val;
- while (!age_timer.empty())
- {
- val = age_timer[age_timer.size() - 1];
- age_timer.pop_back();
- timer.push_back(val);
- }
- dec_mitm_item_quantity(obj, rot_count);
- _compare_blood_quantity(blood, timer.size());
- return;
- }
-
- // Else, create a new stack of potions.
- int o = get_mitm_slot(20);
- if (o == NON_ITEM)
- return;
-
- item_def &item = mitm[o];
- _init_coagulated_blood(item, coag_count, blood, age_timer);
-
- if (blood.held_by_monster())
- move_item_to_grid(&o, blood.holding_monster()->pos());
- else
- move_item_to_grid(&o, blood.pos);
-
- dec_mitm_item_quantity(obj, rot_count + coag_count);
- _compare_blood_quantity(blood, timer.size());
-}
-
string get_desc_quantity(const int quant, const int total, string whose)
{
if (total == quant)
@@ -512,320 +273,6 @@ string get_desc_quantity(const int quant, const int total, string whose)
return "Some of " + whose;
}
-// Prints messages for blood potions coagulating or rotting in inventory.
-static void _potion_stack_changed_message(item_def &potion, int num_changed,
- string verb)
-{
- ASSERT(num_changed > 0);
-
- verb = replace_all(verb, "%s", num_changed == 1 ? "s" : "");
- mprf(MSGCH_ROTTEN_MEAT, "%s %s %s.",
- get_desc_quantity(num_changed, potion.quantity).c_str(),
- potion.name(DESC_PLAIN, false).c_str(),
- verb.c_str());
-}
-
-
-/**
- * Coagulate and/or rot away blood potions in a stack if necessary.
- * @param blood The blood potion.
-*/
-void maybe_coagulate_blood_potions_inv(item_def &blood)
-{
- ASSERT(blood.defined());
- ASSERT(is_blood_potion(blood));
-
- CrawlHashTable &props = blood.props;
- if (!props.exists("timer"))
- init_stack_blood_potions(blood, blood.special);
-
- ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"].get_vector();
- _compare_blood_quantity(blood, timer.size());
- ASSERT(!timer.empty());
-
- // blood.sub_type could be POT_BLOOD or POT_BLOOD_COAGULATED
- // -> need different handling
- int rot_limit = you.num_turns;
- int coag_limit = you.num_turns + 500; // check 500 turns later
-
- // First count whether coagulating is even necessary.
- int rot_count = 0;
- int coag_count = 0;
- vector<int> age_timer;
- int current;
- const int size = timer.size();
- for (int i = 0; i < size; i++)
- {
- current = timer[timer.size()-1].get_int();
- if (current > coag_limit
- || blood.sub_type == POT_BLOOD_COAGULATED && current > rot_limit)
- {
- // Still some time until rotting/coagulating.
- break;
- }
-
- timer.pop_back();
- if (current <= rot_limit)
- rot_count++;
- else if (blood.sub_type == POT_BLOOD && current <= coag_limit)
- {
- coag_count++;
- age_timer.push_back(current);
- }
- }
-
- if (!rot_count && !coag_count)
- return; // Nothing to be done.
-
-#ifdef DEBUG_BLOOD_POTIONS
- mprf(MSGCH_DIAGNOSTICS, "in maybe_coagulate_blood_potions_INV "
- "(turns: %d)", you.num_turns);
-
- mprf(MSGCH_DIAGNOSTICS, "coagulated: %d, rotted: %d, total: %d",
- coag_count, rot_count, blood.quantity);
- more();
-#endif
-
- // just in case
- you.wield_change = true;
- you.redraw_quiver = true;
-
- if (!coag_count) // Some potions rotted away, but none coagulated.
- {
- // Only coagulated blood can rot.
- ASSERT(blood.sub_type == POT_BLOOD_COAGULATED);
- _potion_stack_changed_message(blood, rot_count, "rot%s away");
- bool destroyed = dec_inv_item_quantity(blood.link, rot_count);
-
- if (!destroyed)
- _compare_blood_quantity(blood, timer.size());
- return;
- }
-
- // Coagulated blood cannot coagulate any further...
- ASSERT(blood.sub_type == POT_BLOOD);
-
- _potion_stack_changed_message(blood, coag_count, "coagulate%s");
-
- request_autoinscribe();
-
- // Now that coagulating is necessary, check inventory for !coagulated blood.
- for (int m = 0; m < ENDOFPACK; m++)
- {
- if (!you.inv[m].defined())
- continue;
-
- if (you.inv[m].base_type == OBJ_POTIONS
- && you.inv[m].sub_type == POT_BLOOD_COAGULATED)
- {
- CrawlHashTable &props2 = you.inv[m].props;
- if (!props2.exists("timer"))
- init_stack_blood_potions(you.inv[m], you.inv[m].special);
-
- ASSERT(props2.exists("timer"));
- CrawlVector &timer2 = props2["timer"].get_vector();
- if (!dec_inv_item_quantity(blood.link, coag_count + rot_count))
- _compare_blood_quantity(blood, timer.size());
-
- // Update timer -> push(pop).
- int val;
- while (!age_timer.empty())
- {
- val = age_timer[age_timer.size() - 1];
- age_timer.pop_back();
- timer2.push_back(val);
- }
-
- you.inv[m].quantity += coag_count;
- ASSERT(timer2.size() == you.inv[m].quantity);
-
- // re-sort timer
- _int_sort(timer2);
- return;
- }
- }
-
- // If entire stack has coagulated, simply change subtype.
- if (rot_count + coag_count == blood.quantity)
- {
- ASSERT(timer.empty());
- // Update subtype.
- blood.sub_type = POT_BLOOD_COAGULATED;
- item_colour(blood);
-
- // Re-fill vector.
- int val;
- while (!age_timer.empty())
- {
- val = age_timer[age_timer.size() - 1];
- age_timer.pop_back();
- timer.push_back(val);
- }
- blood.quantity -= rot_count;
- // Stack still exists because of coag_count.
- _compare_blood_quantity(blood, timer.size());
- return;
- }
-
- // Else, create new stack in inventory.
- int freeslot = find_free_slot(blood);
- if (freeslot >= 0 && freeslot < ENDOFPACK)
- {
- item_def &item = you.inv[freeslot];
- item.clear();
- item.link = freeslot;
- item.slot = index_to_letter(item.link);
- item.pos.set(-1, -1);
- _init_coagulated_blood(item, coag_count, blood, age_timer);
-
- blood.quantity -= coag_count + rot_count;
- _compare_blood_quantity(blood, timer.size());
- return;
- }
-
- mprf("You can't carry %s right now.", coag_count > 1 ? "them" : "it");
-
- // No space in inventory, check floor.
- int o = igrd(you.pos());
- while (o != NON_ITEM)
- {
- if (mitm[o].base_type == OBJ_POTIONS
- && mitm[o].sub_type == POT_BLOOD_COAGULATED)
- {
- // Merge with existing stack.
- CrawlHashTable &props2 = mitm[o].props;
- if (!props2.exists("timer"))
- init_stack_blood_potions(mitm[o], mitm[o].special);
-
- ASSERT(props2.exists("timer"));
- CrawlVector &timer2 = props2["timer"].get_vector();
- ASSERT(timer2.size() == mitm[o].quantity);
-
- // Update timer -> push(pop).
- int val;
- while (!age_timer.empty())
- {
- val = age_timer[age_timer.size() - 1];
- age_timer.pop_back();
- timer2.push_back(val);
- }
- _int_sort(timer2);
-
- inc_mitm_item_quantity(o, coag_count);
- ASSERT(timer2.size() == mitm[o].quantity);
- dec_inv_item_quantity(blood.link, rot_count + coag_count);
- _compare_blood_quantity(blood, timer.size());
- return;
- }
- o = mitm[o].link;
- }
- // If we got here nothing was found!
-
- // Create a new stack of potions.
- o = get_mitm_slot();
- if (o == NON_ITEM)
- return;
-
- _init_coagulated_blood(mitm[o], coag_count, blood, age_timer);
-
- move_item_to_grid(&o, you.pos());
-
- if (!dec_inv_item_quantity(blood.link, coag_count + rot_count))
- _compare_blood_quantity(blood, timer.size());
-}
-
-// Removes the oldest timer of a stack of blood potions.
-// Mostly used for (q)uaff and (f)ire.
-int remove_oldest_blood_potion(item_def &stack)
-{
- ASSERT(stack.defined());
- ASSERT(is_blood_potion(stack));
-
- CrawlHashTable &props = stack.props;
- if (!props.exists("timer"))
- init_stack_blood_potions(stack, stack.special);
- ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"].get_vector();
- ASSERT(!timer.empty());
-
- // Assuming already sorted, and first (oldest) potion valid.
- const int val = timer[timer.size() - 1].get_int();
- timer.pop_back();
-
- // The quantity will be decreased elsewhere.
- return val;
-}
-
-// Used whenever copies of blood potions have to be cleaned up.
-void remove_newest_blood_potion(item_def &stack, int quant)
-{
- ASSERT(stack.defined());
- ASSERT(is_blood_potion(stack));
-
- CrawlHashTable &props = stack.props;
- if (!props.exists("timer"))
- init_stack_blood_potions(stack, stack.special);
- ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"].get_vector();
- ASSERT(!timer.empty());
-
- if (quant == -1)
- quant = timer.size() - stack.quantity;
-
- // Overwrite newest potions with oldest ones.
- int repeats = stack.quantity;
- if (repeats > quant)
- repeats = quant;
-
- for (int i = 0; i < repeats; i++)
- {
- timer[i] = timer[timer.size() - 1];
- timer.pop_back();
- }
-
- // Now remove remaining oldest potions...
- repeats = quant - repeats;
- for (int i = 0; i < repeats; i++)
- timer.pop_back();
-
- // ... and re-sort.
- _int_sort(timer);
-}
-
-void merge_blood_potion_stacks(item_def &source, item_def &dest, int quant)
-{
- if (!source.defined() || !dest.defined())
- return;
-
- ASSERT_RANGE(quant, 1, source.quantity + 1);
- ASSERT(is_blood_potion(source));
- ASSERT(is_blood_potion(dest));
-
- CrawlHashTable &props = source.props;
- if (!props.exists("timer"))
- init_stack_blood_potions(source, source.special);
- ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"].get_vector();
- ASSERT(!timer.empty());
-
- CrawlHashTable &props2 = dest.props;
- if (!props2.exists("timer"))
- init_stack_blood_potions(dest, dest.special);
- ASSERT(props2.exists("timer"));
- CrawlVector &timer2 = props2["timer"].get_vector();
-
- // Update timer -> push(pop).
- for (int i = 0; i < quant; i++)
- {
- timer2.push_back(timer[timer.size() - 1].get_int());
- timer.pop_back();
- }
-
- // Re-sort timer.
- _int_sort(timer2);
-}
-
bool check_blood_corpses_on_ground()
{
for (stack_iterator si(you.pos(), true); si; ++si)
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index 34cc6ee106..776c65faa4 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -31,15 +31,6 @@ void turn_corpse_into_chunks(item_def &item, bool bloodspatter = true,
void butcher_corpse(item_def &item, maybe_bool skeleton = MB_MAYBE,
bool chunks = true);
-void init_stack_blood_potions(item_def &stack, int age = -1);
-string get_desc_quantity(const int quant, const int total,
- string whose = "your");
-void maybe_coagulate_blood_potions_floor(int obj);
-void maybe_coagulate_blood_potions_inv(item_def &blood);
-int remove_oldest_blood_potion(item_def &stack);
-void remove_newest_blood_potion(item_def &stack, int quant = -1);
-void merge_blood_potion_stacks(item_def &source, item_def &dest, int quant);
-
bool check_blood_corpses_on_ground();
bool can_bottle_blood_from_corpse(monster_type mons_class);
int num_blood_potions_from_corpse(monster_type mons_class, int chunk_type = -1);
diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc
index 7344e56f2b..588e118004 100644
--- a/crawl-ref/source/mon-act.cc
+++ b/crawl-ref/source/mon-act.cc
@@ -48,6 +48,7 @@
#include "player.h"
#include "random.h"
#include "religion.h"
+#include "rot.h"
#include "shopping.h" // for item values
#include "shout.h"
#include "spl-book.h"
diff --git a/crawl-ref/source/mon-death.cc b/crawl-ref/source/mon-death.cc
index 5862420965..3080ff8e62 100644
--- a/crawl-ref/source/mon-death.cc
+++ b/crawl-ref/source/mon-death.cc
@@ -54,6 +54,7 @@
#include "notes.h"
#include "random.h"
#include "religion.h"
+#include "rot.h"
#include "spl-damage.h"
#include "spl-miscast.h"
#include "spl-summoning.h"
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index 831c28d8f0..a393a65cc0 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -49,6 +49,7 @@
#include "mgen_data.h"
#include "random.h"
#include "religion.h"
+#include "rot.h"
#include "shopping.h"
#include "spl-damage.h"
#include "spl-monench.h"
diff --git a/crawl-ref/source/ng-setup.cc b/crawl-ref/source/ng-setup.cc
index 18c5356d72..796a7fdd74 100644
--- a/crawl-ref/source/ng-setup.cc
+++ b/crawl-ref/source/ng-setup.cc
@@ -15,7 +15,6 @@
#include "item_use.h"
#include "jobs.h"
#include "maps.h"
-#include "misc.h"
#include "mutation.h"
#include "newgame.h"
#include "ng-init.h"
@@ -23,6 +22,7 @@
#include "options.h"
#include "player.h"
#include "religion.h"
+#include "rot.h"
#include "skills.h"
#include "skills2.h"
#include "spl-book.h"
diff --git a/crawl-ref/source/rot.cc b/crawl-ref/source/rot.cc
new file mode 100644
index 0000000000..9b1b717a98
--- /dev/null
+++ b/crawl-ref/source/rot.cc
@@ -0,0 +1,572 @@
+/**
+ * @file
+ * @brief Functions for blood & chunk rot.
+ **/
+
+#include "AppHdr.h"
+
+#include "rot.h"
+
+#include "enum.h"
+#include "env.h"
+#include "itemprop.h"
+#include "items.h"
+#include "libutil.h"
+#include "makeitem.h"
+#include "player.h"
+#include "stuff.h"
+
+
+// Initialise blood potions with a vector of timers.
+void init_stack_blood_potions(item_def &stack, int age)
+{
+ ASSERT(is_blood_potion(stack));
+
+ CrawlHashTable &props = stack.props;
+ props.clear(); // sanity measure
+ props["timer"].new_vector(SV_INT, SFLAG_CONST_TYPE);
+ CrawlVector &timer = props["timer"].get_vector();
+
+ if (age == -1)
+ {
+ if (stack.sub_type == POT_BLOOD)
+ age = 2500;
+ else // coagulated blood
+ age = 500;
+ }
+ // For a newly created stack, all potions use the same timer.
+ const int max_age = you.num_turns + age;
+#ifdef DEBUG_BLOOD_POTIONS
+ mprf(MSGCH_DIAGNOSTICS, "newly created stack will time out at turn %d",
+ max_age);
+#endif
+ for (int i = 0; i < stack.quantity; i++)
+ timer.push_back(max_age);
+
+ stack.special = 0;
+ ASSERT(timer.size() == stack.quantity);
+ props.assert_validity();
+}
+
+// Sort a CrawlVector<int>, should probably be done properly with templates.
+static void _int_sort(CrawlVector &vec)
+{
+ vector<int> help;
+ while (!vec.empty())
+ {
+ help.push_back(vec[vec.size()-1].get_int());
+ vec.pop_back();
+ }
+
+ sort(help.begin(), help.end());
+
+ while (!help.empty())
+ {
+ vec.push_back(help[help.size()-1]);
+ help.pop_back();
+ }
+}
+
+static void _compare_blood_quantity(item_def &stack, int timer_size)
+{
+ if (timer_size != stack.quantity)
+ {
+ mprf(MSGCH_WARN,
+ "ERROR: blood potion quantity (%d) doesn't match timer (%d)",
+ stack.quantity, timer_size);
+
+ // sanity measure
+ stack.quantity = timer_size;
+ }
+}
+
+static void _init_coagulated_blood(item_def &stack, int count, item_def &old,
+ vector<int> &age_timer)
+{
+ stack.base_type = OBJ_POTIONS;
+ stack.sub_type = POT_BLOOD_COAGULATED;
+ stack.quantity = count;
+ stack.plus = 0;
+ stack.plus2 = 0;
+ stack.special = 0;
+ stack.flags = old.flags & (ISFLAG_DROPPED | ISFLAG_THROWN
+ | ISFLAG_NO_PICKUP | ISFLAG_SUMMONED
+ | ISFLAG_DROPPED_BY_ALLY);
+ stack.inscription = old.inscription;
+ item_colour(stack);
+
+ CrawlHashTable &props_new = stack.props;
+ props_new["timer"].new_vector(SV_INT, SFLAG_CONST_TYPE);
+ CrawlVector &timer_new = props_new["timer"].get_vector();
+
+ int val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer_new.push_back(val);
+ }
+ ASSERT(timer_new.size() == count);
+ props_new.assert_validity();
+}
+
+void maybe_coagulate_blood_potions_floor(int obj)
+{
+ item_def &blood = mitm[obj];
+ ASSERT(blood.defined());
+ ASSERT(is_blood_potion(blood));
+
+ CrawlHashTable &props = blood.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(blood, blood.special);
+
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ ASSERT(!timer.empty());
+ _compare_blood_quantity(blood, timer.size());
+
+ // blood.sub_type could be POT_BLOOD or POT_BLOOD_COAGULATED
+ // -> need different handling
+ int rot_limit = you.num_turns;
+ int coag_limit = you.num_turns + 500; // check 500 turns later
+
+ // First count whether coagulating is even necessary.
+ int rot_count = 0;
+ int coag_count = 0;
+ vector<int> age_timer;
+ int current;
+ while (!timer.empty())
+ {
+ current = timer[timer.size()-1].get_int();
+ if (current > coag_limit
+ || blood.sub_type == POT_BLOOD_COAGULATED && current > rot_limit)
+ {
+ // Still some time until rotting/coagulating.
+ break;
+ }
+
+ timer.pop_back();
+ if (current <= rot_limit)
+ rot_count++;
+ else if (blood.sub_type == POT_BLOOD && current <= coag_limit)
+ {
+ coag_count++;
+ age_timer.push_back(current);
+ }
+ }
+
+ if (!rot_count && !coag_count)
+ return; // Nothing to be done.
+
+#ifdef DEBUG_BLOOD_POTIONS
+ mprf(MSGCH_DIAGNOSTICS, "in maybe_coagulate_blood_potions_FLOOR "
+ "(turns: %d)", you.num_turns);
+
+ mprf(MSGCH_DIAGNOSTICS, "Something happened at pos (%d, %d)!",
+ blood.x, blood.y);
+ mprf(MSGCH_DIAGNOSTICS, "coagulated: %d, rotted: %d, total: %d",
+ coag_count, rot_count, blood.quantity);
+ more();
+#endif
+
+ if (!coag_count) // Some potions rotted away.
+ {
+ dec_mitm_item_quantity(obj, rot_count);
+ // Timer is already up to date.
+ return;
+ }
+
+ // Coagulated blood cannot coagulate any further...
+ ASSERT(blood.sub_type == POT_BLOOD);
+
+ if (!blood.held_by_monster())
+ {
+ // Now that coagulating is necessary, check square for
+ // !coagulated blood.
+ ASSERT(blood.pos.x >= 0);
+ ASSERT(blood.pos.y >= 0);
+ for (stack_iterator si(blood.pos); si; ++si)
+ {
+ if (si->base_type == OBJ_POTIONS
+ && si->sub_type == POT_BLOOD_COAGULATED)
+ {
+ // Merge with existing stack.
+ CrawlHashTable &props2 = si->props;
+ if (!props2.exists("timer"))
+ init_stack_blood_potions(*si, si->special);
+
+ ASSERT(props2.exists("timer"));
+ CrawlVector &timer2 = props2["timer"].get_vector();
+ ASSERT(timer2.size() == si->quantity);
+
+ // Update timer -> push(pop).
+ while (!age_timer.empty())
+ {
+ const int val = age_timer.back();
+ age_timer.pop_back();
+ timer2.push_back(val);
+ }
+ _int_sort(timer2);
+ inc_mitm_item_quantity(si.link(), coag_count);
+ ASSERT(timer2.size() == si->quantity);
+ dec_mitm_item_quantity(obj, rot_count + coag_count);
+ return;
+ }
+ }
+ }
+ // If we got here, nothing was found! (Or it's in a monster's
+ // inventory.)
+
+ // Entire stack is gone, rotted or coagulated.
+ // -> Change potions to coagulated type.
+ if (rot_count + coag_count == blood.quantity)
+ {
+ ASSERT(timer.empty());
+
+ // Update subtype.
+ blood.sub_type = POT_BLOOD_COAGULATED;
+ item_colour(blood);
+
+ // Re-fill vector.
+ int val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer.push_back(val);
+ }
+ dec_mitm_item_quantity(obj, rot_count);
+ _compare_blood_quantity(blood, timer.size());
+ return;
+ }
+
+ // Else, create a new stack of potions.
+ int o = get_mitm_slot(20);
+ if (o == NON_ITEM)
+ return;
+
+ item_def &item = mitm[o];
+ _init_coagulated_blood(item, coag_count, blood, age_timer);
+
+ if (blood.held_by_monster())
+ move_item_to_grid(&o, blood.holding_monster()->pos());
+ else
+ move_item_to_grid(&o, blood.pos);
+
+ dec_mitm_item_quantity(obj, rot_count + coag_count);
+ _compare_blood_quantity(blood, timer.size());
+}
+
+// Prints messages for blood potions coagulating or rotting in inventory.
+static void _potion_stack_changed_message(item_def &potion, int num_changed,
+ string verb)
+{
+ ASSERT(num_changed > 0);
+
+ verb = replace_all(verb, "%s", num_changed == 1 ? "s" : "");
+ mprf(MSGCH_ROTTEN_MEAT, "%s %s %s.",
+ get_desc_quantity(num_changed, potion.quantity).c_str(),
+ potion.name(DESC_PLAIN, false).c_str(),
+ verb.c_str());
+}
+
+
+/**
+ * Coagulate and/or rot away blood potions in a stack if necessary.
+ * @param blood The blood potion.
+ */
+void maybe_coagulate_blood_potions_inv(item_def &blood)
+{
+ ASSERT(blood.defined());
+ ASSERT(is_blood_potion(blood));
+
+ CrawlHashTable &props = blood.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(blood, blood.special);
+
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ _compare_blood_quantity(blood, timer.size());
+ ASSERT(!timer.empty());
+
+ // blood.sub_type could be POT_BLOOD or POT_BLOOD_COAGULATED
+ // -> need different handling
+ int rot_limit = you.num_turns;
+ int coag_limit = you.num_turns + 500; // check 500 turns later
+
+ // First count whether coagulating is even necessary.
+ int rot_count = 0;
+ int coag_count = 0;
+ vector<int> age_timer;
+ int current;
+ const int size = timer.size();
+ for (int i = 0; i < size; i++)
+ {
+ current = timer[timer.size()-1].get_int();
+ if (current > coag_limit
+ || blood.sub_type == POT_BLOOD_COAGULATED && current > rot_limit)
+ {
+ // Still some time until rotting/coagulating.
+ break;
+ }
+
+ timer.pop_back();
+ if (current <= rot_limit)
+ rot_count++;
+ else if (blood.sub_type == POT_BLOOD && current <= coag_limit)
+ {
+ coag_count++;
+ age_timer.push_back(current);
+ }
+ }
+
+ if (!rot_count && !coag_count)
+ return; // Nothing to be done.
+
+#ifdef DEBUG_BLOOD_POTIONS
+ mprf(MSGCH_DIAGNOSTICS, "in maybe_coagulate_blood_potions_INV "
+ "(turns: %d)", you.num_turns);
+
+ mprf(MSGCH_DIAGNOSTICS, "coagulated: %d, rotted: %d, total: %d",
+ coag_count, rot_count, blood.quantity);
+ more();
+#endif
+
+ // just in case
+ you.wield_change = true;
+ you.redraw_quiver = true;
+
+ if (!coag_count) // Some potions rotted away, but none coagulated.
+ {
+ // Only coagulated blood can rot.
+ ASSERT(blood.sub_type == POT_BLOOD_COAGULATED);
+ _potion_stack_changed_message(blood, rot_count, "rot%s away");
+ bool destroyed = dec_inv_item_quantity(blood.link, rot_count);
+
+ if (!destroyed)
+ _compare_blood_quantity(blood, timer.size());
+ return;
+ }
+
+ // Coagulated blood cannot coagulate any further...
+ ASSERT(blood.sub_type == POT_BLOOD);
+
+ _potion_stack_changed_message(blood, coag_count, "coagulate%s");
+
+ request_autoinscribe();
+
+ // Now that coagulating is necessary, check inventory for !coagulated blood.
+ for (int m = 0; m < ENDOFPACK; m++)
+ {
+ if (!you.inv[m].defined())
+ continue;
+
+ if (you.inv[m].base_type == OBJ_POTIONS
+ && you.inv[m].sub_type == POT_BLOOD_COAGULATED)
+ {
+ CrawlHashTable &props2 = you.inv[m].props;
+ if (!props2.exists("timer"))
+ init_stack_blood_potions(you.inv[m], you.inv[m].special);
+
+ ASSERT(props2.exists("timer"));
+ CrawlVector &timer2 = props2["timer"].get_vector();
+ if (!dec_inv_item_quantity(blood.link, coag_count + rot_count))
+ _compare_blood_quantity(blood, timer.size());
+
+ // Update timer -> push(pop).
+ int val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer2.push_back(val);
+ }
+
+ you.inv[m].quantity += coag_count;
+ ASSERT(timer2.size() == you.inv[m].quantity);
+
+ // re-sort timer
+ _int_sort(timer2);
+ return;
+ }
+ }
+
+ // If entire stack has coagulated, simply change subtype.
+ if (rot_count + coag_count == blood.quantity)
+ {
+ ASSERT(timer.empty());
+ // Update subtype.
+ blood.sub_type = POT_BLOOD_COAGULATED;
+ item_colour(blood);
+
+ // Re-fill vector.
+ int val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer.push_back(val);
+ }
+ blood.quantity -= rot_count;
+ // Stack still exists because of coag_count.
+ _compare_blood_quantity(blood, timer.size());
+ return;
+ }
+
+ // Else, create new stack in inventory.
+ int freeslot = find_free_slot(blood);
+ if (freeslot >= 0 && freeslot < ENDOFPACK)
+ {
+ item_def &item = you.inv[freeslot];
+ item.clear();
+ item.link = freeslot;
+ item.slot = index_to_letter(item.link);
+ item.pos.set(-1, -1);
+ _init_coagulated_blood(item, coag_count, blood, age_timer);
+
+ blood.quantity -= coag_count + rot_count;
+ _compare_blood_quantity(blood, timer.size());
+ return;
+ }
+
+ mprf("You can't carry %s right now.", coag_count > 1 ? "them" : "it");
+
+ // No space in inventory, check floor.
+ int o = igrd(you.pos());
+ while (o != NON_ITEM)
+ {
+ if (mitm[o].base_type == OBJ_POTIONS
+ && mitm[o].sub_type == POT_BLOOD_COAGULATED)
+ {
+ // Merge with existing stack.
+ CrawlHashTable &props2 = mitm[o].props;
+ if (!props2.exists("timer"))
+ init_stack_blood_potions(mitm[o], mitm[o].special);
+
+ ASSERT(props2.exists("timer"));
+ CrawlVector &timer2 = props2["timer"].get_vector();
+ ASSERT(timer2.size() == mitm[o].quantity);
+
+ // Update timer -> push(pop).
+ int val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer2.push_back(val);
+ }
+ _int_sort(timer2);
+
+ inc_mitm_item_quantity(o, coag_count);
+ ASSERT(timer2.size() == mitm[o].quantity);
+ dec_inv_item_quantity(blood.link, rot_count + coag_count);
+ _compare_blood_quantity(blood, timer.size());
+ return;
+ }
+ o = mitm[o].link;
+ }
+ // If we got here nothing was found!
+
+ // Create a new stack of potions.
+ o = get_mitm_slot();
+ if (o == NON_ITEM)
+ return;
+
+ _init_coagulated_blood(mitm[o], coag_count, blood, age_timer);
+
+ move_item_to_grid(&o, you.pos());
+
+ if (!dec_inv_item_quantity(blood.link, coag_count + rot_count))
+ _compare_blood_quantity(blood, timer.size());
+}
+
+// Removes the oldest timer of a stack of blood potions.
+// Mostly used for (q)uaff and (f)ire.
+int remove_oldest_blood_potion(item_def &stack)
+{
+ ASSERT(stack.defined());
+ ASSERT(is_blood_potion(stack));
+
+ CrawlHashTable &props = stack.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(stack, stack.special);
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ ASSERT(!timer.empty());
+
+ // Assuming already sorted, and first (oldest) potion valid.
+ const int val = timer[timer.size() - 1].get_int();
+ timer.pop_back();
+
+ // The quantity will be decreased elsewhere.
+ return val;
+}
+
+// Used whenever copies of blood potions have to be cleaned up.
+void remove_newest_blood_potion(item_def &stack, int quant)
+{
+ ASSERT(stack.defined());
+ ASSERT(is_blood_potion(stack));
+
+ CrawlHashTable &props = stack.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(stack, stack.special);
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ ASSERT(!timer.empty());
+
+ if (quant == -1)
+ quant = timer.size() - stack.quantity;
+
+ // Overwrite newest potions with oldest ones.
+ int repeats = stack.quantity;
+ if (repeats > quant)
+ repeats = quant;
+
+ for (int i = 0; i < repeats; i++)
+ {
+ timer[i] = timer[timer.size() - 1];
+ timer.pop_back();
+ }
+
+ // Now remove remaining oldest potions...
+ repeats = quant - repeats;
+ for (int i = 0; i < repeats; i++)
+ timer.pop_back();
+
+ // ... and re-sort.
+ _int_sort(timer);
+}
+
+void merge_blood_potion_stacks(item_def &source, item_def &dest, int quant)
+{
+ if (!source.defined() || !dest.defined())
+ return;
+
+ ASSERT_RANGE(quant, 1, source.quantity + 1);
+ ASSERT(is_blood_potion(source));
+ ASSERT(is_blood_potion(dest));
+
+ CrawlHashTable &props = source.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(source, source.special);
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ ASSERT(!timer.empty());
+
+ CrawlHashTable &props2 = dest.props;
+ if (!props2.exists("timer"))
+ init_stack_blood_potions(dest, dest.special);
+ ASSERT(props2.exists("timer"));
+ CrawlVector &timer2 = props2["timer"].get_vector();
+
+ // Update timer -> push(pop).
+ for (int i = 0; i < quant; i++)
+ {
+ timer2.push_back(timer[timer.size() - 1].get_int());
+ timer.pop_back();
+ }
+
+ // Re-sort timer.
+ _int_sort(timer2);
+}
diff --git a/crawl-ref/source/rot.h b/crawl-ref/source/rot.h
new file mode 100644
index 0000000000..bbd83b5ff6
--- /dev/null
+++ b/crawl-ref/source/rot.h
@@ -0,0 +1,20 @@
+/**
+ * @file
+ * @brief Functions for blood & chunk rot.
+ **/
+
+#ifndef ROT_H
+#define ROT_H
+
+#include "externs.h"
+
+void init_stack_blood_potions(item_def &stack, int age = -1);
+string get_desc_quantity(const int quant, const int total,
+ string whose = "your");
+void maybe_coagulate_blood_potions_floor(int obj);
+void maybe_coagulate_blood_potions_inv(item_def &blood);
+int remove_oldest_blood_potion(item_def &stack);
+void remove_newest_blood_potion(item_def &stack, int quant = -1);
+void merge_blood_potion_stacks(item_def &source, item_def &dest, int quant);
+
+#endif
diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc
index 306c64eec6..ab116e2009 100644
--- a/crawl-ref/source/shopping.cc
+++ b/crawl-ref/source/shopping.cc
@@ -28,10 +28,10 @@
#include "libutil.h"
#include "macro.h"
#include "menu.h"
-#include "misc.h"
#include "notes.h"
#include "place.h"
#include "player.h"
+#include "rot.h"
#include "spl-book.h"
#include "stash.h"
#include "state.h"
diff --git a/crawl-ref/source/spl-other.cc b/crawl-ref/source/spl-other.cc
index 03d94bf075..e5c37b8633 100644
--- a/crawl-ref/source/spl-other.cc
+++ b/crawl-ref/source/spl-other.cc
@@ -29,6 +29,7 @@
#include "player-stats.h"
#include "potion.h"
#include "religion.h"
+#include "rot.h"
#include "spl-util.h"
#include "stuff.h"
#include "terrain.h"
diff --git a/crawl-ref/source/throw.cc b/crawl-ref/source/throw.cc
index c4f906ec6c..5e6819bf42 100644
--- a/crawl-ref/source/throw.cc
+++ b/crawl-ref/source/throw.cc
@@ -35,6 +35,7 @@
#include "mutation.h"
#include "options.h"
#include "religion.h"
+#include "rot.h"
#include "shout.h"
#include "skills2.h"
#include "spl-summoning.h"