summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
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
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')
-rw-r--r--crawl-ref/source/dat/database/monspeak.txt2
-rw-r--r--crawl-ref/source/debug.cc14
-rw-r--r--crawl-ref/source/describe.cc20
-rw-r--r--crawl-ref/source/effects.cc107
-rw-r--r--crawl-ref/source/item_use.cc17
-rw-r--r--crawl-ref/source/items.cc54
-rw-r--r--crawl-ref/source/libgui.cc19
-rw-r--r--crawl-ref/source/makeitem.cc9
-rw-r--r--crawl-ref/source/misc.cc606
-rw-r--r--crawl-ref/source/misc.h11
-rw-r--r--crawl-ref/source/newgame.cc5
-rw-r--r--crawl-ref/source/player.cc4
-rw-r--r--crawl-ref/source/spells4.cc11
13 files changed, 664 insertions, 215 deletions
diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt
index b67de8e3c9..6680635431 100644
--- a/crawl-ref/source/dat/database/monspeak.txt
+++ b/crawl-ref/source/dat/database/monspeak.txt
@@ -2241,7 +2241,7 @@ orc sorcerer
@_wizard_@
w:1
-@_generic_orc_@
+@_generic_orc_speech@
w:1
__NONE
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index b685550733..0f0509b730 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -1244,12 +1244,14 @@ void create_spec_object()
break;
case OBJ_POTIONS:
- if (mitm[thing_created].sub_type == POT_BLOOD)
- mitm[thing_created].special = 1200;
- else if (mitm[thing_created].sub_type == POT_BLOOD_COAGULATED)
- mitm[thing_created].special = 200;
- // intentional fall-through
-
+ mitm[thing_created].quantity = 12;
+ if (mitm[thing_created].sub_type == POT_BLOOD
+ || mitm[thing_created].sub_type == POT_BLOOD_COAGULATED)
+ {
+ init_stack_blood_potions(mitm[thing_created]);
+ }
+ break;
+
case OBJ_FOOD:
case OBJ_SCROLLS:
mitm[thing_created].quantity = 12;
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 0c2bbdfc24..2f487034f4 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -1645,8 +1645,26 @@ std::string get_item_description( const item_def &item, bool verbose,
description << "$This book is beyond your current level of understanding.";
break;
- case OBJ_SCROLLS:
case OBJ_POTIONS:
+#ifdef DEBUG_BLOOD_POTIONS
+ // list content of timer vector for blood potions
+ if (item.sub_type == POT_BLOOD
+ || item.sub_type == POT_BLOOD_COAGULATED)
+ {
+ item_def stack = static_cast<item_def>(item);
+ CrawlHashTable &props = stack.props;
+ ASSERT(props.exists("timer"));
+ CrawlVector &timer = props["timer"].get_vector();
+ ASSERT(!timer.empty());
+
+ description << "$Quantity: " << stack.quantity
+ << " Timer size: " << (int) timer.size();
+ description << "$Timers:$";
+ for (int i = 0; i < timer.size(); i++)
+ description << (timer[i].get_long()) << " ";
+ }
+#endif
+ case OBJ_SCROLLS:
case OBJ_ORBS:
case OBJ_CORPSES:
case OBJ_GOLD:
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index e050246104..d77f3c5a5d 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1547,12 +1547,11 @@ bool acquirement(object_class_type class_wanted, int agent,
else if (quant > 1)
thing.quantity = quant;
- if (thing.base_type == OBJ_POTIONS)
+ if (thing.base_type == OBJ_POTIONS
+ && (thing.sub_type == POT_BLOOD
+ || thing.sub_type == POT_BLOOD_COAGULATED))
{
- if (thing.sub_type == POT_BLOOD)
- thing.special = 1200;
- else if (thing.sub_type == POT_BLOOD_COAGULATED)
- thing.special = 200;
+ init_stack_blood_potions(thing);
}
// remove curse flag from item
@@ -2213,9 +2212,6 @@ static void rot_inventory_food(long time_delta)
// Update all of the corpses and food chunks in the player's
// inventory {should be moved elsewhere - dlb}
bool burden_changed_by_rot = false;
- int num_total_blood = 0, num_blood_coagulates = 0;
- int affected_potion = -1; // stack of coagulating blood potions
- // (only one possible at a time)
std::vector<char> rotten_items;
for (int i = 0; i < ENDOFPACK; i++)
{
@@ -2225,11 +2221,18 @@ static void rot_inventory_food(long time_delta)
if (!food_item_needs_time_check(you.inv[i]))
continue;
+ if (you.inv[i].base_type == OBJ_POTIONS)
+ {
+ // also handles messaging
+ if (maybe_coagulate_blood_potions_inv(you.inv[i]))
+ burden_changed_by_rot = true;
+ continue;
+ }
+
// food item timed out -> make it disappear
if ((time_delta / 20) >= you.inv[i].special)
{
- if (you.inv[i].base_type == OBJ_FOOD
- || you.inv[i].base_type == OBJ_POTIONS)
+ if (you.inv[i].base_type == OBJ_FOOD)
{
if (you.equip[EQ_WEAPON] == i)
unwield_item();
@@ -2263,18 +2266,8 @@ static void rot_inventory_food(long time_delta)
// if it hasn't disappeared, reduce the rotting timer
you.inv[i].special -= (time_delta / 20);
- if (you.inv[i].base_type == OBJ_POTIONS
- && you.inv[i].sub_type == POT_BLOOD)
- {
- num_total_blood += you.inv[i].quantity;
- if (you.inv[i].special < 200)
- {
- num_blood_coagulates += you.inv[i].quantity;
- affected_potion = i;
- }
- }
- else if (food_is_rotten(you.inv[i])
- && (you.inv[i].special + (time_delta / 20) >= 100 ))
+ if (food_is_rotten(you.inv[i])
+ && (you.inv[i].special + (time_delta / 20) >= 100 ))
{
rotten_items.push_back(index_to_letter( i ));
}
@@ -2339,50 +2332,6 @@ static void rot_inventory_food(long time_delta)
learned_something_new(TUT_ROTTEN_FOOD);
}
- if (num_blood_coagulates)
- {
- ASSERT(affected_potion != -1);
-
- std::string msg = "";
-
- if (num_total_blood == num_blood_coagulates)
- msg += you.inv[affected_potion].name(DESC_CAP_YOUR, false);
-/*
- // this is for later, when part of a stack can coagulate
- else
- {
- if (congealed_blood_num == 1)
- msg += "One of ";
- else
- msg += "Some of ";
-
- msg += you.inv[affected_potion].name(DESC_NOCAP_YOUR, false);
- }
-*/
- msg += " coagulate";
- if (num_blood_coagulates == 1)
- msg += "s";
- msg += ".";
-
- mpr(msg.c_str(), MSGCH_ROTTEN_MEAT);
-
- const bool known_blood = item_type_known(you.inv[affected_potion]);
- you.inv[affected_potion].sub_type = POT_BLOOD_COAGULATED;
- you.inv[affected_potion].plus
- = you.item_description[IDESC_POTIONS][POT_BLOOD_COAGULATED];
- const bool known_coag_blood = item_type_known(you.inv[affected_potion]);
-
- // identify both blood and coagulated blood, if necessary
- if (!known_blood)
- set_ident_type( OBJ_POTIONS, POT_BLOOD, ID_KNOWN_TYPE );
- if (!known_coag_blood)
- {
- set_ident_flags( you.inv[affected_potion], ISFLAG_IDENT_MASK );
- set_ident_type( OBJ_POTIONS, POT_BLOOD_COAGULATED, ID_KNOWN_TYPE );
- mpr(you.inv[affected_potion].name(DESC_INVENTORY, false).c_str());
- }
- }
-
if (burden_changed_by_rot)
{
mpr("Your equipment suddenly weighs less.", MSGCH_ROTTEN_MEAT);
@@ -2879,10 +2828,9 @@ static void _maybe_restart_fountain_flow(const int x, const int y,
const int tries)
{
dungeon_feature_type grid = grd[x][y];
+
if (grid < DNGN_DRY_FOUNTAIN_BLUE || grid > DNGN_DRY_FOUNTAIN_BLOOD)
- {
return;
- }
int t = 0;
while (tries > t++)
@@ -2941,9 +2889,15 @@ void update_corpses(double elapsedTime)
if (!food_item_needs_time_check(it))
continue;
+ if (it.base_type == OBJ_POTIONS)
+ {
+ maybe_coagulate_blood_potions_floor(c);
+ continue;
+ }
+
if (rot_time >= it.special && !is_being_butchered(it))
{
- if (it.base_type == OBJ_FOOD || it.base_type == OBJ_POTIONS)
+ if (it.base_type == OBJ_FOOD)
{
destroy_item(c);
}
@@ -2964,22 +2918,7 @@ void update_corpses(double elapsedTime)
}
else
{
- // potions of blood have a longer rot time
- if (it.base_type != OBJ_POTIONS)
- ASSERT(rot_time < 256);
-
it.special -= rot_time;
-
- if (it.base_type == OBJ_POTIONS
- && it.sub_type == POT_BLOOD
- && it.special < 200)
- {
- ASSERT(rot_time < 1200);
- it.sub_type = POT_BLOOD_COAGULATED;
-
- it.plus
- = you.item_description[IDESC_POTIONS][POT_BLOOD_COAGULATED];
- }
}
}
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 482291666d..ed787ab125 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -1755,6 +1755,17 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
item.quantity = 1;
item.slot = index_to_letter(item.link);
origin_set_unknown(item);
+ if (item.base_type == OBJ_POTIONS
+ && (item.sub_type == POT_BLOOD
+ || item.sub_type == POT_BLOOD_COAGULATED)
+ && you.inv[throw_2].quantity > 1)
+ {
+ // initialize thrown potion with oldest potion in stack
+ long val = remove_oldest_blood_potion(you.inv[throw_2]);
+ val -= you.num_turns;
+ item.props.clear();
+ init_stack_blood_potions(item, val);
+ }
if (you.duration[DUR_CONF])
{
@@ -3497,6 +3508,12 @@ void drink( int slot )
xom_is_stimulated(255);
}
+ if (you.inv[item_slot].sub_type == POT_BLOOD
+ || you.inv[item_slot].sub_type == POT_BLOOD_COAGULATED)
+ {
+ // always drink oldest potion
+ remove_oldest_blood_potion(you.inv[item_slot]);
+ }
dec_inv_item_quantity( item_slot, 1 );
you.turn_is_over = true;
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index f7aa0a111b..06ccc9d944 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -1530,10 +1530,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
&& (mitm[obj].sub_type == POT_BLOOD
|| mitm[obj].sub_type == POT_BLOOD_COAGULATED))
{
- // use average age
- int age = you.inv[m].special * you.inv[m].quantity
- + mitm[obj].special * quant_got;
- you.inv[m].special = age / (you.inv[m].quantity + quant_got);
+ pick_up_blood_potions_stack(mitm[obj], quant_got);
}
inc_inv_item_quantity( m, quant_got );
dec_mitm_item_quantity( obj, quant_got );
@@ -1586,20 +1583,34 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
check_note_item(item);
item.quantity = quant_got;
+ if (mitm[obj].base_type == OBJ_POTIONS
+ && (mitm[obj].sub_type == POT_BLOOD
+ || mitm[obj].sub_type == POT_BLOOD_COAGULATED))
+ {
+ if (quant_got != mitm[obj].quantity)
+ {
+ // remove oldest timers from original stack
+ for (int i = 0; i < quant_got; i++)
+ remove_oldest_blood_potion(mitm[obj]);
+
+ // ... and newest ones from picked up stack
+ remove_newest_blood_potion(item);
+ }
+ }
dec_mitm_item_quantity( obj, quant_got );
you.m_quiver->on_inv_quantity_changed(freeslot, quant_got);
burden_change();
if (!quiet)
mpr( you.inv[freeslot].name(DESC_INVENTORY).c_str() );
-
+
if (Options.tutorial_left)
{
taken_new_item(item.base_type);
if (is_artefact(item) || get_equip_desc( item ) != ISFLAG_NO_DESC)
learned_something_new(TUT_SEEN_RANDART);
}
-
+
if (item.base_type == OBJ_ORBS
&& you.char_direction == GDT_DESCENDING)
{
@@ -1737,17 +1748,16 @@ bool copy_item_to_grid( const item_def &item, int x_plos, int y_plos,
{
if (items_stack( item, mitm[i] ))
{
+ inc_mitm_item_quantity( i, quant_drop );
+
if (item.base_type == OBJ_POTIONS
&& (item.sub_type == POT_BLOOD
|| item.sub_type == POT_BLOOD_COAGULATED))
{
- // calculate average age
- int age = mitm[i].special * mitm[i].quantity
- + item.special * quant_drop;
- mitm[i].special = age / (mitm[i].quantity + quant_drop);
+ item_def help = item;
+ drop_blood_potions_stack(help, quant_drop, x_plos, y_plos);
}
- inc_mitm_item_quantity( i, quant_drop );
-
+
// If the items on the floor already have a nonzero slot,
// leave it as such, otherwise set the slot.
if (mark_dropped && !mitm[i].slot)
@@ -1780,6 +1790,15 @@ bool copy_item_to_grid( const item_def &item, int x_plos, int y_plos,
}
move_item_to_grid( &new_item, x_plos, y_plos );
+ if (item.base_type == OBJ_POTIONS
+ && (item.sub_type == POT_BLOOD
+ || item.sub_type == POT_BLOOD_COAGULATED)
+ && item.quantity != quant_drop) // partial drop only
+ {
+ // since only the oldest potions have been dropped,
+ // remove the newest ones
+ remove_newest_blood_potion(mitm[new_item]);
+ }
return (true);
} // end copy_item_to_grid()
@@ -1893,7 +1912,16 @@ bool drop_item( int item_dropped, int quant_drop, bool try_offer )
}
else if (strstr(you.inv[item_dropped].inscription.c_str(), "=s") != 0)
StashTrack.add_stash();
-
+
+ if (you.inv[item_dropped].base_type == OBJ_POTIONS
+ && (you.inv[item_dropped].sub_type == POT_BLOOD
+ || you.inv[item_dropped].sub_type == POT_BLOOD_COAGULATED)
+ && you.inv[item_dropped].quantity != quant_drop)
+ {
+ // oldest potions have been dropped
+ for (int i = 0; i < quant_drop; i++)
+ remove_oldest_blood_potion(you.inv[item_dropped]);
+ }
dec_inv_item_quantity( item_dropped, quant_drop );
you.turn_is_over = true;
diff --git a/crawl-ref/source/libgui.cc b/crawl-ref/source/libgui.cc
index 73a83f25be..6b005cb167 100644
--- a/crawl-ref/source/libgui.cc
+++ b/crawl-ref/source/libgui.cc
@@ -29,6 +29,7 @@
#include "externs.h"
#include "guic.h"
#include "message.h"
+#include "misc.h"
#include "mon-util.h"
#include "newgame.h"
#include "player.h"
@@ -367,19 +368,19 @@ void GmapUpdate(int x, int y, int what, bool upd_tile)
c = _gmap_to_colour(gmap_col[what & 0xff]);
break;
}
- if (c == Options.tile_monster_col && mgrd[x][y] != NON_MONSTER
- && upd_tile)
+
+ if (c == Options.tile_monster_col && mgrd[x][y] != NON_MONSTER)
{
const int grid = mgrd[x][y];
- if (mons_friendly(&menv[grid]))
+ if (mons_friendly(&menv[grid]) && upd_tile)
c = Options.tile_friendly_col; // colour friendly monsters
- else if (mons_neutral(&menv[grid])
+ else if (mons_neutral(&menv[grid]) && upd_tile
&& Options.tile_neutral_col != Options.tile_monster_col)
{
c = Options.tile_neutral_col; // colour neutral monsters
}
else if (mons_class_flag( menv[grid].type, M_NO_EXP_GAIN ))
- c = Options.tile_plant_col;
+ c = Options.tile_plant_col; // colour zero xp monsters
}
if (c == Options.tile_floor_col || c == Options.tile_item_col)
@@ -1345,8 +1346,12 @@ static int _handle_mouse_motion(int mouse_x, int mouse_y, bool init)
&& item.sub_type != CORPSE_SKELETON
&& !food_is_rotten(item))
{
- // TODO: Differentiate bottle/butcher for vampires.
- desc += EOL "[Shift-L-Click] Chop up (c)";
+ desc += EOL "[Shift-L-Click] ";
+ if (can_bottle_blood_from_corpse( item.plus))
+ desc += "Bottle blood";
+ else
+ desc += "Chop up";
+ desc += " (c)";
if (you.species == SP_VAMPIRE)
desc += EOL "[Shift-R-Click] Drink blood (e)";
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index 586f9b3aa3..efb4afbe4f 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -19,6 +19,7 @@
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
+#include "misc.h"
#include "mon-util.h"
#include "player.h"
#include "randart.h"
@@ -2313,11 +2314,9 @@ static void generate_potion_item(item_def& item, int force_type, int item_level)
}
item.sub_type = stype;
}
-
- if (item.sub_type == POT_BLOOD)
- item.special = 1200;
- else
- item.special = 0;
+
+ if (item.sub_type == POT_BLOOD || item.sub_type == POT_BLOOD_COAGULATED)
+ init_stack_blood_potions(item);
}
static void generate_scroll_item(item_def& item, int force_type,
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
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index f4cffde844..4c588fc42c 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -69,10 +69,13 @@ void trackers_init_new_level(bool transit);
void turn_corpse_into_chunks( item_def &item );
void init_stack_blood_potions( item_def &stack, int age = -1 );
-void maybe_coagulate_blood_potions_floor( item_def &blood );
-void remove_oldest_potion_inv( item_def &stack );
-void drop_blood_potions_stack( int item, int quant );
-void pick_up_blood_potions_stack( int item, int quant );
+void maybe_coagulate_blood_potions_floor( int obj );
+bool maybe_coagulate_blood_potions_inv( item_def &blood );
+long remove_oldest_blood_potion( item_def &stack );
+void remove_newest_blood_potion( item_def &stack, int quant = -1 );
+void drop_blood_potions_stack( item_def &stack, int quant, int x = you.x_pos,
+ int y = you.y_pos );
+void pick_up_blood_potions_stack( item_def &stack, int quant );
bool can_bottle_blood_from_corpse( int mons_type );
void turn_corpse_into_blood_potions ( item_def &item );
diff --git a/crawl-ref/source/newgame.cc b/crawl-ref/source/newgame.cc
index 8f8f841d28..51138dd2a1 100644
--- a/crawl-ref/source/newgame.cc
+++ b/crawl-ref/source/newgame.cc
@@ -86,6 +86,7 @@
#include "macro.h"
#include "makeitem.h"
#include "menu.h"
+#include "misc.h"
#include "place.h"
#include "player.h"
#include "randart.h"
@@ -852,6 +853,7 @@ static void _give_starting_food()
return;
item_def item;
+ item.quantity = 1;
if ( you.species == SP_SPRIGGAN )
{
item.base_type = OBJ_POTIONS;
@@ -861,7 +863,7 @@ static void _give_starting_food()
{
item.base_type = OBJ_POTIONS;
item.sub_type = POT_BLOOD;
- item.special = 1200;
+ init_stack_blood_potions(item);
}
else
{
@@ -874,7 +876,6 @@ static void _give_starting_food()
else
item.sub_type = FOOD_BREAD_RATION;
}
- item.quantity = 1;
const int slot = find_free_slot(item);
you.inv[slot] = item; // will ASSERT if couldn't find free slot
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index e192b705a4..044e59ee2d 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -1243,10 +1243,7 @@ int player_res_fire(bool calc_unid, bool temp)
// species:
if (you.species == SP_MUMMY
- || you.species == SP_VAMPIRE && you.hunger_state <= HS_NEAR_STARVING)
- {
rf--;
- }
// mutations:
rf += you.mutation[MUT_HEAT_RESISTANCE];
@@ -3664,7 +3661,6 @@ void display_char_status()
{
case HS_STARVING:
attrib.push_back("resist poison");
- attrib.push_back("are susceptible to fire");
attrib.push_back("significantly resist cold");
attrib.push_back("strongly resist negative energy");
attrib.push_back("resist torment");
diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc
index cbb771dfaa..d1e5192f24 100644
--- a/crawl-ref/source/spells4.cc
+++ b/crawl-ref/source/spells4.cc
@@ -1702,16 +1702,21 @@ bool cast_evaporate(int pow, bolt& beem, int potion)
fire_beam(beem);
// both old and new code use up a potion:
+ if (you.inv[potion].sub_type == POT_BLOOD
+ || you.inv[potion].sub_type == POT_BLOOD_COAGULATED)
+ {
+ remove_oldest_blood_potion(you.inv[potion]);
+ }
dec_inv_item_quantity( potion, 1 );
return (true);
} // end cast_evaporate()
-// The intent of this spell isn't to produce helpful potions
+// The intent of this spell isn't to produce helpful potions
// for drinking, but rather to provide ammo for the Evaporate
-// spell out of corpses, thus potentially making it useful.
+// spell out of corpses, thus potentially making it useful.
// Producing helpful potions would break game balance here...
-// and producing more than one potion from a corpse, or not
+// and producing more than one potion from a corpse, or not
// using up the corpse might also lead to game balance problems. -- bwr
void cast_fulsome_distillation( int powc )
{