1.) Vampires can only suck blood from creatures with M_COLD_BLOOD or M_WARM_BLOOD set (no insects anymore!)
These restrictions are the same for blood spatter.
Also, the monster's corpse must be of type contaminated (e.g. gnoll)
or clean. Everything will be rejected.
2.) At xl 6 vampires gain the ability to create potions of blood from butchered corpses. The delay is the same as for butchering, and they also need a butchering tool (since I now think fangs don't really cut it); only the messaging is different, and the result, of course. If the monster doesn't meet the restrictions in (1), the corpse gets butchered instead (important when there are necromancers roaming about) and the resulting chunks don't get the THROWN flag set because vampires are unlikely to want to pick them up.
3.) Potions of blood can turn bad.
They last about 1200 turns which is a rather long time, though of course
not infinite. Potions created from corpses take the corpse age into
account. From age 199 downwards potions of blood are described as
"congealed blood". The effect when quaffing is entirely the same, it's
just a warning that the potion will soon disappear.
I've moved the potion descriptions over into item.plus, so that I could
use item.special for the timer to allow for easy comparison in
update_corpses() etc.
Ideally each stack of potions of blood would have a props vector
attached (similarly to decks) with the timeout turns stored in order
oldest to newest, so that you'd always drink the oldest potion first, and
if a potion was too old it (and it's time value) would just drop out of
the stack.
Since I haven't got this to work yet, instead the weighted average age
of two substacks is calculated and used for the combined stack.
Congealed (age < 200) and comparatively fresh (age >= 200) potions of
blood will never stack.
As suggested in FR 191314, !blood are a now an additional power source for Sublimation of Blood, and the used potion turns into decay. And speaking of decay, I've modified the mummy curse to only affect a substack if the to-be-decayed potions are blood because I think losing your food source that way would be the equivalent of spores destroying all your food at the same time.
I think that's it; might still be buggy though I did test with some vampires, both wiz-mode and not, and because of the special -> plus change for potions, existing potions will now all look alike (clear potions).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3626 c06c8d41-db1a-0410-9941-cceddc491573
Q3XHNSHW6FI4JCXSEABATRFBJPMCF7PXNG2K6ZQTRRPVMIZFHUBQC
67WH6I2XIMRRL26UM4FJJJZEMJIDPW5Q77FFNZGKWGTWS7GTCJ6QC
FTBMSUX7T2GQECB4MESOLHGIWSKJADXCAKU64H7S33N2D4ABU4HQC
TRCCLE5RJ4VJULBOWOI2WC3RJU7WTEGS7RRQTDQL6W5UU246LKTQC
ACDPN464TK2LKLHSDN3YVRHAPF7WLSLLU3UHIYEXKFDEZPEU3XSQC
E3X5HVN5UN75OMTJA6JFQBNZ54P37NDZLZZF7EFBZZC45KR73YGAC
F7X6HVUKHZXYURABYAZJHRYBV7UZTIPOWJMGCMDK26FQ66WGKFZAC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
P2ZCF3BBG523ZEOD6XQA4X5YEHBTWH3IM33YVHXP2SQ5POXZIH4QC
6LT6USGJOTDMRJGXLAN2NSZXK2GKWEXDKKUV6SVV7ZC6WI6EKMDQC
5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC
FLDIOEND63BONSOAIZ7WYAVNQLJ35O3VFY3PTVRWWYOJL4JXREHAC
MRM4SLSXJTRYJPH2YYTAFTO2CAOXLP2OCMRNYRTIRRYOMWM7NMFAC
OPNCHI4UGN7WBIYPAXVV2C4N22ZSWROA435FJCY5UZVXKWRYQ42QC
X76YXE6RFL7QY5WL6MSSS44WHVA5FQMDQY3XTYP2UL6UZB4E6XMQC
MJWFTUS66PTCNEYXEJA3CUJFXNWXIKDD6H3V24PW7HK64NSVOFSAC
TFNFP2YQA4EOVE4VIXBEQSGACZSXHWIQ2T4TIPQ46R2MJW2C4B5AC
PD4F7SBVT5V3JMWCNW3J4XL2G4FJCQ3OJIXX7YMTL4HHPETDQA3AC
VNHFP63ZLLZU3A3PLXP4BITX57DUIYDHFOHQYK3BOBHV3S64G26QC
EIFQHCRHD4FFW7KO7UKHI7OQV2O7PENRYEXAW6R24UVLNHQYUGKQC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
Q3DNEB5OOJ34P5ML4CMK3L6SCP7RLW7DDOZEG24KZBX3C7BJRQDAC
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
5FECCMAC5QKG3SJ4ATZFE2QF7I3A4YKWKCU4XQEW2P2SWZ2J33YQC
Y2ZJISJRUNYX23QPPLIKKUIM7XJFNSDE5GHUE6DCGZ4RCGFNZBJAC
ASCTVJSN3NXYQHRVXAORA43CV6H5V2572IMK4UGRHKBAGJOWHC4AC
THE3KMFFWY4ZUFE3Z6RVTGPSPENYR7ULZDXPQYB3FXWPJOJ5P7WQC
BWAQ3FHBBM6G3K3KYP75CRTR343RDQZJRYX5ZGYUEXYBAC3APDLAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
7Q4H6B62UZACQOUDHCHPMPBYEBXM5GVQINIHVHM4KLRENSH6VGTAC
5TG5LXU4DX65KMWCZ7YJHOB3VAETQAVBUHEUSQTPMA327XV2HQWAC
VXWHZPSSJAOUIBJVCYGYOHILZFVDDVKUY5JMFFCQSYCXL3NAZIKAC
CWTKS62IFZYYR3HGPDP5LW5C53CRWVAD6AAEOV4BSWNW52BCQH3QC
JXOE7KJIEAJHPLMZAJ6O4YHDTRB6BGNP6XT3ZSEFYGSIUMHR77KQC
KBTDCQ7GYBRPB346ZEOJ4OM2XHOHN6FJMVMTOJ42EDK5KJX7VLIAC
DMRXDEKHHBQNY37NPGZFAGUQPALWFANGGK4CUWIOQUPSLM2JBNFQC
2W34FMSGJ2BZY7QQM6X6RTVRXXI2H72Z2MH75SU3SDL4FN4G74KAC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
YMC3RKF4Z7DOHZNGG7INC343SXCTWOPK7ISD42I3WA3AZZNVBGIAC
PSLBTBSCSC65MRX6TEGGFNKPXLP4OE2FZYGMM6VRJTBXTTGMP5UQC
5R4WV4H5SNIM5WU2X33JJ63HIEGKCXN2HELZ6FRRKKANPLMRLF3QC
UVJBHQ5KGUIEFRHKTYQPKSXYDUDKTRAXINUXGJJJ6QGNCR6JO7ZAC
6GSGCC5JQJ5NOKX36UHRNOCXNHDBS2A2TDMAR34UBOGWE2DORXIQC
2H32CFFM2FNS63JJPNM2S6HMO543EX72GMPOU5GI6HTMQYPL6I3AC
CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC
XX3TYGTDZY7AT53JJDTMKQXOYL4TUPVXXE2WY53UKZZOZ4UXDTQQC
I2B33Z7NZGC33AMDSSK446AZZYWKPHWLAGULVHKKZU4MVB4BNJOAC
MBBPLL4SZUB3JUUYQYLZW7S5OXRCEGJX3WWADOQXGHWQ7BIKCY5QC
if (you.equip[EQ_WEAPON] == -1
|| you.inv[you.equip[EQ_WEAPON]].base_type != OBJ_FOOD
|| you.inv[you.equip[EQ_WEAPON]].sub_type != FOOD_CHUNK)
int wielded = you.equip[EQ_WEAPON];
if (wielded != -1)
{
if (you.inv[wielded].base_type == OBJ_FOOD
&& you.inv[wielded].sub_type == FOOD_CHUNK)
{
mpr("The chunk of flesh you are holding crumbles to dust.");
mpr("A flood of magical energy pours into your mind!");
inc_mp( 7 + random2(7), false );
dec_inv_item_quantity( wielded, 1 );
}
else if (you.inv[wielded].base_type == OBJ_POTIONS
&& you.inv[wielded].sub_type == POT_BLOOD)
{
mprf("The blood within %s frothes and boils.",
you.inv[wielded].quantity == 1 ? "the flask you are holding"
: "one of your flasks");
split_blood_potions_into_decay( wielded, 1 );
mpr("A flood of magical energy pours into your mind!");
inc_mp( 7 + random2(7), false );
}
else // no appropriate item wielded
wielded = -1;
}
if (wielded == -1)
// Xom is amused by useful potions being ruined.
if (item_value(you.inv[item], true) / you.inv[item].quantity > 2)
xom_is_stimulated(32 * you.inv[item].quantity);
// Potions of blood are vital to vampires, so make an exception for
// for them. (Come to think of it, this would work nicely for all
// other potion types as well.)
if (you.inv[item].sub_type == POT_BLOOD)
{
int amount = random2(you.inv[item].quantity) + 1;
split_blood_potions_into_decay(item, amount);
// Xom is amused if this happens to thirsty vampires
if (you.species == SP_VAMPIRE && you.hunger_state <= HS_HUNGRY)
xom_is_stimulated(32 * amount);
}
else
{
// Xom is amused by useful potions being ruined.
if (item_value(you.inv[item], true) / you.inv[item].quantity > 2)
xom_is_stimulated(32 * you.inv[item].quantity);
you.inv[item].sub_type = POT_DECAY;
unset_ident_flags( you.inv[item], ISFLAG_IDENT_MASK ); // all different
you.inv[item].sub_type = POT_DECAY;
unset_ident_flags( you.inv[item], ISFLAG_IDENT_MASK ); // all different
}
// void place_chunks(int mcls, unsigned char rot_status, unsigned char chx,
// unsigned char chy, unsigned char ch_col)
// these values are common to all: {dlb}
mitm[o].base_type = OBJ_ARMOUR;
mitm[o].plus = 0;
mitm[o].plus2 = 0;
mitm[o].special = 0;
mitm[o].flags = 0;
mitm[o].colour = mons_class_colour( mons_class );
// these values cannot be set by a reasonable formula: {dlb}
switch (mons_class)
{
case MONS_DRAGON:
mitm[o].sub_type = ARM_DRAGON_HIDE;
break;
case MONS_TROLL:
mitm[o].sub_type = ARM_TROLL_HIDE;
break;
case MONS_ICE_DRAGON:
mitm[o].sub_type = ARM_ICE_DRAGON_HIDE;
break;
case MONS_STEAM_DRAGON:
mitm[o].sub_type = ARM_STEAM_DRAGON_HIDE;
break;
case MONS_MOTTLED_DRAGON:
mitm[o].sub_type = ARM_MOTTLED_DRAGON_HIDE;
break;
case MONS_STORM_DRAGON:
mitm[o].sub_type = ARM_STORM_DRAGON_HIDE;
break;
case MONS_GOLDEN_DRAGON:
mitm[o].sub_type = ARM_GOLD_DRAGON_HIDE;
break;
case MONS_SWAMP_DRAGON:
mitm[o].sub_type = ARM_SWAMP_DRAGON_HIDE;
break;
case MONS_SHEEP:
case MONS_YAK:
default:
mitm[o].sub_type = ARM_ANIMAL_SKIN;
break;
}
move_item_to_grid( &o, you.x_pos, you.y_pos );
}
// these values are common to all: {dlb}
mitm[o].base_type = OBJ_ARMOUR;
mitm[o].plus = 0;
mitm[o].plus2 = 0;
mitm[o].special = 0;
mitm[o].flags = 0;
mitm[o].colour = mons_class_colour( mons_class );
const int mons_class = item.plus;
ASSERT( can_bottle_blood_from_corpse(mons_class) );
item.base_type = OBJ_POTIONS;
item.sub_type = POT_BLOOD;
item.colour = RED;
item.special = (item.special - 80) * 10; // potion's age
// these values cannot be set by a reasonable formula: {dlb}
switch (mons_class)
// max. amount is about one third of the max. amount for chunks
const int max_chunks = mons_weight( mons_class ) / 150;
item.quantity = 1 + random2( max_chunks/3 );
item.quantity = stepdown_value( item.quantity, 2, 2, 6, 6 );
item.flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED);
// happens after the blood has been bottled
if (monster_descriptor(mons_class, MDSC_LEAVES_HIDE) && !one_chance_in(3))
create_monster_hide(mons_class);
}
// A variation of the mummy curse: for potions of blood (vital for Vampires!)
// split the stack and only turn part of it into POT_DECAY.
void split_blood_potions_into_decay( int obj, int amount )
{
ASSERT(obj != -1);
item_def potion = you.inv[obj];
ASSERT(is_valid_item(potion));
ASSERT(potion.base_type == OBJ_POTIONS && potion.sub_type == POT_BLOOD);
ASSERT(amount <= potion.quantity);
if (amount <= 0)
amount = random2(potion.quantity) + 1;
// if entire stack affected just change subtype
if (amount == potion.quantity)
{
you.inv[obj].sub_type = POT_DECAY;
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);
return;
}
}
// only bother creating a distinct stack of potions
// if it won't get destroyed right away
if (!grid_destroys_items(grd[you.x_pos][you.y_pos]))
{
item_def potion2;
potion2.base_type = OBJ_POTIONS;
potion2.sub_type = POT_DECAY;
potion2.plus = potion.plus; // are these even needed?
potion2.plus2 = potion.plus2;
potion2.flags = potion.flags;
potion2.quantity = amount;
potion2.special = 0;
switch (random2(4))
case MONS_STORM_DRAGON:
mitm[o].sub_type = ARM_STORM_DRAGON_HIDE;
break;
case MONS_GOLDEN_DRAGON:
mitm[o].sub_type = ARM_GOLD_DRAGON_HIDE;
break;
case MONS_SWAMP_DRAGON:
mitm[o].sub_type = ARM_SWAMP_DRAGON_HIDE;
break;
case MONS_SHEEP:
case MONS_YAK:
mitm[o].sub_type = ARM_ANIMAL_SKIN;
break;
default:
// future implementation {dlb}
mitm[o].sub_type = ARM_ANIMAL_SKIN;
break;
move_item_to_grid( &o, item.x, item.y );
}
} // end place_chunks()
copy_item_to_grid( potion2, you.x_pos, you.y_pos );
}
// is decreased even if the decay stack goes splat
dec_inv_item_quantity(obj, amount);
}
// Thanks to mummy cursing, we can have potions of decay
// that don't look alike... so we don't stack potions
// if either isn't identified and they look different. -- bwr
if (item1.base_type == OBJ_POTIONS && item1.special != item2.special &&
(!item_type_known(item1) || !item_type_known(item2)))
if (item1.base_type == OBJ_POTIONS)
return false;
// Thanks to mummy cursing, we can have potions of decay
// that don't look alike... so we don't stack potions
// if either isn't identified and they look different. -- bwr
if (item1.plus != item2.plus
&& (!item_type_known(item1) || !item_type_known(item2)))
{
return false;
}
// Don't stack congealed potions of blood with non-congealed ones.
if (item1.sub_type == POT_BLOOD
&& (item1.special < 200 && item2.special >= 200
|| item2.special < 200 && item1.special >= 200))
{
return false;
}
bool teeth_butcher = (you.mutation[MUT_FANGS] == 3);
// Vampires fangs are optimised for biting, not for tearing flesh.
// Other species with this mutation still might benefit from this.
bool teeth_butcher = (you.mutation[MUT_FANGS] == 3
&& you.species != SP_VAMPIRE);
if (you.inv[i].special < 100 &&
(you.inv[i].special + (time_delta / 20) >=100 ))
if (you.inv[i].base_type == OBJ_POTIONS)
{
blood_num += you.inv[i].quantity;
if (you.inv[i].special < 200
&& you.inv[i].special + (time_delta / 20) >= 200)
{
congealed_blood_num += you.inv[i].quantity;
}
}
else if (food_is_rotten(you.inv[i])
&& (you.inv[i].special + (time_delta / 20) >= 100 ))
if (congealed_blood_num)
{
std::string msg = "";
if (blood_num == 1)
mpr("Your potion of blood congeals.", MSGCH_ROTTEN_MEAT);
else if (congealed_blood_num == 1)
mpr("One of your potions of blood congeals.", MSGCH_ROTTEN_MEAT);
else if (congealed_blood_num < blood_num)
mpr("Some of your potions of blood congeal.", MSGCH_ROTTEN_MEAT);
else
mpr("Your potions of blood congeal.", MSGCH_ROTTEN_MEAT);
}
mprf("You finish %s the corpse into pieces.",
(you.has_usable_claws() || you.mutation[MUT_FANGS] == 3) ?
"ripping" : "chopping");
if (can_bottle_blood_from_corpse(item.plus))
{
turn_corpse_into_blood_potions( mitm[ delay.parm1 ] );
}
else
{
mprf("You finish %s the corpse into pieces.",
(you.has_usable_claws() || you.mutation[MUT_FANGS] == 3) ?
"ripping" : "chopping");
if (is_good_god(you.religion) && is_player_same_species(item.plus))
simple_god_message(" expects more respect for your departed "
"relatives.");
else if (you.religion == GOD_ZIN && mons_intel(item.plus) >= I_NORMAL)
simple_god_message(" expects more respect for this departed "
"soul.");
if (is_good_god(you.religion) && is_player_same_species(item.plus))
simple_god_message(" expects more respect for your departed "
"relatives.");
else if (you.religion == GOD_ZIN && mons_intel(item.plus) >= I_NORMAL)
simple_god_message(" expects more respect for this departed "
"soul.");
if (you.species == SP_VAMPIRE &&
mons_corpse_effect(item.plus) != CE_HCL &&
(!god_likes_butchery(you.religion) ||
!you.duration[DUR_PRAYER]))
{
mpr("What a waste.");
}
turn_corpse_into_chunks( mitm[ delay.parm1 ] );
if (you.species == SP_VAMPIRE && you.experience_level < 6
&& mons_has_blood(item.plus)
&& (!god_likes_butchery(you.religion)
|| !you.duration[DUR_PRAYER]))
{
mpr("What a waste.");
}
turn_corpse_into_chunks( mitm[ delay.parm1 ] );
if (you.duration[DUR_BERSERKER] &&
you.berserk_penalty != NO_BERSERK_PENALTY)
{
mpr("You enjoyed that.");
you.berserk_penalty = 0;
if (you.duration[DUR_BERSERKER] &&
you.berserk_penalty != NO_BERSERK_PENALTY)
{
mpr("You enjoyed that.");
you.berserk_penalty = 0;
}