summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/misc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/misc.cc')
-rw-r--r--crawl-ref/source/misc.cc606
1 files changed, 521 insertions, 85 deletions
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index bd124f5d9d..2a64821b6d 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -163,18 +163,32 @@ void turn_corpse_into_chunks( item_def &item )
void init_stack_blood_potions(item_def &stack, int age)
{
ASSERT(stack.base_type == OBJ_POTIONS);
- ASSERT(stack.sub_type == POT_BLOOD);
+ ASSERT(stack.sub_type == POT_BLOOD
+ || stack.sub_type == POT_BLOOD_COAGULATED);
CrawlHashTable &props = stack.props;
+ props.clear(); // sanity measure
props.set_default_flags(SFLAG_CONST_TYPE);
props["timer"].new_vector(SV_LONG);
- CrawlVector &timer = props["timer"];
+ CrawlVector &timer = props["timer"].get_vector();
+ if (age == -1)
+ {
+ if (stack.sub_type == POT_BLOOD)
+ age = 2000;
+ else // coagulated blood
+ age = 500;
+ }
// for a newly created stack, all potions use the same timer
- const long max_age = you.num_turns + (age == -1 ? 1200 : age);
+ const long 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();
}
@@ -184,21 +198,23 @@ static void _long_sort(CrawlVector &vec)
{
std::vector<long> help;
while (!vec.empty())
- help.push_back(vec.pop_back());
+ {
+ help.push_back(vec[vec.size()-1].get_long());
+ vec.pop_back();
+ }
std::sort(help.begin(), help.end());
- long val;
while (!help.empty())
{
- val = help[help.size() - 1];
+ vec.push_back(help[help.size()-1]);
help.pop_back();
- vec.push_back(val);
}
}
-void maybe_coagulate_blood_floor(item_def &blood)
+void maybe_coagulate_blood_potions_floor(int obj)
{
+ item_def &blood = mitm[obj];
ASSERT(is_valid_item(blood));
ASSERT(blood.base_type == OBJ_POTIONS);
@@ -206,46 +222,61 @@ void maybe_coagulate_blood_floor(item_def &blood)
|| blood.sub_type == POT_BLOOD_COAGULATED);
CrawlHashTable &props = blood.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(blood, blood.special);
+
ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"];
- ASSERT(timer.size() == blood.quantity);
+ CrawlVector &timer = props["timer"].get_vector();
ASSERT(!timer.empty());
+ ASSERT(timer.size() == blood.quantity);
// 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 + 200; // check 200 turns later
+ 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;
std::vector<long> age_timer;
long current;
- const int size = timer.size();
- for (int i = 0; i < size; i++)
+ while (!timer.empty())
{
- current = timer.pop_back();
- if (rot_limit >= current)
+ current = timer[timer.size()-1].get_long();
+ 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 && coag_limit >= current)
+ else if (blood.sub_type == POT_BLOOD && current <= coag_limit)
{
coag_count++;
age_timer.push_back(current);
}
- else // still some time until rotting/coagulating
- {
- timer.push_back(current);
- _long_sort(timer);
- break;
- }
}
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(blood.link, rot_count);
+ dec_mitm_item_quantity(obj, rot_count);
// timer is already up to date
return;
}
@@ -254,16 +285,19 @@ void maybe_coagulate_blood_floor(item_def &blood)
ASSERT(blood.sub_type == POT_BLOOD);
// now that coagulating is necessary, check square for !coagulated blood
- int o = igrd[blood.x][blood.y];
- while (o != NON_ITEM)
+ ASSERT(blood.x >= 0 && blood.y >= 0);
+ for (int o = igrd[blood.x][blood.y]; o != NON_ITEM; o = mitm[o].link)
{
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"];
+ CrawlVector &timer2 = props2["timer"].get_vector();
ASSERT(timer2.size() == mitm[o].quantity);
// update timer -> push(pop)
@@ -277,10 +311,9 @@ void maybe_coagulate_blood_floor(item_def &blood)
_long_sort(timer2);
inc_mitm_item_quantity(o, coag_count);
ASSERT(timer2.size() == mitm[o].quantity);
- dec_mitm_item_quantity(blood.link, rot_count + coag_count);
+ dec_mitm_item_quantity(obj, rot_count + coag_count);
return;
}
- o = mitm[o].link;
}
// If we got here nothing was found!
@@ -289,9 +322,11 @@ void maybe_coagulate_blood_floor(item_def &blood)
if (rot_count + coag_count == blood.quantity)
{
ASSERT(timer.empty());
+
// update subtype
blood.sub_type = POT_BLOOD_COAGULATED;
item_colour(blood);
+
// re-fill vector
long val;
while (!age_timer.empty())
@@ -300,14 +335,322 @@ void maybe_coagulate_blood_floor(item_def &blood)
age_timer.pop_back();
timer.push_back(val);
}
- dec_mitm_item_quantity(blood.link, rot_count);
+ dec_mitm_item_quantity(obj, rot_count);
ASSERT(timer.size() == blood.quantity);
+ return;
+ }
+
+ // else, create a new stack of potions
+ int o = get_item_slot( 20 );
+ if (o == NON_ITEM)
+ return;
+
+ item_def &item = mitm[o];
+ item.base_type = OBJ_POTIONS;
+ item.sub_type = POT_BLOOD_COAGULATED;
+ item.quantity = coag_count;
+ item.plus = 0;
+ item.plus2 = 0;
+ item.special = 0;
+ item.flags = 0;
+ item_colour(item);
+
+ CrawlHashTable &props_new = item.props;
+ props_new.set_default_flags(SFLAG_CONST_TYPE);
+ props_new["timer"].new_vector(SV_LONG);
+ CrawlVector &timer_new = props_new["timer"].get_vector();
+
+ long 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() == coag_count);
+ props_new.assert_validity();
+ move_item_to_grid( &o, blood.x, blood.y );
+
+ dec_mitm_item_quantity(obj, rot_count + coag_count);
+ ASSERT(timer.size() == blood.quantity);
+}
+
+static void _coagulating_blood_message(item_def &blood, int num_coagulated)
+{
+ ASSERT(num_coagulated > 0);
+
+ std::string msg;
+ if (blood.quantity == num_coagulated)
+ msg = blood.name(DESC_CAP_YOUR, false);
+ else
+ {
+ if (num_coagulated == 1)
+ msg = "One of ";
+ else
+ msg = "Some of ";
+
+ msg += blood.name(DESC_NOCAP_YOUR, false);
}
+ msg += " coagulate";
+ if (num_coagulated == 1)
+ msg += "s";
+ msg += ".";
+
+ mpr(msg.c_str(), MSGCH_ROTTEN_MEAT);
+}
+
+// returns true if "equipment weighs less" message needed
+// also handles coagulation messages
+bool maybe_coagulate_blood_potions_inv(item_def &blood)
+{
+ ASSERT(is_valid_item(blood));
+ ASSERT(blood.base_type == OBJ_POTIONS);
+
+ ASSERT(blood.sub_type == POT_BLOOD
+ || blood.sub_type == POT_BLOOD_COAGULATED);
+
+ 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.size() == blood.quantity);
+ 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;
+ std::vector<long> age_timer;
+ long current;
+ const int size = timer.size();
+ for (int i = 0; i < size; i++)
+ {
+ current = timer[timer.size()-1].get_long();
+ 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 false; // 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
+
+ if (!coag_count) // some potions rotted away
+ {
+ blood.quantity -= rot_count;
+ if (blood.quantity < 1)
+ destroy_item(blood);
+ else
+ ASSERT(blood.quantity == timer.size());
+
+ return true;
+ }
+
+ // coagulated blood cannot coagulate any further...
+ ASSERT(blood.sub_type == POT_BLOOD);
+
+ bool knew_blood = get_ident_type(OBJ_POTIONS, POT_BLOOD) == ID_KNOWN_TYPE;
+ bool knew_coag = (get_ident_type(OBJ_POTIONS, POT_BLOOD_COAGULATED)
+ == ID_KNOWN_TYPE);
+
+ _coagulating_blood_message(blood, coag_count);
+
+ // identify both blood and coagulated blood, if necessary
+ if (!knew_blood)
+ set_ident_type( OBJ_POTIONS, POT_BLOOD, ID_KNOWN_TYPE );
+
+ if (!knew_coag)
+ set_ident_type( OBJ_POTIONS, POT_BLOOD_COAGULATED, ID_KNOWN_TYPE );
+
+ // now that coagulating is necessary, check inventory for !coagulated blood
+ for (int m = 0; m < ENDOFPACK; m++)
+ {
+ if (!is_valid_item(you.inv[m]))
+ 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();
+
+ blood.quantity -= coag_count + rot_count;
+ if (blood.quantity < 1)
+ destroy_item(blood);
+ else
+ {
+ ASSERT(timer.size() == blood.quantity);
+ if (!knew_blood)
+ mpr(blood.name(DESC_INVENTORY).c_str());
+ }
+
+ // update timer -> push(pop)
+ long 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);
+ if (!knew_coag)
+ mpr(you.inv[m].name(DESC_INVENTORY).c_str());
+
+ // re-sort timer
+ _long_sort(timer2);
+
+ return (rot_count > 0);
+ }
+ }
+
+ // 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
+ long 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
+ ASSERT(timer.size() == blood.quantity);
+
+ if (!knew_coag)
+ mpr(blood.name(DESC_INVENTORY).c_str());
+
+ return (rot_count > 0);
+ }
+
+ // else, create new stack in inventory
+ int freeslot = find_free_slot(blood);
+ if (freeslot >= 0 && freeslot < ENDOFPACK
+ && !is_valid_item(you.inv[freeslot]))
+ {
+ item_def &item = you.inv[freeslot];
+ item.link = freeslot;
+ item.slot = index_to_letter(item.link);
+ item.base_type = OBJ_POTIONS;
+ item.sub_type = POT_BLOOD_COAGULATED;
+ item.quantity = coag_count;
+ item.x = -1;
+ item.y = -1;
+ item.plus = 0;
+ item.plus2 = 0;
+ item.special = 0;
+ item.flags = 0;
+ item_colour(item);
+
+ CrawlHashTable &props_new = item.props;
+ props_new.set_default_flags(SFLAG_CONST_TYPE);
+ props_new["timer"].new_vector(SV_LONG);
+ CrawlVector &timer_new = props_new["timer"].get_vector();
+
+ long 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() == coag_count);
+ props_new.assert_validity();
+
+ blood.quantity -= coag_count + rot_count;
+ ASSERT(timer.size() == blood.quantity);
+
+ if (!knew_blood)
+ mpr(blood.name(DESC_INVENTORY).c_str());
+ if (!knew_coag)
+ mpr(item.name(DESC_INVENTORY).c_str());
+
+ return (rot_count > 0);
+ }
+
+ // no space in inventory, check floor
+ int o = igrd[you.x_pos][you.y_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)
+ long val;
+ while (!age_timer.empty())
+ {
+ val = age_timer[age_timer.size() - 1];
+ age_timer.pop_back();
+ timer2.push_back(val);
+ }
+ _long_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);
+ ASSERT(timer.size() == blood.quantity);
+ if (!knew_blood)
+ mpr(blood.name(DESC_INVENTORY).c_str());
+ return true;
+ }
+ o = mitm[o].link;
+ }
+ // If we got here nothing was found!
+
// create a new stack of potions
o = get_item_slot( 100 + random2(200) );
if (o == NON_ITEM)
- return;
+ return false;
// these values are common to all: {dlb}
mitm[o].base_type = OBJ_POTIONS;
@@ -316,13 +659,13 @@ void maybe_coagulate_blood_floor(item_def &blood)
mitm[o].plus = 0;
mitm[o].plus2 = 0;
mitm[o].special = 0;
- mitm[o].flags = 0;
+ mitm[o].flags = ~(ISFLAG_THROWN | ISFLAG_DROPPED);
item_colour(mitm[o]);
CrawlHashTable &props_new = mitm[o].props;
props_new.set_default_flags(SFLAG_CONST_TYPE);
props_new["timer"].new_vector(SV_LONG);
- CrawlVector &timer_new = props_new["timer"];
+ CrawlVector &timer_new = props_new["timer"].get_vector();
long val;
while (!age_timer.empty())
@@ -334,14 +677,22 @@ void maybe_coagulate_blood_floor(item_def &blood)
ASSERT(timer_new.size() == coag_count);
props_new.assert_validity();
- move_item_to_grid( &o, blood.x, blood.y );
+ move_item_to_grid( &o, you.x_pos, you.y_pos );
- dec_mitm_item_quantity(blood.link, rot_count + coag_count);
- ASSERT(timer.size() == blood.quantity);
+ blood.quantity -= rot_count + coag_count;
+ if (blood.quantity < 1)
+ destroy_item(blood);
+ else
+ {
+ ASSERT(timer.size() == blood.quantity);
+ if (!knew_blood)
+ mpr(blood.name(DESC_INVENTORY).c_str());
+ }
+ return true;
}
-// used for (q)uaff, (f)ire, and Evaporate
-void remove_oldest_potion_inv(item_def &stack)
+// mostly used for (q)uaff, (f)ire, and Evaporate
+long remove_oldest_blood_potion(item_def &stack)
{
ASSERT(is_valid_item(stack));
ASSERT(stack.base_type == OBJ_POTIONS);
@@ -350,56 +701,99 @@ void remove_oldest_potion_inv(item_def &stack)
|| stack.sub_type == POT_BLOOD_COAGULATED);
CrawlHashTable &props = stack.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(stack, stack.special);
ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"];
- ASSERT(timer.size() == stack.quantity);
+ CrawlVector &timer = props["timer"].get_vector();
ASSERT(!timer.empty());
// assuming already sorted, and first (oldest) potion valid
+ const long val = timer[timer.size() - 1].get_long();
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(is_valid_item(stack));
+ ASSERT(stack.base_type == OBJ_POTIONS);
+
+ ASSERT (stack.sub_type == POT_BLOOD
+ || stack.sub_type == POT_BLOOD_COAGULATED);
+
+ 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
+ _long_sort(timer);
}
-// Should be called *after* drop_thing (and only if this returns true)
-// unless the stack has been dropped in its entirety.
-void drop_blood_potions_stack(int item, int quant)
+// Called from copy_item_to_grid.
+// Quantities are set afterwards, so don't ASSERT for those.
+void drop_blood_potions_stack(item_def &stack, int quant, int x, int y)
{
- ASSERT(quant > 0);
- // entire stack was dropped?
- if (!is_valid_item(you.inv[item]))
+ if (!is_valid_item(stack))
return;
- item_def &stack = you.inv[item];
+ ASSERT(quant > 0 && quant <= stack.quantity);
ASSERT(stack.base_type == OBJ_POTIONS);
ASSERT(stack.sub_type == POT_BLOOD
|| stack.sub_type == POT_BLOOD_COAGULATED);
CrawlHashTable &props = stack.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(stack, stack.special);
ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"];
+ CrawlVector &timer = props["timer"].get_vector();
ASSERT(!timer.empty());
// first check whether we can merge with an existing stack on the floor
- int o = igrd[you.x_pos][you.y_pos];
+ int o = igrd[x][y];
while (o != NON_ITEM)
{
if (mitm[o].base_type == OBJ_POTIONS
&& mitm[o].sub_type == stack.sub_type)
{
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"];
+ CrawlVector &timer2 = props2["timer"].get_vector();
// update timer -> push(pop)
for (int i = 0; i < quant; i++)
- timer2.push_back(timer.pop_back());
+ {
+ timer2.push_back(timer[timer.size() - 1].get_long());
+ timer.pop_back();
+ }
- ASSERT(timer2.size() == mitm[o].quantity);
// re-sort timer
_long_sort(timer2);
-
- // now the stack timer should be correct again
- ASSERT(timer.size() == stack.quantity);
return;
}
o = mitm[o].link;
@@ -410,27 +804,25 @@ void drop_blood_potions_stack(int item, int quant)
// have to reduce the timer vector anyway.
while (!timer.empty() && quant-- > 0)
timer.pop_back();
-
- ASSERT(stack.quantity == timer.size());
}
-// Should be called *after* move_item_to_player
-// unless the stack has been picked up in its entirety.
-void pick_up_blood_potions_stack(int item, int quant)
+// Called from move_item_to_player.
+// Quantities are set afterwards, so don't ASSERT for those.
+void pick_up_blood_potions_stack(item_def &stack, int quant)
{
- ASSERT(quant > 0);
- // entire stack was taken?
- if (!is_valid_item(mitm[item]))
+ ASSERT(quant > 0 && quant <= stack.quantity);
+ if (!is_valid_item(stack))
return;
- item_def &stack = mitm[item];
ASSERT(stack.base_type == OBJ_POTIONS);
ASSERT(stack.sub_type == POT_BLOOD
|| stack.sub_type == POT_BLOOD_COAGULATED);
CrawlHashTable &props = stack.props;
+ if (!props.exists("timer"))
+ init_stack_blood_potions(stack, stack.special);
ASSERT(props.exists("timer"));
- CrawlVector &timer = props["timer"];
+ CrawlVector &timer = props["timer"].get_vector();
ASSERT(!timer.empty());
// first check whether we can merge with an existing stack in inventory
@@ -443,25 +835,24 @@ void pick_up_blood_potions_stack(int item, int quant)
&& you.inv[m].sub_type == stack.sub_type)
{
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"];
+ CrawlVector &timer2 = props2["timer"].get_vector();
// update timer -> push(pop)
for (int i = 0; i < quant; i++)
- timer2.push_back(timer.pop_back());
+ {
+ timer2.push_back(timer[timer.size() - 1].get_long());
+ timer.pop_back();
+ }
- ASSERT(timer2.size() == you.inv[m].quantity);
// re-sort timer
_long_sort(timer2);
-
- // now the stack timer should be correct again
- ASSERT(timer.size() == stack.quantity);
return;
}
}
-
// If we got here nothing was found. Huh?
- ASSERT(stack.quantity == timer.size());
}
// Deliberately don't check for rottenness here, so this check
@@ -494,8 +885,8 @@ void turn_corpse_into_blood_potions( item_def &item )
item.base_type = OBJ_POTIONS;
item.sub_type = POT_BLOOD;
- item.colour = RED;
- item.special = (item.special - 80) * 10; // potion's age
+ item_colour(item);
+ item.flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED);
// max. amount is about one third of the max. amount for chunks
const int max_chunks = mons_weight( mons_class ) / 150;
@@ -511,7 +902,10 @@ void turn_corpse_into_blood_potions( item_def &item )
item.quantity = 1;
}
- item.flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED);
+ // initialize timer depending on corpse age
+ // almost rotting: age = 100 --> potion timer = 500 --> will coagulate soon
+ // freshly killed: age = 200 --> potion timer = 2000 --> fresh !blood
+ init_stack_blood_potions(item, (item.special - 100) * 15 + 500);
// happens after the blood has been bottled
if (monster_descriptor(mons_class, MDSC_LEAVES_HIDE) && !one_chance_in(3))
@@ -534,6 +928,23 @@ void split_blood_potions_into_decay( int obj, int amount )
if (amount <= 0)
amount = random2(potion.quantity) + 1;
+ // We're being nice here, and only decay the *oldest* potions.
+ for (int i = 0; i < amount; i++)
+ remove_oldest_blood_potion(potion);
+
+ // try to merge into existing stacks of decayed potions
+ for (int m = 0; m < ENDOFPACK; m++)
+ {
+ if (you.inv[m].base_type == OBJ_POTIONS
+ && you.inv[m].sub_type == POT_DECAY
+ && you.inv[m].colour == potion.colour)
+ {
+ you.inv[obj].quantity -= amount;
+ you.inv[m].quantity += amount;
+ return;
+ }
+ }
+
// if entire stack affected just change subtype
if (amount == potion.quantity)
{
@@ -541,17 +952,42 @@ void split_blood_potions_into_decay( int obj, int amount )
return;
}
- // try to merge into existing stacks of decayed potions
- for (int m = 0; m < ENDOFPACK; m++)
- {
- if (you.inv[m].base_type == OBJ_POTIONS
- && you.inv[m].sub_type == POT_DECAY)
- {
- inc_inv_item_quantity( m, amount );
- dec_inv_item_quantity( obj, amount);
+ // else, create new stack in inventory
+ int freeslot = find_free_slot(you.inv[obj]);
+ if (freeslot >= 0 && freeslot < ENDOFPACK
+ && !is_valid_item(you.inv[freeslot]))
+ {
+ item_def &item = you.inv[freeslot];
+ item.link = freeslot;
+ item.slot = index_to_letter(item.link);
+ item.base_type = OBJ_POTIONS;
+ item.sub_type = POT_DECAY;
+ item.quantity = amount;
+ item.x = -1;
+ item.y = -1;
+ item.plus = 0;
+ item.plus2 = 0;
+ item.special = 0;
+ item.flags = 0;
+
+ you.inv[obj].quantity -= amount;
+ return;
+ }
+ // Okay, inventory is full.
- return;
- }
+ // check whether we can merge with an existing stack on the floor
+ int o = igrd[you.x_pos][you.y_pos];
+ while (o != NON_ITEM)
+ {
+ if (mitm[o].base_type == OBJ_POTIONS
+ && mitm[o].sub_type == POT_DECAY
+ && mitm[o].colour == you.inv[obj].colour)
+ {
+ dec_inv_item_quantity(obj, amount);
+ inc_mitm_item_quantity(o, amount);
+ return;
+ }
+ o = mitm[o].link;
}
// only bother creating a distinct stack of potions