summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/misc.cc
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-04-14 16:33:14 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-04-14 16:33:14 +0000
commitbbbd701b88b0274d7f14d0957828ad4342648135 (patch)
treede63191fc5e12155f5260708ca2dd0fc3a9d15fa /crawl-ref/source/misc.cc
parent94ceb87517474ac372a41a52a3448104f51435bf (diff)
downloadcrawl-ref-bbbd701b88b0274d7f14d0957828ad4342648135.tar.gz
crawl-ref-bbbd701b88b0274d7f14d0957828ad4342648135.zip
Overhaul blood potions to work completely differently.
Instead of storing age in item.special they now use a dynamic vector (item.props, like decks do), so that a stack of potions doesn't have to coagulate all at once. Rather than counting down the timers every 20 turns or so, the time-out turn is calculated at creation, and comparison is done against the current turncount. Any action changing a stack (quaffing, firing, Evaporate, picking up, dropping) will always extract the oldest values from the vector, which is likely to be what the player wants. Blood potions now last about 2000 turns (a bit more if drawn from fresh corpses), and coagulate 500 turns before rotting away. I ran a lot of tests in wiz mode and out, but of course there may still be problems. I've added methods to calculate the new timers from old style age counters (item.special), but I'm not sure that they actually work... Oh well... if worst comes to worst, this commit breaks saves. Also: * vampires are not susceptible to fire anymore when Bloodless * make tile_plant_colour also apply for remembered plants out of LOS * fix 1941759: buggy orc dialogue git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@4228 c06c8d41-db1a-0410-9941-cceddc491573
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