a random type (including enchantments, which isn't so good since they don't have a visible beam). Might want to add BEAM_CHAOS and make it a beam of that.
Weapons of chaos either does a random brand effect (fire, poison, etc) or a random chaos effect (which includes cloning the attack victim).
The AF_CHAOS monster attack flavour either does a random monster flavour or chaos effect (same chaos effects as for weapons).
The relative frequency of all the different effects/brands/flavours no doubt needs adjustment.
All of this is currently only available via Xom. 10% of all common-type demons sent in by Xom will be chaos spawn (the only kind that use AF_CHAOS, and never randomly generated otherwise). All item gifts from Xom which are generated with a brand will have their brand switched to chaos (this should probably be made to happen less than 100% of the time). And finally one of Xom's bad acts is to upgrade a non-branded weapon of a nearby hostile monster to a chaos brand (this might need to be made less (or more) common).
Oh, and if a randart has a brand which would be identified if it were merely an ego weapon, it now identifies the RAP_BRAND property of the randart.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7704 c06c8d41-db1a-0410-9941-cceddc491573
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
RCFS63VUDBLUMOF2T4OS6OFHBA2LEGCKX7KHMUWTFJNMVASDGWAAC
64HB7VYSYHQEN5UP7OYJ5GSVA2XMDSLLH647UPWE5NSFF3AVZSSQC
HIFVBWCWJAGT4DKS6DXL5YB4E64EJMMJNJ2VEJZP7G2Q357MDE5QC
DIY6I5TK7SSDULLLPKGT4LUS7IAL25NOJW7BM7WS542NYQPJ2K2AC
KAGCUOULIUHCPZSOSC6EJGOTH4PMEWCTMA4KGT7F5GQOQY5QX32QC
YJ4Q65CCPG2R3O2CFICU6WS27ISYEHQGWIHNOOYPN2TIPKNXVX7QC
LRNJ7O6FCNZ6TWEEQLXB6ZTKSDJNZ6HXV6T6RFVW5VU6E73MX4AQC
QZM4EDMUKU7QWQKWXHTRINNWH3GQQ6RSAX7WIX6PQAEM5V2Y2BQQC
HHW3CT6TLHVUGKIBE2KNYJTWGZMSFA6OVIUFOTZTIH6SGRA7DLDQC
Q652MJM34TH2ZV6CPA2RERMJMDMK2TQEAALRMXK2G6Y25UWKRJ3QC
JZTWTPXIUEVACX5B6FYQRVRJD3KQN653C4G4GXT2VSMX4EP7WS3AC
SROWUFFIKQV3PLGRJBPFUBMY4THTWIR2632PTVDAYEB5R3BDSDJAC
BMKL2AB2HBBBVAHWDYXPYZHSCO345QWOQHFSICFRN5BCYZ3UEK3AC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
3WNSWDDTTPWQ24SUOFU7EL6GODIMVTK4TL3P3AFLNB6AXUIIHFSAC
B3HWU2BEQQ4E6WKVTW3JQQJFMWTVW3XWKY6BHFNBRHSZPRCF2OTQC
FIYBXLWALQINNQTHG2KNDUUTAQAZRDDLXW2XOVSKDKBADJ3XCJ4AC
IQGGFC563RBS7GDOACKCLXK752EE5RC3T6G5L6H446SXTMSA7T2AC
L4Z2MHAQ2AAQSTVWBUCTBPXXJDLGR4JH2MMPQA3WBVCPCVNFMUXQC
LS2AILMM7THCB7OHESW7BGJMNZZXFHYOYVZBZH6OCFNETNLDEP5QC
RGGANRTOXB5K2HRQGK2F4XFZJVZSWOLVWFXZVVFEQQPO64Z4SQ7QC
5CPP63H7WJ3GLSHJM4Z2YSU2NISLVESEJ3OYMO4MFZEXVOCV4NIAC
PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
PBKRKGKARGZSLVFVDTK5NWDXQD26NHNN67LDSSB75CLEWBR6TLEQC
3ZT5EQNS5KXWEXAKV7C3TTFZ6QHOCR57LTFTETPKIVZGTXHZKRQAC
DGFWFFHCEBQK2TSMHN7RYPRNRVGEAYXM7GYJ4L37Z2ZNSIIUWQ4QC
DF5LOTJFSXOT7UBDHLBDLGA22OY4L7ZF6CVFP6H3KL4N5CHB5C5QC
SA36K3OJ75PNVR2F4QAPNU2LUGFTVHNIAZVSXCBXBT2NLW2RTUPAC
CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
NMZFKLRNGUMWPRLQCGHJPKTEFPPBIGMM2MEND5CR3QIEF5Z6BQDAC
NDKS43E4XVAU3FDY276OF5JW7E5SF6F5EJD2XLV5JLKZ5W6N554QC
EZ3ZUOFAHXI73BSCXNYS36TTK4OGCQBSFEIRVNXUAE64ZVKDW5PAC
VCQYSNAWZZHOZMARWQ4AJBDNFSS7T7CZBQISSPZ2YIIK5PVAWPRQC
KK5IGMPS7OBB2LA7SMGWVR534Q4LYKUD2MESO5IAMOKLNBBDWOMQC
74ETEQCJPMFG26EEH6SNDQNOWYZ2JBRFXGBQ3MMCGQPSVGSTIJVQC
5SKXI22WN2Y4SWUG634WDB7QXLBNV54RPN5OHAXRI55OFM5YBF5QC
CUM44NOPIB7LGTRY2O2R5MYXAB27R3TFM6LUBEQHZFXLZWJ55QZAC
47NSOFQMBZCDIBHEAZSENFUGDSQCX3GJHFBUZ65ARDKCYIZ435LAC
XJBNF2N35THJC2KYGCMPLC3CDCJP25CEDKHUI3P55V6H5YWXBUKAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
PWY4VZVHDLYL7UVNCCOW7BM7LYK2BOGTL23P75HXUJ33MHJPEJPQC
ENQRCBNN7MKP6FFQAZ62FYXEVWCRJB5747LTYMZGRGINXQL6HT5AC
R2DQBWKIW7YUJB5SOQ7J274JIYRVX4H3ISFRPAL5RG2RVVP4G2KAC
4HLF6Q3OBOOHCCJ76L2BXVIYI6EMJ2G7O4XXZPFTLFP6BQGSUZNAC
JESCEQPAFC23D6BUZMBOB4M54XT3GQZJYT7NCJ556LEUCHRER3SQC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
L254F6ZIU2HWGLFFGPIORTN4C3TDQ3E5JZ7Z7GQA5AEDIKL6PKDAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
J6APXOT4QOGQFONWB7G546VTVF6QG42HVOROMHF7YBDJPR4K26OAC
W74555HMPXUQ72AGKBXC5P3STMMX5DZAW6ZESUDLNVJBCAG43PLAC
6GDKXNFXPKQ6AVNOSMJECN7CLELM2KCMVRM2A7BARLK2NNILN6SAC
VNHFP63ZLLZU3A3PLXP4BITX57DUIYDHFOHQYK3BOBHV3S64G26QC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
AIVXE6QBRVCZAASKQRZO6LBDGTYEYSSD2DZCWRX4VLSKE3GCNIDAC
ASCTVJSN3NXYQHRVXAORA43CV6H5V2572IMK4UGRHKBAGJOWHC4AC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
3EUPIYJNWOMOQBP2Z5SGSMWK453BXJD6KL2WFTR3NM565MEBYASAC
ZJIGDAAKOHJWPD7TMNTNKP7ZT4K4GHD7UGSAWERTIJA3BGL5BETQC
FLAGBNUNSIQNFDN53CDWABJRTTFWDL4PG34AI474ZKPXDEPYHOAQC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
L4PKJZERR7WADKWHY3MR6J6OZFREVPL3CB43I6MLJ2BVKWCUTE7AC
ILADK4YNPQVYLZYDMGGBYN2EBBCIPCDNUCWZH27DQTKOMVUDLVMAC
K7J2IGELTIQL5KAMBV62MFVIVMEZEWCOLZ7GHFXHYPANYIF7BRZAC
5FHWTG7M6FW4B3I33YI7QSM3OZIB6ZGC6TI6JISSLY5Y43HI56VAC
PR42BCP5BPRFD2MP5H6CIJP7E57Q6TKL6SOXZWFKMFVR2OZWHT7AC
SPFKVC5KJVGMD4ITZWLWTB6N42IV6AWXGPEEXCYPI7BGFZKY5FHAC
U3SFBWHMMMO2PY6PAX7FMSBSND4RCWGJR42HB47LNQXLOX7ZOB6QC
C5VA63WAQRWPENIMXRPUPZLZJMC77PL2B3A77HYFWDCZU5QDG7VQC
RBAGQ2PB7V5YAM5KSHSZR2E3MLKDSRVM5XYGI2TIXP5QMVBOQHDQC
KBNY5FWKTEAKABFCLPC3QFKFSVZKAGXINPCIFV6WDSWFO4VCKNTAC
OSGS3PH2L5CBTDVZCZS6OCFQNA4A7RMEXBYJQB7DDZBYYJW7QSSAC
ODQ7LIJ2UROGGENIORRXZFWII3ZM2N45YD53FKWDQB7LLRXR4PHAC
ENOQQ6DEA6ECRNTBGYYNK7G3DFEILMKQBNKP4SUQIZW2L6HWVR7QC
ODCWOILHAMEQQVMGVUU6PWRSMHAINWP3MYFMPVMQBQZ5RYKGO45AC
7BBEAPSUBQMBK4XC2Q6YJ2JPSVSPCSBJUYXM6EWXHNTQG4AEJX3QC
IL3QYB75HIQQULVII3MPZKFXJAM7EE73MBHYK6I3EJQLFXRHO6LQC
2OQFLBVWCTXCWUPSDM76KNXTNC3YPAKRI5KLDAEU4KQLN4DEOLYQC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
I7QLYOTE6DLQZM7YWUWYLKHRJRB2A3STQ42ALSRGQICEWKD2QTEQC
AIIVH43Z5X3GTPFY4FXQRZPG6Y7QPH2KJ47VM2Q43PCGGD5MTMOAC
BMHUBADDGIOZRVN4P3O5QKIDUYD4RFWBS7MP5X6LZWAYHUBRVD2QC
NMZFCCM6O3KO2GJWKOSULN27B3QIZKWPBOB62PAILXMRQD4JMIMAC
if (is_unrandom_artefact(item) || is_fixed_artefact(item))
return;
if (item.base_type != OBJ_WEAPONS && item.base_type != OBJ_MISSILES)
return;
int brand;
if (item.base_type == OBJ_WEAPONS)
{
// Only switch already branded items.
if (get_weapon_brand(item) == SPWPN_NORMAL)
return;
brand = (int) SPWPN_CHAOS;
}
else
{
// Only switch already branded items.
if (get_ammo_brand(item) == SPWPN_NORMAL)
return;
brand = (int) SPMSL_CHAOS;
}
if (is_random_artefact(item))
randart_set_property(item, RAP_BRAND, brand);
else
item.special = brand;
}
}
static void _xom_acquirement(object_class_type force_class)
{
god_acting gdact(GOD_XOM);
int item_index = NON_ITEM;
if (!acquirement(force_class, GOD_XOM, false, &item_index)
|| item_index == NON_ITEM)
{
god_speaks(GOD_XOM, "\"No, never mind.\"");
return;
}
_try_brand_switch(item_index);
stop_running();
}
static bool _is_chaos_upgradeable(const item_def &item,
const monsters* mon)
{
// Since Xom is a god he is capable of changing randarts, but not
// other artifacts.
if (is_artefact(item) && !is_random_artefact(item))
return (false);
// Only upgrade permanent items, since the player should get a
// chance to use the item if s/he can defeat the monster.
if (item.flags & ISFLAG_SUMMONED)
return (false);
// Don't know how to downgrade blessed blades to normal blades.
// Can be justified as good gods protecting blessed blades.
if (is_blessed_blade(item))
return (false);
// God gifts from good gods are protected. Also, Beogh hates all
// the other gods so he'll protect his gifts as well.
if (item.orig_monnum < 0)
{
god_type iorig = static_cast<god_type>(-item.orig_monnum - 2);
if ((iorig > GOD_NO_GOD && iorig < NUM_GODS)
&& (is_good_god(iorig) || iorig == GOD_BEOGH))
{
return (false);
}
}
// Leave branded items alone, since this is supposed to be an
// upgrade.
if (item.base_type == OBJ_MISSILES)
{
// Don't make boulders or throwing nets of chaos.
if (item.sub_type == MI_LARGE_ROCK
|| item.sub_type == MI_THROWING_NET)
{
return (false);
}
if (get_ammo_brand(item) == SPMSL_NORMAL)
return (true);
}
else
{
// If the weapon is a launcher and the monster is either out
// of ammo or is carrying javelins then don't bother upgrading
// launcher.
if (is_range_weapon(item)
&& (mon->inv[MSLOT_MISSILE] == NON_ITEM
|| !has_launcher(mitm[mon->inv[MSLOT_MISSILE]])))
{
return (false);
}
if (get_weapon_brand(item) == SPWPN_NORMAL)
return (true);
}
return (false);
}
static bool _choose_chaos_upgrade(const monsters* mon)
{
// Only choose monsters that will attack.
if (!mon->alive() || mons_attitude(mon) != ATT_HOSTILE
|| mons_is_fleeing(mon) || mons_is_panicking(mon))
{
return (false);
}
if (mons_itemuse(mon) < MONUSE_STARTING_EQUIPMENT)
return (false);
// Holy beings are presumably protected by another god, unless they're
// gifts from Xom.
if (mons_is_holy(mon) && mon->god != GOD_XOM)
return (false);
// God gifts from good gods will be protected by their god from being
// given chaos weapons, while other gods won't mind the help in their
// servants killing the player.
if (mon->god != GOD_NO_GOD && is_good_god(mon->god))
return (false);
// Beogh presumably doesn't want Xom messing with his orcs, even if
// it would give them a better weapon.
if (mons_genus(mon->type) == MONS_ORC)
return (false);
mon_inv_type slots[] = {MSLOT_WEAPON, MSLOT_ALT_WEAPON, MSLOT_MISSILE};
// NOTE: Code assumes that the monster will only be carrying one
// missile launcher at a time.
bool special_launcher = false;
for (int i = 0; i < 3; i++)
{
const mon_inv_type slot = slots[i];
const int midx = mon->inv[slot];
if (midx == NON_ITEM)
continue;
const item_def &item(mitm[midx]);
// Monster already has a chaos weapon, give upgrade to a different
// monster.
if (is_chaotic_item(item))
return (false);
if (_is_chaos_upgradeable(item, mon))
{
if (item.base_type != OBJ_MISSILES)
return (true);
// If for some weird reason a monster is carrying a bow
// and javelins then branding the javelins is okay since
// they won't be fired by the bow.
if (!special_launcher || !has_launcher(item))
return (true);
}
if (is_range_weapon(item))
{
// If the launcher alters its ammo then branding the monster's
// ammo won't be an upgrade.
int brand = get_weapon_brand(item);
if (brand == SPWPN_FLAME || brand == SPWPN_FROST
|| brand == SPWPN_VENOM)
{
special_launcher = true;
}
}
}
return (false);
}
static void _do_chaos_upgrade(item_def &item, const monsters* mon)
{
ASSERT(item.base_type == OBJ_MISSILES
|| item.base_type == OBJ_WEAPONS);
ASSERT(!is_unrandom_artefact(item) && !is_fixed_artefact(item));
bool seen = false;
if (mon && you.can_see(mon) && item.base_type == OBJ_WEAPONS)
{
seen = true;
description_level_type desc = mons_friendly(mon) ? DESC_CAP_YOUR :
DESC_CAP_THE;
std::string msg = mon->name(desc);
msg += "'s ";
msg = replace_all(msg, "s's", "s'"); // Proper posessive.
msg += item.name(DESC_PLAIN, false, false, false);
msg += " is briefly surrounded by a scintillating arua of "
"random colours.";
mpr(msg.c_str());
}
const int brand = (item.base_type == OBJ_WEAPONS) ? (int) SPWPN_CHAOS :
(int) SPMSL_CHAOS;
if (is_random_artefact(item))
{
randart_set_property(item, RAP_BRAND, brand);
if (seen)
randart_wpn_learn_prop(item, RAP_BRAND);
}
else
{
item.special = brand;
if (seen)
set_ident_flags(item, ISFLAG_KNOW_TYPE);
}
monsters *mon =
choose_random_nearby_monster(0, _choose_chaos_upgrade);
if (!mon)
continue;
god_speaks(GOD_XOM, _get_xom_speech("chaos upgrade").c_str());
mon_inv_type slots[] = {MSLOT_WEAPON, MSLOT_ALT_WEAPON,
MSLOT_MISSILE};
for (int i = 0; i < 3; i++)
{
int idx = mon->inv[slots[i]];
if (idx == NON_ITEM)
continue;
item_def &item(mitm[idx]);
if (!_is_chaos_upgradeable(item, mon))
continue;
_do_chaos_upgrade(item, mon);
done = true;
break;
}
ASSERT(done);
// Wake the monster up.
behaviour_event( mon, ME_ALERT, MHITYOU );
}
else if (x_chance_in_y(8, sever))
{
break;
default:
break;
}
return (retval);
}
bool is_chaotic_item(const item_def& item)
{
bool retval = false;
switch (item.base_type)
{
case OBJ_WEAPONS:
{
const int item_brand = get_weapon_brand(item);
retval = (item_brand == SPWPN_CHAOS);
}
break;
case OBJ_MISSILES:
{
const int item_brand = get_ammo_brand(item);
retval = (item_brand == SPMSL_CHAOS);
}
if (((item.base_type == OBJ_POTIONS && item.sub_type == POT_MUTATION)
|| (item.base_type == OBJ_WANDS
&& item.sub_type == WAND_POLYMORPH_OTHER))
&& item_type_known(item))
{
if (item_type_known(item) && is_chaotic_item(item))
switch (random2(6))
{
case 0: attk.flavour = AF_POISON_NASTY; break;
case 1: attk.flavour = AF_ROT; break;
case 2: attk.flavour = AF_DRAIN_XP; break;
case 3: attk.flavour = AF_FIRE; break;
case 4: attk.flavour = AF_COLD; break;
case 5: attk.flavour = AF_BLINK; break;
}
mon_attack_flavour flavours[] =
{AF_POISON_NASTY, AF_ROT, AF_DRAIN_XP, AF_FIRE, AF_COLD, AF_BLINK};
attk.flavour = RANDOM_ELEMENT(flavours);
0, 10, MONS_CHAOS_SPAWN, MONS_CHAOS_SPAWN, MH_NATURAL, -3,
{ AT_NO_ATK, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
{ 0, 0, 0, 0 },
0, 0, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_RANDOM, I_NORMAL,
HT_LAND, 0, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_BIG
0, 12, MONS_CHAOS_SPAWN, MONS_CHAOS_SPAWN, MH_DEMONIC, -7,
{ {AT_RANDOM, AF_CHAOS, 21}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
{ 6, 3, 5, 0 },
7, 12, MST_NO_SPELLS, CE_MUTAGEN_RANDOM, Z_NOZOMBIE, S_RANDOM, I_ANIMAL,
HT_LAND, 11, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_BIG
}
}
std::string setup_chaos_ammo(bolt &pbolt, item_def ammo)
{
ASSERT(!is_artefact(ammo));
const bool poisoned = (get_ammo_brand(ammo) == SPMSL_POISONED);
// Don't choose BEAM_POISON or BEAM_HEALING if we have poisoned ammo.
const int pois_weight = poisoned ? 0 : 10;
const int heal_weight = poisoned ? 0 : 10;
const beam_type flavour = static_cast<beam_type>(
random_choose_weighted( pois_weight, BEAM_POISON,
heal_weight, BEAM_HEALING,
10, BEAM_FIRE,
10, BEAM_COLD,
10, BEAM_ELECTRICITY,
10, BEAM_NEG,
10, BEAM_ACID,
10, BEAM_HELLFIRE,
10, BEAM_NAPALM,
10, BEAM_HELLFROST,
10, BEAM_SLOW,
10, BEAM_HASTE,
10, BEAM_PARALYSIS,
10, BEAM_CONFUSION,
10, BEAM_INVISIBILITY,
10, BEAM_POLYMORPH,
10, BEAM_BANISH,
10, BEAM_DISINTEGRATION,
0 ));
std::string name;
int colour;
if (poisoned)
name = "poison ";
switch(flavour)
{
case BEAM_POISON:
name += "poison";
colour = EC_POISON;
break;
case BEAM_HEALING:
name += "healing";
colour = EC_HEAL;
break;
case BEAM_FIRE:
name += "flame";
colour = EC_FIRE;
break;
case BEAM_COLD:
name += "frost";
colour = EC_ICE;
break;
case BEAM_ELECTRICITY:
name += "lightning";
colour = EC_ELECTRICITY;
break;
case BEAM_NEG:
name += "negative energy";
colour = EC_NECRO;
break;
case BEAM_ACID:
name += "acid";
colour = YELLOW;
break;
case BEAM_HELLFIRE:
name += "hellfire";
colour = EC_FIRE;
break;
case BEAM_NAPALM:
name += "sticky fire";
colour = EC_FIRE;
break;
case BEAM_HELLFROST:
name += "hellfrost";
colour = EC_ICE;
break;
case BEAM_SLOW:
name += "slowing";
colour = EC_ENCHANT;
break;
case BEAM_HASTE:
name += "hasting";
colour = EC_ENCHANT;
break;
case BEAM_PARALYSIS:
name += "paralysis";
colour = EC_ENCHANT;
break;
case BEAM_CONFUSION:
name += "confusion";
colour = EC_ENCHANT;
break;
case BEAM_INVISIBILITY:
name += "invisibility";
colour = EC_ENCHANT;
break;
case BEAM_POLYMORPH:
name += "polymorphing";
colour = EC_MUTAGENIC;
break;
case BEAM_BANISH:
name += "banishment";
colour = EC_WARP;
break;
case BEAM_DISINTEGRATION:
name += "disintegration";
colour = EC_DEATH;
break;
default:
ASSERT(!"Invalid chaos ammo flavour.");
break;
pbolt.name = "bolt of ";
pbolt.name += name;
pbolt.flavour = flavour;
pbolt.colour = colour;
pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
// Get name for a plain arrow/bolt/dart/needle.
ammo.special = 0;
std::string ammo_name = ammo.name(DESC_NOCAP_A);
ammo_name += " of ";
ammo_name += name;
return ammo_name;
if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
&& ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
// Chaos overides flame and frost/ice.
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
ammo_name = setup_chaos_ammo(pbolt, item);
// [dshaligram] Branded arrows are much stronger.
dice_mult = (dice_mult * 150) / 100;
pbolt.effect_known = false;
}
else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
&& ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
&& ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
&& ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
}
void melee_attack::_monster_die(monsters* monster, killer_type killer,
int killer_index)
{
const bool chaos = damage_brand == SPWPN_CHAOS;
// Copy defender before it gets reset by monster_die()
monsters* def_copy = NULL;
if (chaos)
def_copy = new monsters(*monster);
monster_die(monster, killer, killer_index);
if (chaos)
{
chaos_killed_defender(def_copy);
delete def_copy;
}
const monsters* mon = dynamic_cast<const monsters*>(defender);
// No uniques, pandemonium lords or player ghosts. Also, figuring
// out the name for the clone of a named monster isn't worth it.
if (mons_is_unique(mon->type) || mon->is_named() || mon->ghost.get())
return (false);
// Holy beings can't be duplicated by chaotic means.
if (mons_is_holy(mon))
return (false);
// Is there space for the clone?
int squares = 0;
for (int i = 0; i < 8; i++)
{
const coord_def p = mon->pos() + Compass[i];
if (in_bounds(p) && p != you.pos() && mgrd(p) == NON_MONSTER
&& monster_habitable_grid(mon, grd(p)))
{
if (one_chance_in(++squares))
*pos = p;
}
}
if (squares == 0)
return (false);
// Is there an open slot in menv?
for (int i = 0; i < MAX_MONSTERS; i++)
if (menv[i].type == -1)
{
*midx = i;
break;
}
if (*midx == NON_MONSTER)
return (false);
// Is the monster carrying an artefact?
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
{
const int index = mon->inv[i];
if (index == NON_ITEM)
continue;
if (is_artefact(mitm[index]))
return (false);
}
return (true);
}
static bool _do_clone(monsters* orig, coord_def pos, int midx)
{
bool obvious = false;
monsters &mon(menv[midx]);
mon = *orig;
mon.position = pos;
mgrd(pos) = midx;
// Duplicate objects, or unequip them if they can't be duplicated.
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
{
const int old_index = orig->inv[i];
if (old_index == NON_ITEM)
continue;
const int new_index = get_item_slot(0);
if (new_index == NON_ITEM)
{
mon.unequip(mitm[old_index], i, 0, true);
mon.inv[i] = NON_ITEM;
continue;
}
mon.inv[i] = new_index;
mitm[new_index] = mitm[old_index];
}
// The player shouldn't get new permanent followers from cloning.
if (mon.attitude == ATT_FRIENDLY && !mons_is_summoned(&mon))
mon.mark_summoned(6, true);
if (you.can_see(orig) && you.can_see(&mon))
{
simple_monster_message(orig, " is duplicated!");
obvious = true;
}
mark_interesting_monst(&mon, mon.behaviour);
if (you.can_see(&mon))
{
seen_monster(&mon);
viewwindow(true, false);
}
return (obvious);
}
enum chaos_type
{
CHAOS_CLONE,
CHAOS_POLY,
CHAOS_POLY_UP,
CHAOS_MAKE_SHIFTER,
CHAOS_HEAL,
CHAOS_HASTE,
CHAOS_INVIS,
CHAOS_SLOW,
CHAOS_PARA,
CHAOS_PETRIFY,
NUM_CHAOS_TYPES
};
// XXX: We might want to vary the probabilites for the various effects
// based on whether the source is weapon of chaos or a monster with
// AF_CHAOS
void melee_attack::chaos_affects_defender()
{
coord_def clone_pos;
int clone_midx;
const bool mon = defender->atype() == ACT_MONSTER;
const bool immune = mon && mons_immune_magic(def);
const bool is_shifter = mon && mons_is_shapeshifter(def);
const bool is_chaotic = mon && mons_is_chaotic(def);
const bool can_clone = _can_clone(defender, &clone_pos, &clone_midx);
const bool can_poly = is_shifter || (defender->can_safely_mutate()
&& !immune);
int clone_chance = can_clone ? 1 : 0;
int poly_chance = can_poly ? 1 : 0;
int poly_up_chance = can_poly ? 1 : 0;
int shifter_chance = can_poly ? 1 : 0;
if (is_chaotic)
{
// Polymorphing might reduce amount of chaos in the world.
poly_chance = 0;
poly_up_chance = 0;
// Chaos wants more chaos.
clone_chance *= 2;
// Chaos loves shifters.
if (is_shifter)
{
clone_chance *= 2;
poly_up_chance = 4;
// Already a shifter
shifter_chance = 0;
}
}
// NOTE: Must appear in exact same order as in chaos_type enumeration.
int probs[NUM_CHAOS_TYPES] =
{
clone_chance, // CHAOS_CLONE
poly_chance, // CHAOS_POLY
poly_up_chance, // CHAOS_POLY_UP
shifter_chance, // CHAOS_MAKE_SHIFTER
5, // CHAOS_HEAL
5, // CHAOS_HASTE
5, // CHAOS_INVIS
15, // CHAOS_SLOW
15, // CHAOS_PARA
15, // CHAOS_PETRIFY
};
bolt beam;
beam.flavour = BEAM_NONE;
int choice = choose_random_weighted(probs, probs + NUM_CHAOS_TYPES);
switch(static_cast<chaos_type>(choice))
{
case CHAOS_CLONE:
ASSERT(can_clone);
ASSERT(defender->atype() == ACT_MONSTER);
obvious_effect = _do_clone(def, clone_pos, clone_midx);
break;
case CHAOS_POLY:
ASSERT(can_poly);
beam.flavour = BEAM_POLYMORPH;
break;
case CHAOS_POLY_UP:
ASSERT(can_poly);
ASSERT(defender->atype() == ACT_MONSTER);
obvious_effect = you.can_see(defender);
monster_polymorph(def, RANDOM_MONSTER, PPT_MORE, true);
break;
case CHAOS_MAKE_SHIFTER:
ASSERT(can_poly);
ASSERT(!is_shifter);
ASSERT(defender->atype() == ACT_MONSTER);
obvious_effect = you.can_see(defender);
def->add_ench(one_chance_in(3) ?
ENCH_GLOWING_SHAPESHIFTER : ENCH_SHAPESHIFTER);
// Immediately polymorph monster, just to make the effect obvious.
monster_polymorph(def, RANDOM_MONSTER, PPT_SAME, true);
break;
case CHAOS_HEAL:
beam.flavour = BEAM_HEALING;
break;
case CHAOS_HASTE:
beam.flavour = BEAM_HASTE;
break;
case CHAOS_INVIS:
beam.flavour = BEAM_INVISIBILITY;
break;
case CHAOS_SLOW:
beam.flavour = BEAM_SLOW;
break;
case CHAOS_PARA:
beam.flavour = BEAM_PARALYSIS;
break;
case CHAOS_PETRIFY:
beam.flavour = BEAM_PETRIFY;
break;
default:
ASSERT(!"Invalid chaos effect type");
break;
}
if (beam.flavour != BEAM_NONE)
{
beam.name = atk_name(DESC_CAP_THE);
beam.range = 1;
beam.colour = BLACK;
beam.is_beam = false;
beam.is_explosion = false;
beam.is_big_cloud = false;
beam.effect_known = false;
beam.thrower = (attacker->atype() == ACT_PLAYER) ? KILL_YOU
: def->confused_by_you() ? KILL_YOU_CONF
: KILL_MON;
beam.beam_source =
(attacker->atype() == ACT_PLAYER) ? MHITYOU : monster_index(atk);
beam.source = attacker->pos();
beam.target = defender->pos();
beam.pos = defender->pos();
beam.damage = dice_def(damage_done + special_damage + aux_damage, 1);
beam.ench_power = beam.damage.num;
fire_beam(beam);
if (you.can_see(defender))
obvious_effect = beam.obvious_effect;
}
if (!you.can_see(attacker))
obvious_effect = false;
}
void melee_attack::chaos_affects_attacker()
{
}
// NOTE: Isn't called if monster dies from poisoning caused by chaos.
void melee_attack::chaos_killed_defender(monsters* def_copy)
{
}
// NOTE: random_chaos_brand() and random_chaos_attack_flavour() should
// return a set of effects that are roughly the same, to make it easy
// for chaos_affects_defender() not to do duplicate effects caused
// by the non-chaos brands/flavours they return.
int melee_attack::random_chaos_brand()
{
int brands[] = {SPWPN_FLAMING, SPWPN_FREEZING, SPWPN_ELECTROCUTION,
SPWPN_VENOM, SPWPN_DRAINING, SPWPN_VAMPIRICISM,
SPWPN_PAIN, SPWPN_DISTORTION, SPWPN_CONFUSE,
SPWPN_CHAOS};
return (RANDOM_ELEMENT(brands));
}
mon_attack_flavour melee_attack::random_chaos_attack_flavour()
{
mon_attack_flavour flavours[] =
{AF_FIRE, AF_COLD, AF_ELEC, AF_POISON_NASTY, AF_VAMPIRIC, AF_DISTORT,
AF_CONFUSE, AF_CHAOS};
return (RANDOM_ELEMENT(flavours));
}
%%%%
# Chaos spawns shouldn't have coherent speech, since that would be too
# orderly for beings of pure chaos.
chaos spawn
VISUAL:@The_monster@ grows dozens of eye stalks in order to get a better look at you.
VISUAL:@The_monster@ splits into many small globs of multi-coloured light, then recombines.
VISUAL:@The_monster@ breifly grows a face disturbingly similar to your own.
@The_monster@ ululates chillingly with its many mouths.
@The_monster@ gibbers incoherently in a cacophony of voices.