(new Xom effects), tweaked to fit into current trunk.
FM4LIPXOMA65AXD33QNNRVMURTVCLGB7NCQYYS6I3DPOZFIKN4XQC
VFS5GS5BT64LETFFNKKZRHRT4EX6L3DYQD4QQKSBOKO4NHW4K6BQC
HP72STXEK6KJ5SAPZKKLLAXC5HB7J4E6X6NK2BNFP6GARF52K5RQC
LZN527GR3AULRHWIBA537RR4WSCNNYY4XSEZ6VFF7TKGYSUSBOCAC
UWYK2M6I6P6EFAVHLWQGDK77STFO3SPO5SVOF5WZTOKUH2O6BBHQC
5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC
IFYZSFWJRTV6JM46H6U4CMTQX46VT562EAM64Y6UYU3F6RDWRRYQC
TYAUNNAVB4GEKXYSR447JXRDKRJJZHAVV7XLWGU7RXEP5JMJCQKAC
QK7YLIH7SWBSL3OEBQJYVFNTW4XT5FF6U3GPKW7PEGHBLPV3TVQAC
TPJYUAKSEZMCCCJANJ5EQ7F67QVTPFEOWBD7WYK33NLRN657Y5VQC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
GYOKKBVTZ6HRW2NYC3EMGSB6ULBX24L5DLDZNEP2F6UTA4KPU3CAC
HCVH2CWL32UD66O6Z7ZYDUASWN3RF5TW6FSWURGMD7MELKB772FAC
SJJMO4TFG3QO46SLHUFSUYXXVZJDULVYTU4RTCM7TQPIGHZTWBDAC
542WW2UJM6XF22442ZD5NFMTS6HTDM74JKJ7GQHBLQ3D6C6T4C3AC
GP7RUHLNB4KISSZAENEYVNY3MB5YRCFRWRWYAROBZIQ5MR4DXB7QC
V25XPIUUPVMKRK663D33PWE3GFQR47XZAV642T2NYGRFWZR5LT6AC
ZF6STV5NEMEP5O5S6QO52R2CS5JEAGUQA5OKSBI2BIWF3OLZP4CQC
XYQFJLTMLSU7LC7VODUJVN5F2P47STH2KVSP7Q3BSCUUDRHROW7QC
NYURIMPCM2RADLMIQSN76OPKXQSK4XBLFNXD2OO53KGZI3MA6AQAC
LUD5XPFLSYPJOYZG6DDV4C5WIQUGZPE7YV6AR75ENCAIISD3LZWQC
WPWQO7BWPJHMAWU7FUJE5VZDZV66365DLBLLK64T557E7NDEMKXQC
IQGGFC563RBS7GDOACKCLXK752EE5RC3T6G5L6H446SXTMSA7T2AC
5UERXKWLQVLPKE6LN74XZ6V6NGZ3RL3RE22O4MH3ID7IQQ27YWPAC
BERPOHVI3K3SUE6M4MJVDSJVJ7Q4LWGRZ3SM2AZENUZJLRC2AP3AC
TPO6FNMPNUSWH4NCKO3VLYNAADEPSAXLUITCCACLZZSY53PKA62QC
43XCHFXZ34FDVT2QG23RZ65V6NRBY6PE3ENQEEDXFYKRR7IRFHCAC
FWNNTOEERPUKXPE4OC52UABFZLKIU3O5GRNNLDK4QI4HR2IOU36QC
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
45CWQUPQHKUFUJ4MVL5K3KCVCCKLY2Z7RZWZ53UT723RV5STPSRAC
H2OHWQKMHL66CPVJGJL35RY7EIZB74SZTGOLDJDSCG5WVEVPIIUAC
7GCM5WFIKX5N2PQ5UCVNMFJEKTZTBCUVZH5RZ7CPDL3Z6GB26KAQC
64HB7VYSYHQEN5UP7OYJ5GSVA2XMDSLLH647UPWE5NSFF3AVZSSQC
22YVHM74WBJNJE4PA5CBEUTDWM6FAGGGILI26A4LXAURX55TNRKAC
VO5CYD7Z4FYFDT6DGVBX6RT3PHLRSUJA6TWV3GPYVE7QCSJ3JEAAC
HGBHRHXFVTEWYYV2D5HKTKL4Z6OSNR2ZOIHFQX55CVRCXQUQ7MQAC
2HG3XZSSPWPPEAPBGO7PBXSJOGJWXIU6UIGEWZZAG54OMM5HQXUQC
LEZWYG752CKY6URMLSQTOADDRAJ5NTT7KH7BC5ENGQ55GXKHQMWQC
CDEJUVHMOT2SRPKWBB5IUPAXPGX6TUTGRBFLSPTR4XNZABDLSNUQC
PUY2VWZJ3G7HNCLIHZP5VGT2DAGHI436ETDJQTPEBFPRSEHOM2NQC
ZYXDNELXBXZ6F5EVS5R6CTR4SIZ6G2RX6DKGHAZYTKPO6XJCZYRAC
7TC5S2NB46NFPXQKVXUYA26DZ2OE42WWE7RUSBJHXQPUGAWHIFMAC
T6O6AMWRWJE6EQWGKWJRHQG7X2V7XRWS5WZOQIMWUD6TPLHUGPBAC
PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC
BAQJ26USF7KRR6ALYPXUPUFOYLNGBQVQOS53JXJJWZKDVREHFL2QC
KO7PZNWIRLSAPOXUHK7U446ZXT562QCPIT3AYFYKPG7OWRLNBU4QC
5CPP63H7WJ3GLSHJM4Z2YSU2NISLVESEJ3OYMO4MFZEXVOCV4NIAC
FAVME2A2U4OUKN2BTISAG5PCI5Y4BN6YVFKCSQQHHZLEVVVF4LKAC
BMXIXQKM2247TENYZSOULFAT6VSZJNGGFBQ3SN7IOM4ROQRVTHZAC
TRBDS4IKHBASW2CTAQ2YHMBMBMRB6QEBQ4ZE7GRSR3U2VJXA7TJQC
FYSQ7HXDIKXZXDGYVKZMODW7HYQQLRZO52Q2HVM3PYBD37UT5B4AC
5MGUZD2UACJCSG74TEZHI3Z4YL5KL6ZVUCQ3XVZKDOLKM7EMGWJAC
IWYUUMWDNSGRNR7CJDMJQJHQ6T4UTTGX6X5DQMQ4LH7CZUODDQPQC
IBV5MNXWCCOWCI7DPVTPFEEMLX7QWP75EP6YQ3RA2WJOKWSGMPSQC
AVEOSLDREJHWFZE6ULAWGQPDT24YX4OAOGL7YAUCFVRGHCS22UCAC
A54S2WRWNIV4Y4XB5JHR7NALLB55ZCX3Z5DSGBMAVGVY6I7KY2GAC
RDOOG5LBE5TCTFYCKJIB7TGGTRFX4HBLMJZYXS5TCFWNCU3QII5QC
ERS7DYNAVCC7QUVG5FXJJRONYESLZU22CQGKCA5HLLLG6DMXYRLQC
2NVJIPJ5NMHUI2J4WOR6KE4XZOCJIVWHQK4M2M6KG7PL24PGBDGAC
2U7MKFVTHEPKDLBYR4X6GINIEIBG5B3YR3INX36ITGP7MEPTR3VQC
BQ5QKUWLCYSBO4A7EWZN4FXPNSIAR6JPBAZE4PSV6XCUNTV53ERQC
QNSFZBLVAN7BPYYRXUELHBZCLIQTYDATDIXF3YVH7LBBST66VUCQC
W5VEC2PBIM5DMU5233HOWAZUEPTGWJRZZIA3H35YYQQW6BTP6XUAC
QLIDDODO7Q7S2UTKVR2SMRMEAXHRPA5C7SU267EQGV6VJ7UXW5JQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
UPA65AL4JXYLIHH4D42IWJHRTOAF2BPOVZOAKOXBLZBYIMDZDFFQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
EFWEYIB2R3DPD3JWIPU6LS6SFLPMYN7J7X4GBZR7DJWKHJ3UELSAC
JVWWN7BKTLALZ3VIKCFC45EUJ7CC5MLMH2ZOGOTNRF7FLWJGSMQAC
ESWIM76FGJL4QFLSHU6AC4D74PT7OPLQ7ZCJYWLZS5UCBAJDXYHAC
O6ZMFKDI3XO2SWPNEYHIPYFDWJR4TVDP5BAATK6LVCVETQID6E7AC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
RX6575DZOHRUXQUZH34YZGPZJF4STUPLBQDIVTINA2L6LVCKRIGQC
NMZFCCM6O3KO2GJWKOSULN27B3QIZKWPBOB62PAILXMRQD4JMIMAC
BINKDWGFGUPTOA7IE5KK4ZIELGU5WC3X47MYXOWU4X43EGAC5DUAC
U2CE3EGQNU6WJMRSUMVRLWPY5Q32VAE2ESTQARJQHJAG4EIHOJCQC
DWWOM6V2Z4RQVT6AKW6OYLSFOUE4W5PTP2SH4AAZE2E4EIH4RLUAC
};
enum xom_event_type
{
XOM_DID_NOTHING = 0,
// good acts
XOM_GOOD_POTION,
XOM_GOOD_SPELL_TENSION,
XOM_GOOD_SPELL_CALM,
XOM_GOOD_MAPPING,
XOM_GOOD_CONFUSION, // 5
XOM_GOOD_SINGLE_ALLY,
XOM_GOOD_ANIMATE_MON_WPN,
XOM_GOOD_ANNOYANCE_GIFT,
XOM_GOOD_RANDOM_ITEM,
XOM_GOOD_ACQUIREMENT, // 10
XOM_GOOD_ALLIES,
XOM_GOOD_POLYMORPH,
XOM_GOOD_SWAP_MONSTERS,
XOM_GOOD_TELEPORT,
XOM_GOOD_VITRIFY, // 15
XOM_GOOD_MUTATION,
XOM_GOOD_MAJOR_ALLY,
XOM_GOOD_LIGHTNING,
XOM_GOOD_SCENERY,
XOM_LAST_GOOD_ACT = XOM_GOOD_SCENERY, // 19
// bad acts
XOM_BAD_MISCAST_PSEUDO, // 20
XOM_BAD_MISCAST_MINOR,
XOM_BAD_MISCAST_MAJOR,
XOM_BAD_MISCAST_NASTY,
XOM_BAD_STATLOSS,
XOM_BAD_TELEPORT, // 25
XOM_BAD_SWAP_WEAPONS,
XOM_BAD_CHAOS_UPGRADE,
XOM_BAD_MUTATION,
XOM_BAD_POLYMORPH,
XOM_BAD_STAIRS, // 30
XOM_BAD_CONFUSION,
XOM_BAD_DRAINING,
XOM_BAD_TORMENT,
XOM_BAD_ANIMATE_WPN,
XOM_BAD_SUMMON_DEMONS, // 35
XOM_BAD_BANISHMENT,
XOM_LAST_BAD_ACT = XOM_BAD_BANISHMENT, // 36
XOM_PLAYER_DEAD = 100, // player already dead (shouldn't happen)
NUM_XOM_EVENTS
#endif
static const char *describe_xom_mood()
{
return (you.piety > 180) ? "Xom's teddy bear." :
(you.piety > 150) ? "a beloved toy of Xom." :
(you.piety > 120) ? "a favourite toy of Xom." :
(you.piety > 80) ? "a toy of Xom." :
(you.piety > 50) ? "a plaything of Xom." :
(you.piety > 20) ? "a special plaything of Xom."
: "a very special plaything of Xom.";
}
{
favour = (you.piety > 180) ? "Xom's teddy bear." :
(you.piety > 150) ? "a beloved toy of Xom." :
(you.piety > 120) ? "a favourite toy of Xom." :
(you.piety > 80) ? "a toy of Xom." :
(you.piety > 50) ? "a plaything of Xom." :
(you.piety > 20) ? "a special plaything of Xom."
: "a very special plaything of Xom.";
}
favour = describe_xom_mood();
return (false);
}
static bool _xom_give_item(int power)
{
if (_xom_annoyance_gift(power))
return (true);
god_speaks(GOD_XOM, _get_xom_speech("general gift").c_str());
return (false);
}
static int _xom_give_item(int power, bool debug = false)
{
if (_xom_annoyance_gift(power, debug))
return (XOM_GOOD_ANNOYANCE_GIFT);
if (!debug)
god_speaks(GOD_XOM, _get_xom_speech("general gift").c_str());
static bool _art_is_safe(item_def item)
{
int prop_str = artefact_wpn_property(item, ARTP_STRENGTH);
int prop_int = artefact_wpn_property(item, ARTP_INTELLIGENCE);
int prop_dex = artefact_wpn_property(item, ARTP_DEXTERITY);
return (prop_str >= 0 && prop_int >= 0 && prop_dex >= 0);
}
static int _xom_swap_weapons(bool debug = false)
{
if (player_stair_delay())
return (XOM_DID_NOTHING);
item_def *wpn = you.weapon();
if (!wpn)
return (XOM_DID_NOTHING);
if (you.duration[DUR_BERSERKER]
|| wpn->base_type != OBJ_WEAPONS
|| get_weapon_brand(*wpn) == SPWPN_DISTORTION
|| !safe_to_remove_or_wear(*wpn, true, true))
{
return (XOM_DID_NOTHING);
}
std::vector<monsters *> mons_wpn;
for (unsigned i = 0; i < MAX_MONSTERS; ++i)
{
monsters* m = &menv[i];
if (!m->alive())
continue;
if (!see_grid(m->pos()))
continue;
if (!wpn || mons_wont_attack(m) || mons_is_summoned(m)
|| mons_itemuse(m) < MONUSE_STARTING_EQUIPMENT
|| (m->flags & MF_HARD_RESET))
{
continue;
}
const int mweap = m->inv[MSLOT_WEAPON];
if (mweap == NON_ITEM)
continue;
const item_def weapon = mitm[mweap];
// Let's be nice about this.
if (weapon.base_type == OBJ_WEAPONS
&& !(weapon.flags & ISFLAG_SUMMONED)
&& you.can_wield(weapon, true) && m->can_wield(*wpn, true)
&& !get_weapon_brand(weapon) != SPWPN_DISTORTION
&& (!is_artefact(weapon) || _art_is_safe(weapon)))
{
mons_wpn.push_back(m);
}
}
if (mons_wpn.empty())
return (XOM_DID_NOTHING);
if (debug)
return (XOM_BAD_SWAP_WEAPONS);
god_speaks(GOD_XOM, _get_xom_speech("swap weapons").c_str());
const int num_mons = mons_wpn.size();
// Pick a random monster...
monsters *mon = mons_wpn[random2(num_mons)];
// ...and get its weapon.
int monwpn = mon->inv[MSLOT_WEAPON];
int mywpn = you.equip[EQ_WEAPON];
ASSERT (monwpn != NON_ITEM);
ASSERT (mywpn != -1);
unwield_item();
item_def &myweapon = you.inv[mywpn];
int index = get_item_slot(10);
if (index == NON_ITEM)
return (XOM_DID_NOTHING);
// Move monster's old item to player's inventory as last step.
mon->unequip(*(mon->mslot_item(MSLOT_WEAPON)), MSLOT_WEAPON, 0, true);
mon->inv[MSLOT_WEAPON] = NON_ITEM;
mitm[index] = myweapon;
unwind_var<int> save_speedinc(mon->speed_increment);
if (!mon->pickup_item(mitm[index], false, true))
{
mpr("Monster wouldn't take item.", MSGCH_ERROR);
mon->inv[MSLOT_WEAPON] = monwpn;
mon->equip(mitm[monwpn], MSLOT_WEAPON, 0);
unlink_item(index);
destroy_item(myweapon);
return (XOM_DID_NOTHING);
}
// Mark the weapon as thrown, so that we'll autograb it once the
// monster is dead.
mitm[index].flags |= ISFLAG_THROWN;
mprf("%s wields %s!",
mon->name(DESC_CAP_THE).c_str(),
myweapon.name(DESC_NOCAP_YOUR).c_str());
mon->equip(myweapon, MSLOT_WEAPON, 0);
// Item is gone from player's inventory.
dec_inv_item_quantity(mywpn, myweapon.quantity);
mitm[monwpn].pos.reset();
mitm[monwpn].link = NON_ITEM;
int freeslot = find_free_slot(mitm[monwpn]);
if (freeslot < 0 || freeslot >= ENDOFPACK
|| is_valid_item(you.inv[freeslot]))
{
// Something is terribly wrong.
return (XOM_DID_NOTHING);
}
item_def &myitem = you.inv[freeslot];
// Copy item.
myitem = mitm[monwpn];
myitem.link = freeslot;
myitem.pos.set(-1, -1);
// Remove "dropped by ally" flag.
myitem.flags &= ~(ISFLAG_DROPPED_BY_ALLY);
if (!myitem.slot)
myitem.slot = index_to_letter(myitem.link);
origin_set_monster(myitem, mon);
note_inscribe_item(myitem);
dec_mitm_item_quantity(monwpn, myitem.quantity);
you.m_quiver->on_inv_quantity_changed(freeslot, myitem.quantity);
burden_change();
mprf("You wield %s %s!",
mon->name(DESC_NOCAP_ITS).c_str(),
you.inv[freeslot].name(DESC_PLAIN).c_str());
you.equip[EQ_WEAPON] = freeslot;
wield_effects(freeslot, true);
you.wield_change = true;
you.m_quiver->on_weapon_changed();
return (XOM_BAD_SWAP_WEAPONS);
}
return (true);
if (mons_wont_attack(m) || mons_is_summoned(m)
|| mons_itemuse(m) < MONUSE_STARTING_EQUIPMENT
|| (m->flags & MF_HARD_RESET))
{
continue;
}
const int mweap = m->inv[MSLOT_WEAPON];
if (mweap == NON_ITEM)
continue;
const item_def weapon = mitm[mweap];
if (weapon.base_type == OBJ_WEAPONS
&& !(weapon.flags & ISFLAG_SUMMONED)
&& weapon.quantity == 1
&& !is_range_weapon(weapon)
&& !is_special_unrandom_artefact(weapon)
&& !get_weapon_brand(weapon) != SPWPN_DISTORTION)
{
mons_wpn.push_back(m);
}
}
if (mons_wpn.empty())
return (XOM_DID_NOTHING);
if (debug)
return (XOM_GOOD_ANIMATE_MON_WPN);
god_speaks(GOD_XOM, _get_xom_speech("animate monster weapon").c_str());
const int num_mons = mons_wpn.size();
// Pick a random monster...
monsters *mon = mons_wpn[random2(num_mons)];
// ...and get its weapon.
const int wpn = mon->inv[MSLOT_WEAPON];
ASSERT(wpn != NON_ITEM);
const int dur = std::min(2 + (random2(sever) / 5), 6);
const int monster = create_monster(
mgen_data(MONS_DANCING_WEAPON, BEH_FRIENDLY,
dur, SPELL_TUKIMAS_DANCE,
mon->pos(), mon->mindex(), 0, GOD_XOM));
if (monster == -1)
return (XOM_DID_NOTHING);
// Make the monster unwield its weapon.
mon->unequip(*(mon->mslot_item(MSLOT_WEAPON)), MSLOT_WEAPON, 0, true);
mon->inv[MSLOT_WEAPON] = NON_ITEM;
mprf("%s %s dances into the air!",
apostrophise(mon->name(DESC_CAP_THE)).c_str(),
mitm[wpn].name(DESC_PLAIN).c_str());
destroy_item(menv[monster].inv[MSLOT_WEAPON]);
menv[monster].inv[MSLOT_WEAPON] = wpn;
mitm[wpn].set_holding_monster(monster);
menv[monster].colour = mitm[wpn].colour;
return (XOM_GOOD_ANIMATE_MON_WPN);
return (true);
return (XOM_GOOD_LIGHTNING);
}
static int _xom_change_scenery(bool debug = false)
{
std::vector<coord_def> candidates;
for (radius_iterator ri(you.pos(), LOS_RADIUS, false, true); ri; ++ri)
{
if (!in_bounds(*ri) || !see_grid(*ri))
continue;
dungeon_feature_type feat = grd(*ri);
if (feat >= DNGN_FOUNTAIN_BLUE && feat <= DNGN_DRY_FOUNTAIN_BLOOD)
candidates.push_back(*ri);
else if (feat >= DNGN_CLOSED_DOOR && feat <= DNGN_SECRET_DOOR
|| feat == DNGN_OPEN_DOOR && you.pos() != *ri)
{
candidates.push_back(*ri);
}
}
const std::string speech = _get_xom_speech("scenery");
if (candidates.empty())
{
if (!one_chance_in(8))
return (XOM_DID_NOTHING);
// Place one or more altars to Xom.
coord_def place;
bool success = false;
const int max_altars = std::max(1, random2(random2(14)));
for (int tries = max_altars; tries > 0; --tries)
{
if ((random_near_space(you.pos(), place)
|| random_near_space(you.pos(), place, true))
&& grd(place) == DNGN_FLOOR && see_grid(place))
{
if (debug)
return (XOM_GOOD_SCENERY);
grd(place) = DNGN_ALTAR_XOM;
success = true;
}
}
if (success)
{
god_speaks(GOD_XOM, speech.c_str());
return (XOM_GOOD_SCENERY);
}
return (XOM_DID_NOTHING);
}
if (debug)
return (XOM_GOOD_SCENERY);
const int fountain_diff = (DNGN_DRY_FOUNTAIN_BLUE - DNGN_FOUNTAIN_BLUE);
std::random_shuffle(candidates.begin(), candidates.end());
int doors_open = 0;
int doors_close = 0;
int fountains_flow = 0;
int fountains_blood = 0;
for (unsigned int i = 0; i < candidates.size(); ++i)
{
coord_def pos = candidates[i];
switch (grd(pos))
{
case DNGN_CLOSED_DOOR:
case DNGN_DETECTED_SECRET_DOOR:
case DNGN_SECRET_DOOR:
grd(pos) = DNGN_OPEN_DOOR;
doors_open++;
break;
case DNGN_OPEN_DOOR:
grd(pos) = DNGN_CLOSED_DOOR;
doors_close++;
break;
case DNGN_DRY_FOUNTAIN_BLUE:
case DNGN_DRY_FOUNTAIN_SPARKLING:
case DNGN_DRY_FOUNTAIN_BLOOD:
{
if (x_chance_in_y(fountains_flow, 5))
continue;
grd(pos) = (dungeon_feature_type) (grd(pos) - fountain_diff);
fountains_flow++;
break;
}
case DNGN_FOUNTAIN_BLUE:
if (x_chance_in_y(fountains_blood, 3))
continue;
grd(pos) = DNGN_FOUNTAIN_BLOOD;
fountains_blood++;
break;
default:
break;
}
}
if (!doors_open && !doors_close && !fountains_flow && !fountains_blood)
return (XOM_DID_NOTHING);
god_speaks(GOD_XOM, speech.c_str());
std::vector<std::string> effects;
if (doors_open > 0)
{
snprintf(info, INFO_SIZE, "%s door%s burst%s open",
doors_open == 1 ? "A" :
doors_open == 2 ? "Two"
: "Several",
doors_open == 1 ? "" : "s",
doors_open == 1 ? "s" : "");
effects.push_back(info);
}
if (doors_close > 0)
{
snprintf(info, INFO_SIZE, "%s%s door%s slam%s shut",
doors_close == 1 ? "a" :
doors_close == 2 ? "two"
: "several",
doors_open > 0 ? (doors_close == 1 ? "nother" : " other")
: "",
doors_close == 1 ? "" : "s",
doors_close == 1 ? "s" : "");
std::string closed = info;
if (effects.empty())
closed = uppercase_first(closed);
effects.push_back(closed);
}
if (!effects.empty())
{
mprf("%s!",
comma_separated_line(effects.begin(), effects.end(),
", and ").c_str());
effects.clear();
}
if (fountains_flow > 0)
{
snprintf(info, INFO_SIZE,
"%s fountain%s start%s reflowing",
fountains_flow == 1 ? "A" : "Some",
fountains_flow == 1 ? "" : "s",
fountains_flow == 1 ? "s" : "");
effects.push_back(info);
}
if (fountains_blood > 0)
{
snprintf(info, INFO_SIZE,
"%s%s fountain%s start%s gushing blood",
fountains_blood == 1 ? "a" : "some",
fountains_flow > 0 ? (fountains_blood == 1 ? "nother"
: " other")
: "",
fountains_blood == 1 ? "" : "s",
fountains_blood == 1 ? "s" : "");
std::string fountains = info;
if (effects.empty())
fountains = uppercase_first(fountains);
effects.push_back(fountains);
}
if (!effects.empty())
{
mprf("%s!",
comma_separated_line(effects.begin(), effects.end(),
", and ").c_str());
}
return (XOM_GOOD_SCENERY);
done = _xom_send_one_ally(sever);
else if (x_chance_in_y(6, sever))
{
_xom_give_item(sever);
done = true;
}
done = _xom_send_one_ally(sever, debug);
else if (tension < random2(5) && x_chance_in_y(6, sever))
done = _xom_change_scenery(debug);
else if (x_chance_in_y(7, sever))
done = _xom_give_item(sever, debug);
else if (tension > random2(10) && x_chance_in_y(7, sever))
done = _xom_send_allies(sever);
else if (x_chance_in_y(8, sever))
done = _xom_polymorph_nearby_monster(true);
else if (tension > 0 && x_chance_in_y(9, sever))
done = _xom_rearrange_pieces(sever);
else if (random2(tension) < 15 && x_chance_in_y(10, sever))
{
_xom_give_item(sever);
done = true;
}
else if (x_chance_in_y(11, sever) && you.level_type != LEVEL_ABYSS)
else if (tension > random2(10) && x_chance_in_y(8, sever))
done = _xom_send_allies(sever, debug);
else if (tension > random2(8) && x_chance_in_y(9, sever))
done = _xom_animate_monster_weapon(sever, debug);
else if (x_chance_in_y(10, sever))
done = _xom_polymorph_nearby_monster(true, debug);
else if (tension > 0 && x_chance_in_y(11, sever))
done = _xom_rearrange_pieces(sever, debug);
else if (random2(tension) < 15 && x_chance_in_y(12, sever))
done = _xom_give_item(sever, debug);
else if (x_chance_in_y(13, sever) && you.level_type != LEVEL_ABYSS)
else if (tension > random2(15) && x_chance_in_y(14, sever))
done = _xom_send_major_ally(sever);
else if (tension > 0 && x_chance_in_y(15, sever))
done = _xom_throw_divine_lightning();
else if (tension > random2(15) && x_chance_in_y(16, sever))
done = _xom_send_major_ally(sever, debug);
else if (tension > 0 && x_chance_in_y(17, sever))
done = _xom_throw_divine_lightning(debug);
const int level = random2(max_level + 1);
const int level = (nasty ? 1 + random2(max_level)
: random2(max_level + 1));
if (debug)
{
switch (level)
{
case 0: return (XOM_BAD_MISCAST_PSEUDO);
case 1: return (XOM_BAD_MISCAST_MINOR);
case 2: return (XOM_BAD_MISCAST_MAJOR);
case 3: return (XOM_BAD_MISCAST_NASTY);
}
}
{
_xom_miscast(0, nasty);
done = true;
}
done = _xom_miscast(0, nasty, debug);
done = true;
}
else if (x_chance_in_y(8, sever))
{
done = _xom_chaos_upgrade_nearby_monster();
badness = 2 + coinflip();
}
else if (random2(tension) < 10 && x_chance_in_y(9, sever))
{
done = _xom_give_mutations(false);
badness = 3;
done = XOM_BAD_TELEPORT;
// It's pointless to confuse player if there's no danger nearby.
else if (tension > 0 && x_chance_in_y(12, sever))
else if (random2(tension) < 11 && x_chance_in_y(13, sever))
god_speaks(GOD_XOM, _get_xom_speech("banishment").c_str());
// handles note taking
banished(DNGN_ENTER_ABYSS, "Xom");
badness = 5;
done = true;
if (!debug)
{
god_speaks(GOD_XOM, _get_xom_speech("banishment").c_str());
// handles note taking
banished(DNGN_ENTER_ABYSS, "Xom");
badness = 5;
}
done = XOM_BAD_BANISHMENT;
}
#ifdef WIZARD
struct xom_effect_count
{
std::string effect;
int count;
xom_effect_count(std::string e, int c) : effect(e), count(c) {};
};
static bool _sort_xom_effects(const xom_effect_count &a,
const xom_effect_count &b)
{
if (a.count == b.count)
return (a.effect < b.effect);
return (a.count > b.count);
}
static const char* _xom_effect_to_name(int effect)
{
ASSERT(effect < XOM_PLAYER_DEAD);
// See xom.h
static const char* _xom_effect_names[] =
{
"nothing",
// good acts
"potion", "spell (tension)", "spell (no tension)", "mapping",
"confuse monsters", "single ally", "animate monster weapon",
"annoyance gift", "random item gift", "acquirement", "summon allies",
"polymorph", "swap monsters", "teleportation", "vitrification",
"mutation", "permanent ally", "lightning", "change scenery",
// bad acts
"miscast (pseudo)", "miscast (minor)", "miscast (major)",
"miscast (nasty)", "stat loss", "teleportation", "swap weapons",
"chaos upgrade", "mutation", "polymorph", "repel stairs", "confusion",
"draining", "torment", "animate weapon", "summon demons", "banishment"
};
std::string result = "";
if (effect > XOM_DID_NOTHING && effect < XOM_PLAYER_DEAD)
{
if (effect <= XOM_LAST_GOOD_ACT)
result = "GOOD: ";
else
result = "BAD: ";
}
result += _xom_effect_names[effect];
return (result.c_str());
}
// Loops over the entire piety spectrum and calls xom_acts() multiple
// times for each value, then prints the results into a file.
// TODO: Allow specification of niceness, tension, boredness, and repeats.
void debug_xom_effects()
{
// Repeat N times.
const int N = 10;
FILE *ostat = fopen("xom_debug.stat", "a");
if (!ostat)
{
mprf(MSGCH_ERROR, "Can't write 'xom_debug.stat'. Aborting.");
return;
}
const int real_piety = you.piety;
const god_type real_god = you.religion;
you.religion = GOD_XOM;
const int tension = get_tension(GOD_XOM);
fprintf(ostat, "---- STARTING XOM DEBUG TESTING ----\n");
fprintf(ostat, "%s\n", dump_overview_screen(false).c_str());
fprintf(ostat, screenshot().c_str());
fprintf(ostat, "\n%s\n", mpr_monster_list().c_str());
fprintf(ostat, " --> Tension: %d\n", tension);
if (you.penance[GOD_XOM])
fprintf(ostat, "You are under Xom's penance!\n");
else if (_xom_is_bored())
fprintf(ostat, "Xom is BORED.\n");
std::vector<int> mood_effects;
std::vector<std::vector<int> > all_effects;
std::vector<std::string> moods;
std::vector<int> mood_good_acts;
std::string old_mood = "";
std::string mood = "";
// Add an empty list to later add all effects to.
all_effects.push_back(mood_effects);
moods.push_back("total");
mood_good_acts.push_back(0); // count total good acts
int mood_good = 0;
for (int p = 0; p <= MAX_PIETY; ++p)
{
you.piety = p;
int sever = abs(p - HALF_MAX_PIETY);
mood = describe_xom_mood();
if (old_mood != mood)
{
if (old_mood != "")
{
all_effects.push_back(mood_effects);
mood_effects.clear();
mood_good_acts.push_back(mood_good);
mood_good_acts[0] += mood_good;
mood_good = 0;
}
moods.push_back(mood);
old_mood = mood;
}
// Repeat N times.
for (int i = 0; i < N; ++i)
{
bool niceness = xom_is_nice(tension);
int result = xom_acts(niceness, sever, tension, true);
mood_effects.push_back(result);
all_effects[0].push_back(result);
if (result <= XOM_LAST_GOOD_ACT)
mood_good++;
}
}
all_effects.push_back(mood_effects);
mood_effects.clear();
mood_good_acts.push_back(mood_good);
mood_good_acts[0] += mood_good;
const int num_moods = moods.size();
std::vector<xom_effect_count> xom_ec_pairs;
for (int i = 0; i < num_moods; ++i)
{
mood_effects = all_effects[i];
const int total = mood_effects.size();
if (i == 0)
fprintf(ostat, "\nTotal effects (all piety ranges)\n");
else
fprintf(ostat, "\nMood: You are %s\n", moods[i].c_str());
fprintf(ostat, "GOOD%7.2f%%\n",
(100.0 * (float) mood_good_acts[i] / (float) total));
fprintf(ostat, "BAD %7.2f%%\n",
(100.0 * (float) (total - mood_good_acts[i]) / (float) total));
std::sort(mood_effects.begin(), mood_effects.end());
xom_ec_pairs.clear();
int old_effect = XOM_DID_NOTHING;
int count = 0;
for (int k = 0; k < total; ++k)
{
if (mood_effects[k] != old_effect)
{
if (count > 0)
{
std::string name = _xom_effect_to_name(old_effect);
xom_effect_count xec = xom_effect_count(name, count);
xom_ec_pairs.push_back(xec);
}
old_effect = mood_effects[k];
count = 1;
}
else
count++;
}
if (count > 0)
{
std::string name = _xom_effect_to_name(old_effect);
xom_effect_count xec = xom_effect_count(name, count);
xom_ec_pairs.push_back(xec);
}
std::sort(xom_ec_pairs.begin(), xom_ec_pairs.end(), _sort_xom_effects);
for (unsigned int k = 0; k < xom_ec_pairs.size(); ++k)
{
xom_effect_count xec = xom_ec_pairs[k];
fprintf(ostat, "%7.2f%% %s\n",
(100.0 * (float) xec.count / (float) total),
xec.effect.c_str());
}
}
fprintf(ostat, "---- FINISHED XOM DEBUG TESTING ----\n");
fclose(ostat);
mpr("Results written into 'xom_debug.stat'.");
you.piety = real_piety;
you.religion = real_god;
#endif