specifically code for equipping, unequpping, an equipped unrandart doing something every time world_reacts() is called (special wield effects), melee hit effects, and evoking. Left hardcoded outside of art-func.h:
Sword of Cerebov temproarily downgrading the defender's fire resistance.
Staff of Olgreb boosting poison spells, as if it were a staff of poison.
Vampire's Tooth always getting maximal vampiric drain.
Mace of Variablity's initial pluses being chosen at creation time.
Since what used to be special wield effects is now handled very differently, noisy weapons and the lantern of shadows effects are handled with player attributes rather than SPWLD_NOISES and SPWLD_SHADOW.
Unrandarts can now have an elemental colour for their colour (currently only used for the Mace of Variability).
Unrandarts' value modification, being special, and being evil are now handled in art-data.txt rather than being hardcoded.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10055 c06c8d41-db1a-0410-9941-cceddc491573
4QYXUNAH54YJPAHB42Z3PPG7MGWGZ3G6Q5V2GTVOAB4MWECG52IAC
CYXNT6HYWGJE4AIGMJVHY3RT57TEU6YTNHUTQ5MQ42JLWR42G37AC
5TQYGO7IXRZVWFDFLFKMZ4M3WJKR6GAPAZ4ZMGDZK32S3YL3DM2QC
B7RFPXVV7Y7EXQZ5XUPB2WQYHGNZB6ERYKBTRCXENTHKW4D3FIMAC
VWQ4ZX7WOPQFKN7GFLA2U6MZXAPJ4XZ6IHEDFKAOUWZYXALQJIEQC
XSJFXTHRV2FGAWDXWO2QHNGAECLYTLOD6CKTAWUPZQ74DSWXUUXQC
I5MVOMOSRAOJNDYY2VUQHZJ6CSMUF6DXFT6Z5K2I5HELD47U6WAAC
DCZMEKDHQWSQCQYQD6ZXB3XOMWLVLPSPVSBVVMPXMSZF7GO3BVCAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
HIF5ANSHAX2LBOG43PTXWD3TLUAFFAOQ4NFDEBPXF4TGZ3LNU2FAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
LV5VQHTUP5OPVIV6FGDX5EJTHT6UJYU7MDPNYSEWQRK3GTDIJ6MAC
KC7HKOPRUKHHZBJMSUHPELMELYEJ3MW66ZXVZW5S5KUHPMCYEZBAC
BMF6Y2AW2UKQ4J2KKWZOF4MFRMIU4PJG5DT72D4H6ZUV56MXR7SQC
NDKS43E4XVAU3FDY276OF5JW7E5SF6F5EJD2XLV5JLKZ5W6N554QC
PBKRKGKARGZSLVFVDTK5NWDXQD26NHNN67LDSSB75CLEWBR6TLEQC
5SKXI22WN2Y4SWUG634WDB7QXLBNV54RPN5OHAXRI55OFM5YBF5QC
NRIZKLUO26UHNKB4IERXI6ECMD2IJYZACQNIUU3SH6BPLGHAJYVAC
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
3OFG2YD2UOHC6UA7H7LWKOWCNFFSTDOXAA6L3KC335OF5RV3YOYQC
PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
GJRMKM6IUTABRCU7T4MMFLZ3DDNUB6CHMA2DSD3WYKBES3VUI4XQC
3A537UMVZSYIFVKVO3SVK5JRSRLDN3YJKK3RKELHR2D4U6CMKM4QC
2D77G7XIIDVS2RUE33YH2NAPSXNLXKXXN3RP2TKPLWRXTEAVSZ3QC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
FAVME2A2U4OUKN2BTISAG5PCI5Y4BN6YVFKCSQQHHZLEVVVF4LKAC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
LP3U7LC6QK6TCMLAYTRGZ2CDZAHPM6VWDT6NPE5ET4WBBZVHBDXQC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
PH47ROWCEIJEYKD2QANVOVWXRA6PQIU7FHBK2CU6XZDRIOVNFEPQC
5ZPQ6NKBRG2BBJSFKXW6DKAS6TGAHMJEM3VW7MYB4CCN2W7UBUZAC
SHSIZVHSB4RPTLGMFJXUDVHF3FTEZZYCRIH4CBZP4MXOBR3HC4QAC
VIHJ3Z75JN7BXWNOPWV3CWQZEA7K5DK36EKHCYMZC7SOSJYH2XHQC
FE4NKR5J4P53PSRDZPEW6MNHUJNCFSWCE6RWBMXIMQBV3EGCDOWQC
KDRK6FPQK6PNQWCE7CZ2XEGPJL6ZQX4BFNPOIGZEPTI7JV4WG6HQC
SEXTAB43OKE5D5NNPNNPJXWR3CLJVRL7VSLMKDATDRLNX4FI3AFQC
652WD4FIJ7E2WV2M2RSIJXVKZULJHKMRMH7P3DKXLUX6WLEZLY3AC
BFZZ7DFLZM4WNHQOKWDJENZOLMXH3UPHZ437BMISYJ3VSO2Y57WQC
RYT42Z6CED4KV5CCJ45CHZ3DQGLFMDCVH6CSQZNXOILULDG4MXVQC
WN3ZHNL4T7QMQC6JF5DJR6NHLZVOGQVO4OJNEKEM5FZKQ7AVOP4AC
4FQAKUKUO6PCAZ3N4HUR5XL6E4VA5UQUZ3AEDGRBLVY7W2LMWI7QC
TPJYUAKSEZMCCCJANJ5EQ7F67QVTPFEOWBD7WYK33NLRN657Y5VQC
LY7DLLD7IKL6ZQPVROLDJ46XRM5CMAAEBRFDKJ4M53CPC5GFCGSQC
YJ4Q65CCPG2R3O2CFICU6WS27ISYEHQGWIHNOOYPN2TIPKNXVX7QC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
G6WNKWA4GNUGNOH63AKXJQMQS2V4FGJ55CB4P2Z3MPSX3UPMVF6AC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
GACH6PWPGGUBEE7PFEPQMOZKSR7HTQGL2WLGF2AQPJD3FCCSKZNQC
SCXTTP2FDNB2A7F4XXGXSSOEKZQ7ODDGN5YBCTZXGZ22CLCEH3WQC
XRZPPYWPWUOM4SFNI6BHKH2UKJQNLKOV6Y7XIEPEZXE5QYRT26PAC
CCBZCRRBI5MJA7BXOMCIDO7B7OCXWEWYLEG3IE4XFZGLEF36OMYQC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
3ZWALZFSTSIVYXY4BAY6ANGINTDACZC6RSSJTEMQSTSUIE66YOBQC
PCPH4EUFXZQY5SB2R3REGG2HTQ4M7M5AW4TDBLMXBOD247IDCJKQC
XUCCWGMXKPIR34BBCCOI67YHI3RST4STDWSDUZTN4B2CJWXQLQ7AC
LUH6GAJODIQXNPBEHMSUTKH3KA3DYVRCKZ6GJJ4HHNHOIMMUEP6QC
5AZ6MLZVJXRGXNJYLWDQB5RS3A3GKIYS52RA4SOST3PR2FTFNNNQC
PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC
AZEDPVKNGB4BVHN2XBSBQAC3RCG7AWT3TWPH7ATUG43RQADRPNKAC
DHI4FAUHYA3V7I2LKBE74AA672FVDUE2LBOZHTTL3EAE4YJP2ICAC
UKDT3XRY6C6UJ6XW6SNX6KOB4ZUZ676ERJGBTQFJYHXEDQ3P24YQC
7P3DLNFFFSZX43ZJRCWBB3EBTTYPCTDH5KMVXHIZS7JFFMTZNHXAC
HWARR4GI55BIEIJFDE57Y6QOBJ35QP64BJKGEN4T6LR7SKQCFOUQC
XGPPA2X3G3266EVSTXGQ7ZWYOCJ32FEYPEUJV55AI5TGFYHTYTQQC
KT3JMGSH5VTNRV2H5POWZLYNXSIRE5CT2XW3ZID7FNZTXYOZG22QC
FIYBXLWALQINNQTHG2KNDUUTAQAZRDDLXW2XOVSKDKBADJ3XCJ4AC
BRC76FXJE6OUGJRDKMOLJO7V776HLXBCHL5DP4I2EN76CU4FKOGAC
AA3I6WU2SPJUPKRIPGXCLU4EZOV4QELVVZ7EIAW3H73GES5DSFXQC
JPBP7PZSMLAI3DCMQZ3RB5EBDFAXJLZJE5MMUHADIJE5VYEMD5KQC
TONFKA6OUHKQVVN7LP6QR4LHODWBCZQ6V42ITUO6EI4SW642A7PAC
IE3INS4WUXZOBVXB5VWRBYPVPXADD2U2W5H3TBTOYNWJ3EGRSGQQC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
5OYPJO4PYZVSBANUX4PCRISNZJK7JW5HG2OW2O7J7UVBJKKVF2WQC
MIC2SDBOCS63ZDNBQ2VRZMTFOYJL5DUQVR67I7Z3KBJGDBNZJIGAC
KHHAE5ZK7ITEZVMMUKYROKECLE2RU5ZU5OQ4Z4XSRQXE2R65O67AC
U6OTXM3JN7SGPVIGQ5F6NR2I7J5V7KOWFQ7AVNNRQQDNLBEDMYFQC
KXHG3CHEUCZX6UZXAQG6QXPS6IRTIJWGGZWYVAMHWYYTCM4KBUSQC
6CWMT6I76TMTHT7BVOVZJ7ATDMZ3VBKKICIRRZBOSGLHJMDS2DWQC
P3BAFR6LTI7HANQCRLZWJ7LRJYFPFYIG7OVZV4RZM5EAMODRJKZAC
GDISJTTXFDQYMOFLUHIDCYWMU7O2SGXN2YQ3VRUIHU5YE3SUVJ7AC
CQ2PQIN6JJWUDKW46Z6S4ZD5HZYFUEBSCG7LTRYQZXAW5YNUWYJQC
OMTU7OMVWDVAGJCQGQJDZ3YU252T6IM2LPS2ZMPWB7MIXCJK62AQC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
ILADK4YNPQVYLZYDMGGBYN2EBBCIPCDNUCWZH27DQTKOMVUDLVMAC
Y4W5B7TS4CW7OBGFOSUGCFG5GSO2FU7QXMXFWDPWG7AM4CL7VHWQC
YT23TCUEE6A3I7EX6WIZYHCPRYJDE74IZKR5YKPHKOFSYJCKFUCQC
GAUT326KHT3MMZPC7TJUOKGPZ6X624E7Z3IOFZEXYWUCORBCZTOAC
VGKONTCFVUJKMGHHKIUBSMTYHWH7776H5HUJ74XV7NLK3TCGIZPAC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
VF3Y6IKB5WKEMZRPG6KMWXGST6GU5HDKZ26BFYGKAQBDUZ5JE2TQC
HNPSSHGZFQ3E2I6X6VTKZ3WBBM2G25P2D7SIL2SZYKV2CCEA2ADAC
DWWOM6V2Z4RQVT6AKW6OYLSFOUE4W5PTP2SH4AAZE2E4EIH4RLUAC
SN3VSV7G6NF6NLX5E47QJQQFDIPC2LQUUYGZMH6AWBF3FOUHXCBQC
2HEJJUVBKA4IWE27MXPEV5IU26IH7PDT6HIEGOVE7BOWHL4MQ5NQC
H7BW6SEIQ57X6V4BM2RX54W42W6BH5XJBAS6FYCYKLQQG43ZMU4QC
LU56733MPKSGXIYBH4DUPLMRBHL4ZPWTVQMJ4PM4ILWRTSZWIFKAC
LTFWXACXWTSFJNSVDFCMC4IJBHUERV6DNCKIECIMRXG2M2U3TYJAC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
NHDY5NMP7SGSOFT6PQS2PN6YPLUEFRTD3LXNLYFHHL5G27QY65FQC
22ZPAVDZLX4R66AODFVA56HH4T3XU62UUON6LYZG3GGTXCTD3YVAC
PKXXBHS3LWLPZI2QVRX22MSQ4R2626IXRSNHFFYHXYTLJJQU54LQC
}
}
# Fill in function pointers.
my $enum = $artefact->{_ENUM};
my $funcs;
if ($found_funcs{$enum})
{
$funcs = $found_funcs{$enum};
delete($found_funcs{$enum});
}
else
{
$funcs = {};
}
foreach my $func_name ("equip", "unequip", "world_reacts", "melee_effect",
"evoke")
{
my $val;
if ($funcs->{$func_name})
{
$val = "_${enum}_$func_name";
}
else
{
$val = "NULL";
}
my %valid_func = (
equip => 1,
unequip => 1,
world_reacts => 1,
melee_effect => 1,
evoke => 1
);
sub read_funcs
{
unless(open(INPUT, "<art-func.h"))
{
die "Couldn't open art-func.h for reading: $!\n";
}
while(<INPUT>)
{
if (/^static .* _([A-Z_]+)_(\S+)\s*\(/)
{
my $enum = $1;
my $func = $2;
if (!$valid_func{$func})
{
push(@warnings, "Unrecognized func '$func' for artefact " .
"'$enum'");
next;
}
$found_funcs{$enum} ||= {};
my $func_list = $found_funcs{$enum};
$func_list->{$func} = 1;
}
}
close(INPUT);
}
sub read_data
{
unless(open(INPUT, "<art-data.txt"))
{
die "Couldn't open art-data.txt for reading: $!\n";
}
my $prev_line = "";
my $curr_art = {};
while (<INPUT>)
{
chomp;
$line_num++;
# Skip comment-only lines
next if (/^#/);
# Strip comments.
s/#.*//;
# Strip trailing whitspace; leading whitespace indicates the
# continuation of a string field.
s/\s*$//;
if ($_ =~ /^\s*$/)
{
if ($prev_line !~ /^\s*$/)
{
finish_art($curr_art);
push(@all_artefacts, $curr_art);
$curr_art = {};
}
}
else
{
process_line($curr_art, $_);
}
$prev_line = $_;
}
close(INPUT);
if (keys(%$curr_art) > 0)
{
finish_art($curr_art);
push(@all_artefacts, $curr_art);
}
chomp;
$line_num++;
# Skip comment-only lines
next if (/^#/);
# Strip comments.
s/#.*//;
# Strip trailing whitspace; leading whitespace indicates the
# continuation of a string field.
s/\s*$//;
if ($_ =~ /^\s*$/)
{
if ($prev_line !~ /^\s*$/)
{
finish_art($curr_art);
push(@all_artefacts, $curr_art);
$curr_art = {};
}
}
else
my $key;
foreach $key (keys(%found_funcs))
if (is_unrandom_artefact( item ))
{
if (item_ident( item, ISFLAG_KNOW_PROPERTIES ))
{
switch (item.special)
{
case UNRAND_CEREBOV:
valued += 2000;
break;
case UNRAND_ASMODEUS:
valued += 1500;
break;
case UNRAND_ZONGULDROK:
valued += 1250;
break;
case UNRAND_TORMENT:
case UNRAND_SINGING_SWORD:
case UNRAND_DISPATER:
valued += 1200;
break;
case UNRAND_PRUNE:
case UNRAND_TROG:
valued += 1000;
break;
case UNRAND_CURSES:
valued += 800;
break;
case UNRAND_VARIABILITY:
valued += 700;
break;
default:
valued += 1000;
break;
}
break;
}
} // end uniques
enum special_wield_type // you.special_wield
{
SPWLD_NONE, // 0
SPWLD_SING,
SPWLD_TROG,
SPWLD_CURSE,
SPWLD_VARIABLE, // 4
SPWLD_PRUNE, // 5 - implicit in it_use3::special_wielded() {dlb}
SPWLD_TORMENT, // 6
SPWLD_ZONGULDROK,
SPWLD_POWER,
SPWLD_WUCAD_MU, // 9
SPWLD_OLGREB, // 10
SPWLD_SHADOW = 50, // 50
SPWLD_NOISE // further differentiation useless -> removed (jpeg)
};
void use_artefact( unsigned char item_wield_2 );
void use_artefact(item_def &item, bool unmeld = false);
void use_artefact( unsigned char item_wield_2, bool *show_msgs = NULL);
void use_artefact(item_def &item, bool *show_msgs = NULL, bool unmeld = false);
}
}
int item_special_wield_effect(const item_def &item)
{
if (item.base_type != OBJ_WEAPONS || !is_artefact(item))
return (SPWLD_NONE);
int i_eff = SPWPN_NORMAL;
if (is_special_unrandom_artefact( item ))
i_eff = item.special;
else if (is_artefact( item ))
i_eff = artefact_wpn_property(item, ARTP_BRAND);
else
i_eff = item.special;
switch (i_eff)
{
case UNRAND_SINGING_SWORD:
return (SPWLD_SING);
case UNRAND_TROG:
return (SPWLD_TROG);
case UNRAND_CURSES:
return (SPWLD_CURSE);
case UNRAND_VARIABILITY:
return (SPWLD_VARIABLE);
case UNRAND_TORMENT:
return (SPWLD_TORMENT);
case UNRAND_ZONGULDROK:
return (SPWLD_ZONGULDROK);
case UNRAND_POWER:
return (SPWLD_POWER);
case UNRAND_OLGREB:
return (SPWLD_OLGREB);
case UNRAND_WUCAD_MU:
return (SPWLD_WUCAD_MU);
default:
return (SPWLD_NONE);
// Only used for Singing Sword introducing itself
// (could be extended to other talking weapons...)
const std::string old_desc = item.name(DESC_CAP_THE);
const bool was_known = item_type_known(item);
bool known_recurser = false;
break;
case UNRAND_SINGING_SWORD:
if (!was_known)
{
mprf(MSGCH_TALK, "%s says, "
"\"Hi! I'm the Singing Sword!\"",
old_desc.c_str());
}
else
mpr("The Singing Sword hums in delight!", MSGCH_TALK);
break;
case UNRAND_TROG:
mpr("You feel bloodthirsty!");
break;
case UNRAND_CURSES:
mpr("A shiver runs down your spine.");
break;
case UNRAND_PRUNE:
mpr("You feel pruney.");
break;
case UNRAND_TORMENT:
mpr("A terribly searing pain shoots up your arm!");
break;
case UNRAND_ZONGULDROK:
mpr("You sense an extremely unholy aura.");
break;
case UNRAND_POWER:
mpr("You sense an aura of extreme power.");
break;
case UNRAND_OLGREB:
if (player_can_smell())
mpr("You smell chlorine.");
else
mpr("The staff glows slightly green.");
case UNRAND_VAMPIRES_TOOTH:
if (you.is_undead != US_UNDEAD)
{
mpr("You feel a strange hunger, and smell blood in "
"the air...");
}
else
mpr("You feel strangely empty.");
case SPWPN_NORMAL:
break;
case UNRAND_CURSES:
if (!item_cursed(item) && one_chance_in(3))
do_curse_item(item, false);
break;
case UNRAND_WUCAD_MU:
MiscastEffect(&you, WIELD_MISCAST, SPTYP_DIVINATION, 9, 90,
"the Staff of Wucad Mu" );
// Call unrandart equip function first, so that it can modify the
// artefact's properties before they're applied.
if (is_unrandom_artefact( item ))
{
const unrandart_entry *entry = get_unrand_entry(item.special);
if (entry->equip_func)
entry->equip_func(&item, show_msgs, unmeld);
if (entry->world_reacts_func)
{
equipment_type eq = get_item_slot(item.base_type, item.sub_type);
you.unrand_reacts |= (1 << eq);
}
}
item_def& weapon = *you.weapon();
const int old_plus = weapon.plus;
const int old_plus2 = weapon.plus2;
const char old_colour = weapon.colour;
if (silenced(you.pos()) || !one_chance_in(20))
return;
std::string msg;
const item_def* weapon = you.weapon();
case SPWLD_SING:
case SPWLD_NOISE:
const std::string key = weapon->props[ART_NOISE_KEY];
msg = getSpeakString(key);
if (!msg.empty())
{
// "Your Singing Sword" sounds disrespectful
// (as if there could be more than one!)
msg = replace_all(msg, "@Your_weapon@", "@The_weapon@");
msg = replace_all(msg, "@your_weapon@", "@the_weapon@");
}
}
else
if (you.unrand_reacts == SPWLD_SING)
{
msg = getSpeakString("Singing Sword");
if (!msg.empty())
{
// "Your Singing Sword" sounds disrespectful
// (as if there could be more than one!)
msg = replace_all(msg, "@Your_weapon@", "@The_weapon@");
msg = replace_all(msg, "@your_weapon@", "@the_weapon@");
}
}
else // SPWLD_NOISE
{
msg = getSpeakString("noisy weapon");
if (!msg.empty())
{
msg = replace_all(msg, "@Your_weapon@", "Your @weapon@");
msg = replace_all(msg, "@your_weapon@", "your @weapon@");
}
}
// Set appropriate channel (will usually be TALK).
msg_channel_type channel = MSGCH_TALK;
// Set appropriate channel (will usually be TALK).
msg_channel_type channel = MSGCH_TALK;
// Disallow anything with VISUAL in it.
if (!msg.empty() && msg.find("VISUAL") != std::string::npos)
msg = "";
// Disallow anything with VISUAL in it.
if (!msg.empty() && msg.find("VISUAL") != std::string::npos)
msg = "";
if (!msg.empty())
{
std::string param = "";
std::string::size_type pos = msg.find(":");
if (!msg.empty())
{
std::string param = "";
std::string::size_type pos = msg.find(":");
if (pos != std::string::npos)
param = msg.substr(0, pos);
if (!param.empty())
{
bool match = true;
if (param == "DANGER")
channel = MSGCH_DANGER;
else if (param == "WARN")
channel = MSGCH_WARN;
else if (param == "SOUND")
channel = MSGCH_SOUND;
else if (param == "PLAIN")
channel = MSGCH_PLAIN;
else if (param == "SPELL" || param == "ENCHANT")
msg = ""; // disallow these as well, channel stays TALK
else if (param != "TALK")
match = false;
if (param == "DANGER")
channel = MSGCH_DANGER;
else if (param == "WARN")
channel = MSGCH_WARN;
else if (param == "SOUND")
channel = MSGCH_SOUND;
else if (param == "PLAIN")
channel = MSGCH_PLAIN;
else if (param == "SPELL" || param == "ENCHANT")
msg = ""; // disallow these as well, channel stays TALK
else if (param != "TALK")
match = false;
if (match && !msg.empty())
msg = msg.substr(pos + 1);
}
}
if (msg.empty()) // give default noises
{
if (you.unrand_reacts == SPWLD_SING)
msg = "@The_weapon@ sings.";
else
{
channel = MSGCH_SOUND;
msg = "You hear a strange noise.";
}
}
// replace weapon references
msg = replace_all(msg, "@The_weapon@", "The @weapon@");
msg = replace_all(msg, "@the_weapon@", "the @weapon@");
msg = replace_all(msg, "@weapon@", weapon.name(DESC_BASENAME));
// replace references to player name and god
msg = replace_all(msg, "@player_name@", you.your_name);
msg = replace_all(msg, "@player_god@",
you.religion == GOD_NO_GOD ? "atheism"
: god_name(you.religion, coinflip()));
mpr(msg.c_str(), channel);
noisy(25, you.pos());
if (match && !msg.empty())
msg = msg.substr(pos + 1);
case SPWLD_CURSE:
if (one_chance_in(30))
curse_an_item(false);
break;
case SPWLD_VARIABLE:
do_uncurse_item(weapon);
if (x_chance_in_y(2, 5))
weapon.plus += (coinflip() ? +1 : -1);
if (x_chance_in_y(2, 5))
weapon.plus2 += (coinflip() ? +1 : -1);
if (msg.empty()) // give default noises
{
channel = MSGCH_SOUND;
msg = "You hear a strange noise.";
}
if (weapon.plus < -4)
weapon.plus = -4;
else if (weapon.plus > 16)
weapon.plus = 16;
// replace weapon references
if (weapon)
{
msg = replace_all(msg, "@The_weapon@", "The @weapon@");
msg = replace_all(msg, "@the_weapon@", "the @weapon@");
msg = replace_all(msg, "@weapon@", weapon->name(DESC_BASENAME));
}
// replace references to player name and god
msg = replace_all(msg, "@player_name@", you.your_name);
msg = replace_all(msg, "@player_god@",
you.religion == GOD_NO_GOD ? "atheism"
: god_name(you.religion, coinflip()));
case SPWLD_TORMENT:
if (one_chance_in(200))
{
torment(TORMENT_SPWLD, you.pos());
did_god_conduct(DID_UNHOLY, 1);
}
break;
void shadow_lantern_effect()
{
if (x_chance_in_y(player_spec_death() + 1, 8))
{
create_monster(mgen_data(MONS_SHADOW, BEH_FRIENDLY, 2, 0, you.pos(),
MHITYOU));
did_god_conduct(DID_NECROMANCY, 1);
}
}
void unrand_reacts()
{
item_def* weapon = you.weapon();
const int old_plus = weapon ? weapon->plus : 0;
const int old_plus2 = weapon ? weapon->plus2 : 0;
animate_dead(&you, 1 + random2(3), BEH_HOSTILE, MHITYOU);
did_god_conduct(DID_NECROMANCY, 1);
}
break;
case SPWLD_POWER:
weapon.plus = stepdown_value( -4 + (you.hp / 5), 4, 4, 4, 20 );
weapon.plus2 = weapon.plus;
break;
case SPWLD_OLGREB:
// Giving Olgreb's staff a little lift since staves of poison have
// been made better. -- bwr
weapon.plus = you.skills[SK_POISON_MAGIC] / 3;
weapon.plus2 = weapon.plus;
break;
case SPWLD_WUCAD_MU:
weapon.plus = std::min(you.intel - 3, 22);
weapon.plus2 = std::min(you.intel / 2, 13);
break;
item_def& item = you.inv[you.equip[i]];
unrandart_entry* entry = get_unrand_entry(item.special);
case SPWLD_SHADOW:
if (x_chance_in_y(player_spec_death() + 1, 8))
{
create_monster(
mgen_data(MONS_SHADOW, BEH_FRIENDLY, 2, 0, you.pos(), MHITYOU));
did_god_conduct(DID_NECROMANCY, 1);
entry->world_reacts_func(&item);
static bool evoke_sceptre_of_asmodeus()
{
bool rc = true;
if (one_chance_in(21))
rc = false;
else if (one_chance_in(20))
{
// Summon devils, maybe a Fiend.
const monster_type mon = (one_chance_in(4) ? MONS_FIEND :
summon_any_demon(DEMON_COMMON));
const bool good_summon = create_monster(
mgen_data::hostile_at(mon,
you.pos(), 6, 0, true)) != -1;
if (good_summon)
{
if (mon == MONS_FIEND)
mpr("\"Your arrogance condemns you, mortal!\"");
else
mpr("The Sceptre summons one of its servants.");
}
else
mpr("The air shimmers briefly.");
}
else
{
// Cast a destructive spell.
const spell_type spl = static_cast<spell_type>(
random_choose_weighted(114, SPELL_BOLT_OF_FIRE,
57, SPELL_LIGHTNING_BOLT,
57, SPELL_BOLT_OF_DRAINING,
12, SPELL_HELLFIRE,
0));
your_spells(spl, you.skills[SK_EVOCATIONS] * 8, false);
}
return (rc);
}
switch (item.base_type)
const unrandart_entry *entry = is_unrandom_artefact(item)
? get_unrand_entry(item.special) : NULL;
if (entry && entry->evoke_func)
{
ASSERT(item_is_equipped(item));
if (entry->evoke_func(&item, &pract, &did_work, &unevokable))
return (did_work);
}
else switch (item.base_type)
else if (is_unrandom_artefact(item))
{
switch (item.special)
{
case UNRAND_DISPATER:
if (you.duration[DUR_DEATHS_DOOR] || !enough_hp(11, true)
|| !enough_mp(5, true))
{
break;
}
mpr("You feel the staff feeding on your energy!");
dec_hp( 5 + random2avg(19, 2), false, "Staff of Dispater" );
dec_mp( 2 + random2avg(5, 2) );
make_hungry(100, false, true);
power = you.skills[SK_EVOCATIONS] * 8;
your_spells( SPELL_HELLFIRE, power, false );
pract = (coinflip() ? 2 : 1);
did_work = true;
break;
case UNRAND_ASMODEUS:
if (evoke_sceptre_of_asmodeus())
{
make_hungry(200, false, true);
did_work = true;
pract = 1;
}
break;
case UNRAND_OLGREB:
if (!enough_mp( 4, true )
|| you.skills[SK_EVOCATIONS] < random2(6))
{
break;
}
dec_mp(4);
make_hungry(50, false, true);
pract = 1;
did_work = true;
power = 10 + you.skills[SK_EVOCATIONS] * 8;
your_spells( SPELL_OLGREBS_TOXIC_RADIANCE, power, false );
if (x_chance_in_y(you.skills[SK_EVOCATIONS] + 1, 10))
your_spells( SPELL_VENOM_BOLT, power, false );
break;
case UNRAND_WUCAD_MU:
if (you.magic_points == you.max_magic_points
|| you.skills[SK_EVOCATIONS] < random2(25))
{
break;
}
mpr("Magical energy flows into your mind!");
inc_mp( 3 + random2(5) + you.skills[SK_EVOCATIONS] / 3, false );
make_hungry(50, false, true);
pract = 1;
did_work = true;
if (one_chance_in(3))
{
// NH_NEVER prevents "nothing happens" messages.
MiscastEffect( &you, NON_MONSTER, SPTYP_DIVINATION,
random2(9), random2(70),
"the Staff of Wucad Mu", NH_NEVER );
}
break;
default:
unevokable = true;
break;
}
}
if (is_unrandom_artefact( item ))
{
switch (item.special)
{
case UNRAND_SINGING_SWORD:
if (showMsgs)
mpr("The Singing Sword sighs.", MSGCH_TALK);
break;
case UNRAND_TROG:
if (showMsgs)
mpr("You feel less violent.");
break;
case UNRAND_OLGREB:
item.plus = 0;
item.plus2 = 0;
break;
case UNRAND_WUCAD_MU:
item.plus = 0;
item.plus2 = 0;
MiscastEffect( &you, WIELD_MISCAST, SPTYP_DIVINATION, 9, 90,
"the Staff of Wucad Mu" );
break;
default:
break;
}
return (true);
}
you.unrand_reacts = SPWLD_NONE;
you.attribute[ATTR_NOISES] = 0;
if (is_unrandom_artefact( item ))
{
const unrandart_entry *entry = get_unrand_entry(item.special);
if (entry->unequip_func)
entry->unequip_func(&item, show_msgs);
if (entry->world_reacts_func)
{
equipment_type eq = get_item_slot(item.base_type, item.sub_type);
you.unrand_reacts &= ~(1 << eq);
}
}
if (is_unrandom_artefact(item))
{
const unrandart_entry* entry = get_unrand_entry(item.special);
if (entry->evoke_func && item_type_known(item))
{
if (item_is_equipped(item))
return (true);
if (msg)
mpr("That item can only be evoked when wielded.");
return (false);
}
// Unrandart might still be evokable (e.g., reaching)
}
if (is_unrandom_artefact(item))
{
switch (item.special)
{
case UNRAND_ASMODEUS:
case UNRAND_WUCAD_MU:
case UNRAND_DISPATER:
case UNRAND_OLGREB:
if (!wielded)
{
if (msg)
mpr("That item can only be evoked when wielded.");
return (false);
}
return (true);
wpn_skill(SK_UNARMED_COMBAT), hands(HANDS_ONE),
spwld(SPWLD_NONE), hand_half_bonus(false),
art_props(0), attack_verb("bug"), verb_degree(),
wpn_skill(SK_UNARMED_COMBAT), hands(HANDS_ONE), hand_half_bonus(false),
art_props(0), unrand_entry(NULL), attack_verb("bug"), verb_degree(),
case SPWLD_TROG:
if (coinflip())
attacker->go_berserk(false);
break;
case SPWLD_WUCAD_MU:
if (one_chance_in(9))
{
MiscastEffect(attacker, MELEE_MISCAST, SPTYP_DIVINATION,
random2(9), random2(70), "the Staff of Wucad Mu");
}
break;
default:
break;
unrand_entry->melee_effects_func(weapon, attacker, defender, mondied);
return (!defender->alive());
torment(TORMENT_SPWLD, you.pos());
did_god_conduct(DID_UNHOLY, 5);
if (is_holy_item(*weapon))
did_god_conduct(DID_HOLY, 1);
else if (is_demonic(*weapon))
did_god_conduct(DID_UNHOLY, 1);
if (spwld == SPWLD_ZONGULDROK || spwld == SPWLD_CURSE)
did_god_conduct(DID_NECROMANCY, 3);
if (weapon)
{
if (weapon->base_type == OBJ_WEAPONS)
{
if (is_holy_item(*weapon))
did_god_conduct(DID_HOLY, 1);
else if (is_demonic(*weapon))
did_god_conduct(DID_UNHOLY, 1);
}
if (is_unrandom_artefact(*weapon))
{
switch (weapon->special)
{
case UNRAND_ASMODEUS:
case UNRAND_DISPATER:
case UNRAND_CEREBOV:
did_god_conduct(DID_UNHOLY, 3);
break;
default:
break;
}
}
}
object_class_type base_type; // class of ura
int sub_type; // type of ura
int plus; // plus of ura
int plus2; // plus2 of ura
int colour; // colour of ura
object_class_type base_type;
unsigned char sub_type;
short plus;
short plus2;
unsigned char colour; // colour of ura
short value;
unsigned char flags;
void (*equip_func)(item_def* item, bool* show_msgs, bool unmeld);
void (*unequip_func)(const item_def* item, bool* show_msgs);
void (*world_reacts_func)(item_def* item);
void (*melee_effects_func)(item_def* item, actor* attacker,
actor* defender, bool mondied);
bool (*evoke_func)(item_def *item, int* pract, bool* did_work,
bool* unevokable);
/*
* File: art-func.h
* Summary: Functions non-standard unrandarts uses.
* Written by: Matthew Cline
*
* Modified for Crawl Reference by $Author$ on $Date$
*/
/*
* util/art-data.pl scans through this file to grab the functions
* non-standard unrandarts use and put them into the unranddata structs
* in art-data.h, so the function names must have the form of
* _UNRAND_ENUM_func_name() in order to be recognized.
*/
#ifdef ART_FUNC_H
#error "art-func.h included twice!"
#endif
#ifdef ART_DATA_H
#error "art-func.h must be included before art-data.h"
#endif
#define ART_FUNC_H
#include "effects.h" // For Sceptre of Torment tormenting
#include "food.h" // For evokes
#include "monplace.h" // For Sceptre of Asmodeus evoke
#include "monstuff.h" // For Scythe of Curses cursing items
#include "spells3.h" // For Zonguldrok animating dead
#include "spl-cast.h" // For evokes
#include "spl-mis.h" // For Staff of Wucad Mu miscasts
/*******************
* Helper functions.
*******************/
static void _equip_mpr(bool* show_msgs, const char* msg,
msg_channel_type chan = MSGCH_PLAIN)
{
bool def_show = true;
if (show_msgs == NULL)
show_msgs = &def_show;
if (*show_msgs)
mpr(msg, chan);
// Caller shouldn't give any more messages.
*show_msgs = false;
}
/*******************
* Unrand functions.
*******************/
static void _ASMODEUS_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER)
{
did_god_conduct(DID_UNHOLY, 3);
}
}
static bool _evoke_sceptre_of_asmodeus()
{
bool rc = true;
if (one_chance_in(21))
rc = false;
else if (one_chance_in(20))
{
// Summon devils, maybe a Fiend.
const monster_type mon = (one_chance_in(4) ? MONS_FIEND :
summon_any_demon(DEMON_COMMON));
const bool good_summon = create_monster(
mgen_data::hostile_at(mon,
you.pos(), 6, 0, true)) != -1;
if (good_summon)
{
if (mon == MONS_FIEND)
mpr("\"Your arrogance condemns you, mortal!\"");
else
mpr("The Sceptre summons one of its servants.");
}
else
mpr("The air shimmers briefly.");
}
else
{
// Cast a destructive spell.
const spell_type spl = static_cast<spell_type>(
random_choose_weighted(114, SPELL_BOLT_OF_FIRE,
57, SPELL_LIGHTNING_BOLT,
57, SPELL_BOLT_OF_DRAINING,
12, SPELL_HELLFIRE,
0));
your_spells(spl, you.skills[SK_EVOCATIONS] * 8, false);
}
return (rc);
}
static bool _ASMODEUS_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (_evoke_sceptre_of_asmodeus())
{
make_hungry(200, false, true);
*did_work = true;
*pract = 1;
}
return (false);
}
////////////////////////////////////////////////////
// XXX: Defender's resistance to fire being temporarily downgraded is
// hardcoded in melee_attack::fire_res_apply_cerebov_downgrade()
static void _CEREBOV_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER)
{
did_god_conduct(DID_UNHOLY, 3);
}
}
////////////////////////////////////////////////////
static void _CURSES_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "A shiver runs down your spine.");
}
static void _CURSES_world_reacts(item_def *item)
{
if (one_chance_in(30))
curse_an_item(false);
}
static void _CURSES_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER)
{
did_god_conduct(DID_NECROMANCY, 3);
}
}
/////////////////////////////////////////////////////
static void _DISPATER_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER)
{
did_god_conduct(DID_UNHOLY, 3);
}
}
static bool _DISPATER_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (you.duration[DUR_DEATHS_DOOR] || !enough_hp(11, true)
|| !enough_mp(5, true))
{
return (false);
}
mpr("You feel the staff feeding on your energy!");
dec_hp( 5 + random2avg(19, 2), false, "Staff of Dispater" );
dec_mp( 2 + random2avg(5, 2) );
make_hungry(100, false, true);
int power = you.skills[SK_EVOCATIONS] * 8;
your_spells( SPELL_HELLFIRE, power, false );
*pract = (coinflip() ? 2 : 1);
*did_work = true;
return (false);
}
////////////////////////////////////////////////////
// XXX: Staff giving a boost to poison spells is hardocded in
// player_spec_poison()
static void _olgreb_pluses(item_def *item)
{
// Giving Olgreb's staff a little lift since staves of poison have
// been made better. -- bwr
item->plus = you.skills[SK_POISON_MAGIC] / 3;
item->plus2 = item->plus;
}
static void _OLGREB_equip(item_def *item, bool *show_msgs, bool unmeld)
{
if (player_can_smell())
_equip_mpr(show_msgs, "You smell chlorine.");
else
_equip_mpr(show_msgs, "The staff glows a sickly green.");
_olgreb_pluses(item);
}
static void _OLGREB_unequip(const item_def *item, bool *show_msgs)
{
if (player_can_smell())
_equip_mpr(show_msgs, "The smell of chlorine vanishes.");
else
_equip_mpr(show_msgs, "The staff's sickly green glow vanishes.");
}
static void _OLGREB_world_reacts(item_def *item)
{
_olgreb_pluses(item);
}
static bool _OLGREB_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (!enough_mp( 4, true ) || you.skills[SK_EVOCATIONS] < random2(6))
return (false);
dec_mp(4);
make_hungry(50, false, true);
*pract = 1;
*did_work = true;
int power = 10 + you.skills[SK_EVOCATIONS] * 8;
your_spells( SPELL_OLGREBS_TOXIC_RADIANCE, power, false );
if (x_chance_in_y(you.skills[SK_EVOCATIONS] + 1, 10))
your_spells( SPELL_VENOM_BOLT, power, false );
return (false);
}
////////////////////////////////////////////////////
static void _power_pluses(item_def *item)
{
item->plus = stepdown_value( -4 + (you.hp / 5), 4, 4, 4, 20 );
item->plus2 = item->plus;
}
static void _POWER_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You sense an aura of extreme power.");
_power_pluses(item);
}
static void _POWER_world_reacts(item_def *item)
{
_power_pluses(item);
}
////////////////////////////////////////////////////
static void _SINGING_SWORD_equip(item_def *item, bool *show_msgs, bool unmeld)
{
bool def_show = true;
if (show_msgs == NULL)
show_msgs = &def_show;
if (!*show_msgs)
return;
if (item_type_known(*item))
{
mprf(MSGCH_TALK, "%s says, \"Hi! I'm the Singing Sword!\"",
item->name(DESC_CAP_THE).c_str());
}
else
mpr("The Singing Sword hums in delight!", MSGCH_TALK);
*show_msgs = false;
// Make noisy_equipment() use a special speech database key.
item->props[ART_NOISE_KEY] = "Singing Sword";
}
static void _SINGING_SWORD_unequip(const item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "The Singing Sword sighs.", MSGCH_TALK);
}
////////////////////////////////////////////////////
static void _PRUNE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You feel pruney.");
}
////////////////////////////////////////////////////
static void _TORMENT_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "A terribly searing pain shoots up your arm!");
}
static void _TORMENT_world_reacts(item_def *item)
{
if (one_chance_in(200))
{
torment(TORMENT_SPWLD, you.pos());
did_god_conduct(DID_UNHOLY, 1);
}
}
static void _TORMENT_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER && coinflip())
{
torment(TORMENT_SPWLD, you.pos());
did_god_conduct(DID_UNHOLY, 5);
}
}
/////////////////////////////////////////////////////
static void _TROG_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You feel bloodthirsty!");
}
static void _TROG_unequip(const item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "You feel less violent.");
}
static void _TROG_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (coinflip())
attacker->go_berserk(false);
}
////////////////////////////////////////////////////
static void _wucad_miscast(actor* victim, int power,int fail)
{
MiscastEffect(victim, WIELD_MISCAST, SPTYP_DIVINATION, power, fail,
"the Staff of Wucad Mu", NH_NEVER);
}
static void _wucad_pluses(item_def *item)
{
item->plus = std::min(you.intel - 3, 22);
item->plus2 = std::min(you.intel / 2, 13);
}
static void _WUCAD_MU_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_wucad_miscast(&you, 9, 90);
_wucad_pluses(item);
}
static void _WUCAD_MU_unequip(const item_def *item, bool *show_msgs)
{
_wucad_miscast(&you, 9, 90);
}
static void _WUCAD_MU_world_reacts(item_def *item)
{
_wucad_pluses(item);
}
static void _WUCAD_MU_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (one_chance_in(9))
_wucad_miscast(attacker, random2(9), random2(70));
}
static bool _WUCAD_MU_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (you.magic_points == you.max_magic_points
|| you.skills[SK_EVOCATIONS] < random2(25))
{
return (false);
}
mpr("Magical energy flows into your mind!");
inc_mp( 3 + random2(5) + you.skills[SK_EVOCATIONS] / 3, false );
make_hungry(50, false, true);
*pract = 1;
*did_work = true;
if (one_chance_in(3))
_wucad_miscast(&you, random2(9), random2(70));
return (false);
}
///////////////////////////////////////////////////
// XXX: Always getting maximal vampiric drain is harcoded in
// melee_attack::apply_damage_brand()
static void _VAMPIRES_TOOTH_equip(item_def *item, bool *show_msgs, bool unmeld)
{
if (you.is_undead != US_UNDEAD)
{
_equip_mpr(show_msgs,
"You feel a strange hunger, and smell blood in the air...");
}
else
_equip_mpr(show_msgs, "You feel strangely empty.");
// Vampire's Tooth imposes more hunger than a normal vampiric
// weapon. Like other vampiric weapons, the hunger is less for
// vampires than for other species.
if (you.species != SP_VAMPIRE)
artefact_set_property(*item, ARTP_METABOLISM, 3);
else
artefact_set_property(*item, ARTP_METABOLISM, 1);
}
///////////////////////////////////////////////////
// XXX: Pluses at creation time are hardcoded in make_item_unrandart()
static void _VARIABILITY_world_reacts(item_def *item)
{
do_uncurse_item(*item);
if (x_chance_in_y(2, 5))
item->plus += (coinflip() ? +1 : -1);
if (x_chance_in_y(2, 5))
item->plus2 += (coinflip() ? +1 : -1);
if (item->plus < -4)
item->plus = -4;
else if (item->plus > 16)
item->plus = 16;
if (item->plus2 < -4)
item->plus2 = -4;
else if (item->plus2 > 16)
item->plus2 = 16;
}
///////////////////////////////////////////////////
static void _ZONGULDROK_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You sense an extremely unholy aura.");
}
static void _ZONGULDROK_world_reacts(item_def *item)
{
if (one_chance_in(5))
{
animate_dead(&you, 1 + random2(3), BEH_HOSTILE, MHITYOU);
did_god_conduct(DID_NECROMANCY, 1);
}
}
static void _ZONGULDROK_melee_effect(item_def* weapon, actor* attacker,
actor* defender, bool mondied)
{
if (attacker->atype() == ACT_PLAYER)
{
did_god_conduct(DID_NECROMANCY, 3);
}
}
///////////////////////////////////////////////////
if (you.unrand_reacts != SPWLD_NONE)
special_wielded();
if (you.attribute[ATTR_NOISES])
noisy_equipment();
if (you.attribute[ATTR_SHADOWS])
shadow_lantern_effect();