summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authordolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573>2008-10-16 21:42:51 +0000
committerdolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573>2008-10-16 21:42:51 +0000
commitfcd2f73d85997682a9c89c897348ae8e32432589 (patch)
tree96faec16d93756f84ac6b64aaa67e6f7befd5cec /crawl-ref
parent66e21b71a9d7d1e2569caa2c63d25e3b935fd2c0 (diff)
downloadcrawl-ref-fcd2f73d85997682a9c89c897348ae8e32432589.tar.gz
crawl-ref-fcd2f73d85997682a9c89c897348ae8e32432589.zip
Replace Yred's "Control Undead" power with "Enslave Soul", as proposed
on crawl-ref-discuss. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7263 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/abl-show.cc24
-rw-r--r--crawl-ref/source/beam.cc48
-rw-r--r--crawl-ref/source/dat/descript/ability.txt4
-rw-r--r--crawl-ref/source/dat/descript/gods.txt2
-rw-r--r--crawl-ref/source/directn.cc11
-rw-r--r--crawl-ref/source/enum.h57
-rw-r--r--crawl-ref/source/mon-util.cc133
-rw-r--r--crawl-ref/source/mon-util.h5
-rw-r--r--crawl-ref/source/monstuff.cc26
-rw-r--r--crawl-ref/source/monstuff.h4
-rw-r--r--crawl-ref/source/religion.cc146
-rw-r--r--crawl-ref/source/religion.h2
12 files changed, 410 insertions, 52 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc
index bc0a29fb98..4b42bd5b5d 100644
--- a/crawl-ref/source/abl-show.cc
+++ b/crawl-ref/source/abl-show.cc
@@ -110,7 +110,7 @@ ability_type god_abilities[MAX_NUM_GODS][MAX_GOD_ABILITIES] =
// Yredelemnul
{ ABIL_YRED_ANIMATE_CORPSE, ABIL_YRED_RECALL_UNDEAD,
ABIL_YRED_ANIMATE_DEAD, ABIL_YRED_DRAIN_LIFE,
- ABIL_YRED_CONTROL_UNDEAD },
+ ABIL_YRED_ENSLAVE_SOUL },
// Xom
{ ABIL_NON_ABILITY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
ABIL_NON_ABILITY, ABIL_NON_ABILITY },
@@ -238,7 +238,7 @@ static const ability_def Ability_List[] =
{ ABIL_YRED_RECALL_UNDEAD, "Recall Undead Slaves", 2, 0, 50, 0, ABFLAG_NONE },
{ ABIL_YRED_ANIMATE_DEAD, "Animate Dead", 3, 0, 100, 1, ABFLAG_NONE },
{ ABIL_YRED_DRAIN_LIFE, "Drain Life", 6, 0, 200, 2, ABFLAG_NONE },
- { ABIL_YRED_CONTROL_UNDEAD, "Control Undead", 5, 0, 150, 2, ABFLAG_NONE },
+ { ABIL_YRED_ENSLAVE_SOUL, "Enslave Soul", 8, 0, 150, 4, ABFLAG_NONE },
// Okawaru
{ ABIL_OKAWARU_MIGHT, "Might", 2, 0, 50, 1, ABFLAG_NONE },
@@ -696,7 +696,6 @@ static talent _get_talent(ability_type ability, bool check_confused)
case ABIL_TSO_CLEANSING_FLAME:
case ABIL_ELYVILON_RESTORATION:
- case ABIL_YRED_CONTROL_UNDEAD:
case ABIL_OKAWARU_HASTE:
case ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB:
case ABIL_LUGONU_CORRUPT:
@@ -707,6 +706,7 @@ static talent _get_talent(ability_type ability, bool check_confused)
case ABIL_ZIN_SANCTUARY:
case ABIL_TSO_SUMMON_DAEVA:
case ABIL_KIKU_INVOKE_DEATH:
+ case ABIL_YRED_ENSLAVE_SOUL:
case ABIL_ELYVILON_DIVINE_VIGOUR:
case ABIL_LUGONU_ABYSS_ENTER:
invoc = true;
@@ -1525,10 +1525,22 @@ static bool _do_ability(const ability_def& abil)
exercise(SK_INVOCATIONS, 2 + random2(4));
break;
- case ABIL_YRED_CONTROL_UNDEAD:
- mass_enchantment(ENCH_CHARM, you.skills[SK_INVOCATIONS] * 8, MHITYOU);
- exercise(SK_INVOCATIONS, 3 + random2(4));
+ case ABIL_YRED_ENSLAVE_SOUL:
+ {
+ god_acting gdact;
+
+ if (!spell_direction(spd, beam))
+ return (false);
+
+ if (!zapping(ZAP_ENSLAVE_SOUL, you.skills[SK_INVOCATIONS] * 4, beam,
+ true))
+ {
+ return (false);
+ }
+
+ exercise(SK_INVOCATIONS, 8 + random2(10));
break;
+ }
case ABIL_SIF_MUNA_CHANNEL_ENERGY:
mpr("You channel some magical energy.");
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index e1a24f427e..9a4b7d2add 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -1049,6 +1049,21 @@ const zap_info zap_data[] = {
},
{
+ ZAP_ENSLAVE_SOUL,
+ "0",
+ 100,
+ NULL,
+ NULL,
+ BLACK,
+ true,
+ BEAM_ENSLAVE_SOUL,
+ DCHAR_SPACE,
+ false,
+ false,
+ false
+ },
+
+ {
ZAP_AGONY,
"0agony",
100,
@@ -4506,6 +4521,7 @@ bool _beam_has_saving_throw(const bolt& beam)
case BEAM_HEALING:
case BEAM_INVISIBILITY:
case BEAM_DISPEL_UNDEAD:
+ case BEAM_ENSLAVE_SOUL: // it has a different saving throw
case BEAM_ENSLAVE_DEMON: // it has a different saving throw
rc = false;
break;
@@ -4533,6 +4549,11 @@ bool _ench_flavour_affects_monster(beam_type flavour, const monsters* mon)
rc = (mons_holiness(mon) == MH_UNDEAD && mon->attitude != ATT_FRIENDLY);
break;
+ case BEAM_ENSLAVE_SOUL:
+ rc = (mons_holiness(mon) == MH_NATURAL
+ && mon->attitude != ATT_FRIENDLY);
+ break;
+
case BEAM_DISPEL_UNDEAD:
rc = (mons_holiness(mon) == MH_UNDEAD);
break;
@@ -4645,6 +4666,26 @@ static mon_resist_type _affect_monster_enchantment(bolt &beam, monsters *mon)
return (MON_AFFECTED);
}
+ case BEAM_ENSLAVE_SOUL:
+#if DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS,
+ "HD: %d; pow: %d", mon->hit_dice, beam.ench_power);
+#endif
+
+ if (!mons_can_be_zombified(mon) || mons_intel(mon) < I_NORMAL)
+ {
+ simple_monster_message(mon, " is unaffected.");
+ return (MON_OTHER);
+ }
+
+ if (mon->hit_dice >= random2(beam.ench_power / 2))
+ return (MON_RESIST);
+
+ beam.obvious_effect = true;
+ mon->flags |= MF_ENSLAVED_SOUL;
+ simple_monster_message(mon, "'s soul is now ripe for the taking.");
+ return (MON_AFFECTED);
+
case BEAM_ENSLAVE_DEMON:
#if DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
@@ -5220,9 +5261,12 @@ static bool _nasty_beam(monsters *mon, const bolt &beam)
if (beam.flavour == BEAM_CHARM)
return (mons_is_holy(mon));
- // degeneration / sleep
- if (beam.flavour == BEAM_DEGENERATE || beam.flavour == BEAM_SLEEP)
+ // degeneration / sleep / enslave soul
+ if (beam.flavour == BEAM_DEGENERATE || beam.flavour == BEAM_SLEEP
+ || beam.flavour == BEAM_ENSLAVE_SOUL)
+ {
return (mons_holiness(mon) == MH_NATURAL);
+ }
// dispel undead / control undead
if (beam.flavour == BEAM_DISPEL_UNDEAD
diff --git a/crawl-ref/source/dat/descript/ability.txt b/crawl-ref/source/dat/descript/ability.txt
index a65ee033dd..6f09fbc74d 100644
--- a/crawl-ref/source/dat/descript/ability.txt
+++ b/crawl-ref/source/dat/descript/ability.txt
@@ -201,9 +201,9 @@ Drain Life
Drain the life force of the monsters surrounding you.
%%%%
-Control Undead
+Enslave Soul
-Attempt to temporarily enslave undead monsters around you.
+Mark a monster's living soul as yours, once the monster dies. The soul will retain at least some of the monster's faculties, and sometimes even the monster's equipment. However, you can only enslave one soul at any given time.
%%%%
# Okawaru
Might
diff --git a/crawl-ref/source/dat/descript/gods.txt b/crawl-ref/source/dat/descript/gods.txt
index 90502b7f24..006bee1f2c 100644
--- a/crawl-ref/source/dat/descript/gods.txt
+++ b/crawl-ref/source/dat/descript/gods.txt
@@ -84,7 +84,7 @@ Kikubaaqudgha offers the power to recall your undead followers to your near surr
%%%%
Yredelemnul powers
-Yredelemnul gives you the power to raise corpses to fight on your side, to recall your undead followers to your vicinity, and to drain the life force of living creatures. Eventually, you'll gain the power to sway hostile undead ceatures' loyalties, and make them fight for you instead. Yredelemnul will sporadically gift you with stronger undead servants. Under prayer, with sufficient piety, Yredelemnul will mirror any injury you have to suffer onto the attacker.
+Yredelemnul gives you the power to raise corpses to fight on your side, to recall your undead followers to your vicinity, and to drain the life force of living creatures. Eventually, you'll gain the power to bind a monster's soul, and make it fight for you instead. Yredelemnul will sporadically gift you with stronger undead servants. Under prayer, with sufficient piety, Yredelemnul will mirror any injury you have to suffer onto the attacker.
%%%%
Xom powers
diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc
index 4e18b8aa0f..29ec859c09 100644
--- a/crawl-ref/source/directn.cc
+++ b/crawl-ref/source/directn.cc
@@ -2847,6 +2847,17 @@ static void _describe_monster(const monsters *mon)
}
}
+ if (mons_enslaved_body_and_soul(mon))
+ {
+ mprf(MSGCH_EXAMINE, "%s soul is ripe for the taking.",
+ mon->pronoun(PRONOUN_CAP_POSSESSIVE).c_str());
+ }
+ else if (mons_enslaved_soul(mon))
+ {
+ mprf(MSGCH_EXAMINE, "%s is a disembodied soul.",
+ mon->pronoun(PRONOUN_CAP).c_str());
+ }
+
text = _mon_enchantments_string(mon);
if (!text.empty())
print_formatted_paragraph(text, numcols);
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 6856219604..941155f61b 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -66,7 +66,7 @@ enum ability_type
ABIL_YRED_RECALL_UNDEAD,
ABIL_YRED_ANIMATE_DEAD,
ABIL_YRED_DRAIN_LIFE,
- ABIL_YRED_CONTROL_UNDEAD, // 144
+ ABIL_YRED_ENSLAVE_SOUL, // 144
// 160 - reserved for Vehumet
ABIL_OKAWARU_MIGHT = 170, // 170
// Okawaru no longer heals (JPEG)
@@ -217,6 +217,7 @@ enum beam_type // beam[].flavour
BEAM_BANISH,
BEAM_DEGENERATE,
BEAM_ENSLAVE_UNDEAD,
+ BEAM_ENSLAVE_SOUL,
BEAM_PAIN,
BEAM_DISPEL_UNDEAD,
BEAM_DISINTEGRATION,
@@ -1978,31 +1979,34 @@ enum mon_flight_type
// These are now saved in an unsigned long in the monsters struct.
enum monster_flag_type
{
- MF_CREATED_FRIENDLY = 0x01, // no benefit from killing
- MF_BATTY = 0x02, // flutters like a bat
- MF_JUST_SUMMONED = 0x04, // monster skips next available action
- MF_TAKING_STAIRS = 0x08, // is following player through stairs
-
- MF_INTERESTING = 0x10, // Player finds monster interesting
- MF_SEEN = 0x20, // Player has already seen monster
- MF_DIVINE_PROTECTION = 0x40, // Monster has divine protection.
-
- MF_KNOWN_MIMIC = 0x80, // Mimic that has taken a swing at the PC,
- // or that the player has inspected with ?
- MF_BANISHED = 0x100, // Monster that has been banished.
- MF_HARD_RESET = 0x200, // Summoned, should not drop gear on reset
- MF_WAS_NEUTRAL = 0x400, // mirror to CREATED_FRIENDLY for neutrals
- MF_ATT_CHANGE_ATTEMPT = 0x800, // Saw player and attitude changed (or
- // not); currently used for holy beings
- // (good god worshippers -> neutral)
- // and orcs (Beogh worshippers -> friendly)
- MF_WAS_IN_VIEW = 0x1000, // Was in view during previous turn
- MF_BAND_MEMBER = 0x2000, // Created as a member of a band
- MF_GOT_HALF_XP = 0x4000, // Player already got half xp value earlier
- MF_HONORARY_UNDEAD = 0x8000 // Consider this monster to have MH_UNDEAD
- // holiness, regardless of its actual type;
- // currently used for abominations created
- // via Twisted Resurrection
+ MF_CREATED_FRIENDLY = 0x01, // no benefit from killing
+ MF_BATTY = 0x02, // flutters like a bat
+ MF_JUST_SUMMONED = 0x04, // monster skips next available action
+ MF_TAKING_STAIRS = 0x08, // is following player through stairs
+
+ MF_INTERESTING = 0x10, // Player finds monster interesting
+ MF_SEEN = 0x20, // Player has already seen monster
+ MF_DIVINE_PROTECTION = 0x40, // Monster has divine protection.
+
+ MF_KNOWN_MIMIC = 0x80, // Mimic that has taken a swing at the PC,
+ // or that the player has inspected with ?
+ MF_BANISHED = 0x100, // Monster that has been banished.
+ MF_HARD_RESET = 0x200, // Summoned, should not drop gear on reset
+ MF_WAS_NEUTRAL = 0x400, // mirror to CREATED_FRIENDLY for neutrals
+ MF_ATT_CHANGE_ATTEMPT = 0x800, // Saw player and attitude changed (or
+ // not); currently used for holy beings
+ // (good god worshippers -> neutral)
+ // and orcs (Beogh worshippers -> friendly)
+ MF_WAS_IN_VIEW = 0x1000, // Was in view during previous turn
+ MF_BAND_MEMBER = 0x2000, // Created as a member of a band
+ MF_GOT_HALF_XP = 0x4000, // Player already got half xp value earlier
+ MF_HONORARY_UNDEAD = 0x8000, // Consider this monster to have MH_UNDEAD
+ // holiness, regardless of its actual type;
+ // currently used for abominations created
+ // via Twisted Resurrection
+ MF_ENSLAVED_SOUL = 0x10000 // An undead monster soul enslaved by
+ // Yredelemnul's power, or the natural
+ // monster from whom the soul is taken
};
// Adding slots breaks saves. YHBW.
@@ -2962,6 +2966,7 @@ enum zap_type
ZAP_BREATHE_STICKY_FLAME,
ZAP_BREATHE_LIGHTNING,
ZAP_PETRIFY,
+ ZAP_ENSLAVE_SOUL,
ZAP_HELLFROST,
NUM_ZAPS
};
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 6744e03a99..96ec1a46a9 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -368,6 +368,101 @@ static int _scan_mon_inv_randarts(const monsters *mon,
return (ret);
}
+static int _scan_mon_inv_items(const monsters *mon,
+ bool (*item_type)(const item_def&))
+{
+ int ret = 0;
+
+ if (mons_itemuse(mon) >= MONUSE_STARTING_EQUIPMENT)
+ {
+ const int weapon = mon->inv[MSLOT_WEAPON];
+ const int second = mon->inv[MSLOT_ALT_WEAPON]; // Two-headed ogres, etc.
+ const int misc = mon->inv[MSLOT_MISCELLANY];
+ const int potion = mon->inv[MSLOT_POTION];
+ const int wand = mon->inv[MSLOT_WAND];
+ const int scroll = mon->inv[MSLOT_SCROLL];
+
+ if (weapon != NON_ITEM && mitm[weapon].base_type == OBJ_WEAPONS
+ && item_type(mitm[weapon]))
+ {
+ ret++;
+ }
+
+ if (second != NON_ITEM && mitm[second].base_type == OBJ_WEAPONS
+ && item_type(mitm[second]))
+ {
+ ret++;
+ }
+
+ if (misc != NON_ITEM && mitm[misc].base_type == OBJ_MISCELLANY
+ && item_type(mitm[misc]))
+ {
+ ret++;
+ }
+
+ if (potion != NON_ITEM && mitm[potion].base_type == OBJ_POTIONS
+ && item_type(mitm[potion]))
+ {
+ ret++;
+ }
+
+ if (wand != NON_ITEM && mitm[misc].base_type == OBJ_WANDS
+ && item_type(mitm[wand]))
+ {
+ ret++;
+ }
+
+ if (scroll != NON_ITEM && mitm[scroll].base_type == OBJ_SCROLLS
+ && item_type(mitm[scroll]))
+ {
+ ret++;
+ }
+ }
+
+ return (ret);
+}
+
+static bool _mons_has_undrinkable_potion(const monsters *mon)
+{
+ bool ret = false;
+
+ if (mons_itemuse(mon) >= MONUSE_STARTING_EQUIPMENT)
+ {
+ const int potion = mon->inv[MSLOT_POTION];
+
+ if (potion != NON_ITEM && mitm[potion].base_type == OBJ_POTIONS)
+ {
+ const potion_type ptype =
+ static_cast<potion_type>(mitm[potion].sub_type);
+
+ if (!mon->can_drink_potion(ptype))
+ ret = true;
+ }
+ }
+
+ return (ret);
+}
+
+int mons_unusable_items(const monsters *mon)
+{
+ int ret = 0;
+
+ if (mons_is_holy(mon))
+ ret += _scan_mon_inv_items(mon, is_evil_item) > 0;
+ else if (mons_is_unholy(mon))
+ {
+ ret += _scan_mon_inv_items(mon, is_holy_item) > 0;
+
+ if (mons_holiness(mon) == MH_UNDEAD
+ && _mons_has_undrinkable_potion(mon))
+ {
+ ret++;
+ }
+ }
+
+ return (ret);
+}
+
mon_holy_type mons_class_holiness(int mc)
{
ASSERT(smc);
@@ -769,6 +864,11 @@ mon_itemuse_type mons_class_itemuse(int mc)
mon_itemuse_type mons_itemuse(const monsters *mon)
{
+ if (mons_enslaved_twisted_soul(mon))
+ return (MONUSE_OPEN_DOORS);
+ else if (mons_enslaved_intact_soul(mon))
+ return (mons_class_itemuse(mons_zombie_base(mon)));
+
return (mons_class_itemuse(mon->type));
}
@@ -826,7 +926,33 @@ bool mons_class_can_be_zombified(int mc)
bool mons_can_be_zombified(const monsters *mon)
{
return (mons_class_can_be_zombified(mon->type)
- && !mons_is_summoned(mon));
+ && !mons_is_summoned(mon)
+ && !mons_enslaved_body_and_soul(mon));
+}
+
+bool mons_enslaved_body_and_soul(const monsters *mon)
+{
+ return (testbits(mon->flags, MF_ENSLAVED_SOUL)
+ && mons_holiness(mon) == MH_NATURAL);
+}
+
+bool mons_enslaved_twisted_soul(const monsters *mon)
+{
+ return (testbits(mon->flags, MF_ENSLAVED_SOUL)
+ && (mon->type == MONS_ABOMINATION_SMALL
+ || mon->type == MONS_ABOMINATION_LARGE));
+}
+
+bool mons_enslaved_intact_soul(const monsters *mon)
+{
+ return (testbits(mon->flags, MF_ENSLAVED_SOUL)
+ && mon->type == MONS_SPECTRAL_THING);
+}
+
+bool mons_enslaved_soul(const monsters *mon)
+{
+ return (mons_enslaved_twisted_soul(mon)
+ || mons_enslaved_intact_soul(mon));
}
int downscale_zombie_damage(int damage)
@@ -2055,6 +2181,11 @@ mon_intel_type mons_class_intel(int mc)
mon_intel_type mons_intel(const monsters *mon)
{
+ if (mons_enslaved_twisted_soul(mon))
+ return (I_NORMAL);
+ else if (mons_enslaved_intact_soul(mon))
+ return (mons_class_intel(mons_zombie_base(mon)));
+
return (mons_class_intel(mon->type));
}
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index e653674afe..a56be52e68 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -523,6 +523,7 @@ corpse_effect_type mons_corpse_effect(int mc);
* *********************************************************************** */
bool mons_class_flag(int mc, int bf);
+int mons_unusable_items(const monsters *mon);
// last updated 12may2000 {dlb}
/* ***********************************************************************
@@ -606,6 +607,10 @@ bool mons_class_is_zombified(int mc);
bool mons_is_zombified(const monsters *monster);
bool mons_class_can_be_zombified(int mc);
bool mons_can_be_zombified(const monsters *mon);
+bool mons_enslaved_body_and_soul(const monsters *mon);
+bool mons_enslaved_twisted_soul(const monsters *mon);
+bool mons_enslaved_intact_soul(const monsters *mon);
+bool mons_enslaved_soul(const monsters *mon);
// last updated 12may2000 {dlb}
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index ff42d7d438..48ad39c81e 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -254,9 +254,8 @@ bool curse_an_item( bool decay_potions, bool quiet )
return (true);
}
-static void _monster_drop_ething(monsters *monster,
- bool mark_item_origins = false,
- int owner_id = NON_ITEM)
+void monster_drop_ething(monsters *monster, bool mark_item_origins,
+ int owner_id)
{
const bool hostile_grid = grid_destroys_items(grd(monster->pos()));
const int midx = (int) monster_index(monster);
@@ -310,8 +309,11 @@ static void _place_monster_corpse(const monsters *monster)
corpse_class = MONS_GLOWING_SHAPESHIFTER;
// Doesn't leave a corpse.
- if (mons_weight(corpse_class) == 0 || coinflip())
+ if (mons_weight(corpse_class) == 0 || mons_enslaved_body_and_soul(monster)
+ || coinflip())
+ {
return;
+ }
int o = get_item_slot();
if (o == NON_ITEM)
@@ -623,6 +625,15 @@ static bool _monster_avoided_death(monsters *monster, killer_type killer, int i)
}
}
+ // Yredelemnul special
+ if (you.religion == GOD_YREDELEMNUL && mons_enslaved_body_and_soul(monster)
+ && mons_near(monster) && killer != KILL_RESET
+ && killer != KILL_DISMISSED)
+ {
+ yred_make_enslaved_soul(monster, player_under_penance());
+ return (true);
+ }
+
return (false);
}
@@ -782,7 +793,8 @@ void monster_die(monsters *monster, killer_type killer,
const int monster_killed = monster_index(monster);
const bool hard_reset = testbits(monster->flags, MF_HARD_RESET);
- const bool gives_xp = !mons_is_summoned(monster);
+ const bool gives_xp = !mons_is_summoned(monster)
+ && !mons_enslaved_body_and_soul(monster);
bool in_transit = false;
bool drop_items = !hard_reset && !mons_is_holy(monster);
@@ -1364,7 +1376,7 @@ void monster_die(monsters *monster, killer_type killer,
const coord_def mwhere = monster->pos();
if (drop_items)
- _monster_drop_ething(monster, YOU_KILL(killer) || pet_kill);
+ monster_drop_ething(monster, YOU_KILL(killer) || pet_kill);
monster_cleanup(monster);
// Force redraw for monsters that die.
@@ -1684,7 +1696,7 @@ bool monster_polymorph(monsters *monster, monster_type targetc,
monster->speed_increment = 67 + random2(6);
- _monster_drop_ething(monster);
+ monster_drop_ething(monster);
// New monster type might be interesting.
mark_interesting_monst(monster);
diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h
index cce7636b59..f1e2349bbc 100644
--- a/crawl-ref/source/monstuff.h
+++ b/crawl-ref/source/monstuff.h
@@ -108,6 +108,10 @@ void behaviour_event(monsters *mon, int event_type, int src = MHITNOT,
* *********************************************************************** */
bool curse_an_item(bool decay_potions, bool quiet = false);
+
+void monster_drop_ething(monsters *monster, bool mark_item_origins = false,
+ int owner_id = NON_ITEM);
+
/* ***********************************************************************
* called from: fight
* *********************************************************************** */
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index 3d6508170f..7fbc442f75 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -213,7 +213,7 @@ const char* god_gain_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"recall your undead slaves",
"animate legions of the dead",
"drain ambient lifeforce",
- "control the undead" },
+ "enslave living souls" },
// Xom
{ "", "", "", "", "" },
// Vehumet
@@ -299,7 +299,7 @@ const char* god_lose_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"recall your undead slaves",
"animate legions of the dead",
"drain ambient lifeforce",
- "control the undead" },
+ "enslave living souls" },
// Xom
{ "", "", "", "", "" },
// Vehumet
@@ -1159,6 +1159,16 @@ bool mons_is_god_gift(const monsters *mon, god_type god)
return (mon->god == god);
}
+static bool _is_yred_enslaved_body_and_soul(const monsters* mon)
+{
+ return (mon->alive() && mons_enslaved_body_and_soul(mon));
+}
+
+static bool _is_yred_enslaved_soul(const monsters* mon)
+{
+ return (mon->alive() && mons_enslaved_soul(mon));
+}
+
bool is_yred_undead_slave(const monsters* mon)
{
return (mon->alive() && mons_holiness(mon) == MH_UNDEAD
@@ -4610,6 +4620,34 @@ static bool _make_god_gifts_hostile(bool level_only)
return (apply_to_all_dungeons(_god_gifts_hostile_wrapper) || success);
}
+static bool _yred_enslaved_souls_on_level_disappear()
+{
+ bool success = false;
+
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *monster = &menv[i];
+ if (_is_yred_enslaved_soul(monster))
+ {
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Undead soul disappearing: %s on level %d, branch %d",
+ monster->name(DESC_PLAIN).c_str(),
+ static_cast<int>(you.your_level),
+ static_cast<int>(you.where_are_you));
+#endif
+
+ simple_monster_message(monster, " is freed.");
+
+ // The monster disappears.
+ monster_die(monster, KILL_DISMISSED, NON_MONSTER);
+
+ success = true;
+ }
+ }
+
+ return (success);
+}
+
static bool _yred_undead_slaves_on_level_abandon_you()
{
bool success = false;
@@ -4617,7 +4655,20 @@ static bool _yred_undead_slaves_on_level_abandon_you()
for (int i = 0; i < MAX_MONSTERS; ++i)
{
monsters *monster = &menv[i];
- if (is_yred_undead_slave(monster))
+ if (_is_yred_enslaved_body_and_soul(monster))
+ {
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Undead soul abandoning: %s on level %d, branch %d",
+ monster->name(DESC_PLAIN).c_str(),
+ static_cast<int>(you.your_level),
+ static_cast<int>(you.where_are_you));
+#endif
+
+ yred_make_enslaved_soul(monster, true, true, false);
+
+ success = true;
+ }
+ else if (is_yred_undead_slave(monster))
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Undead abandoning: %s on level %d, branch %d",
@@ -4664,6 +4715,11 @@ static bool _orcish_followers_on_level_abandon_you()
return (success);
}
+static bool _yred_souls_disappear()
+{
+ return (apply_to_all_dungeons(_yred_enslaved_souls_on_level_disappear));
+}
+
// Upon excommunication, ex-Yredelemnulites lose all their undead
// slaves. When under penance, Yredelemnulites can lose all undead
// slaves in sight.
@@ -4685,7 +4741,8 @@ static bool _yred_slaves_abandon_you()
monsters *monster = &menv[mgrd(*ri)];
- if (is_yred_undead_slave(monster))
+ if (_is_yred_enslaved_body_and_soul(monster)
+ || is_yred_undead_slave(monster))
{
num_slaves++;
@@ -4699,9 +4756,15 @@ static bool _yred_slaves_abandon_you()
continue;
}
- monster->attitude = ATT_HOSTILE;
- behaviour_event(monster, ME_ALERT, MHITYOU);
- // For now CREATED_FRIENDLY stays.
+
+ if (_is_yred_enslaved_body_and_soul(monster))
+ yred_make_enslaved_soul(monster, true, true, false);
+ else
+ {
+ monster->attitude = ATT_HOSTILE;
+ behaviour_event(monster, ME_ALERT, MHITYOU);
+ // For now CREATED_FRIENDLY stays.
+ }
num_reclaim++;
@@ -4899,6 +4962,75 @@ void good_god_holy_attitude_change(monsters *holy)
behaviour_event(holy, ME_ALERT, MHITNOT);
}
+void yred_make_enslaved_soul(monsters *mon, bool force_hostile,
+ bool quiet, bool allow_fail)
+{
+ if (allow_fail)
+ _yred_souls_disappear();
+
+ const int type = mon->type;
+ const std::string name_cap_the = mon->name(DESC_CAP_THE);
+ const std::string name_plain = mon->name(DESC_PLAIN);
+ bool twisted = false;
+
+ if ((allow_fail || mons_unusable_items(mon) > 0) && coinflip())
+ twisted = true;
+
+ if (!quiet)
+ {
+ mprf("%s's soul %s.", name_cap_the.c_str(),
+ twisted ? "becomes twisted" : "remains intact");
+ }
+
+ if (twisted)
+ {
+ mon->type = mons_zombie_size(type) == Z_BIG ?
+ MONS_ABOMINATION_LARGE : MONS_ABOMINATION_SMALL;
+ mon->base_monster = mons_species(mon->type);
+ }
+ else
+ {
+ mon->type = MONS_SPECTRAL_THING;
+ mon->base_monster = mons_species(type);
+ }
+
+ define_monster(*mon);
+
+ if (twisted)
+ {
+ // Mark abominations as undead.
+ mon->flags |= MF_HONORARY_UNDEAD;
+
+ monster_drop_ething(mon);
+ }
+
+ mon->flags |= MF_ENSLAVED_SOUL;
+
+ if (mons_is_unique(type))
+ {
+ mon->mname = name_plain;
+
+ // Special case for Blork the orc: shorten his name to "Blork"
+ // to avoid mentions of "Blork the orc the spectral orc" or
+ // "Blork the orc the small abomination".
+ if (type == MONS_BLORK_THE_ORC)
+ mon->mname = "Blork";
+ }
+
+ // Wow, permanent enslaving!
+ mon->attitude = !force_hostile ? ATT_FRIENDLY : ATT_HOSTILE;
+ mon->flags |= MF_CREATED_FRIENDLY;
+ behaviour_event(mon, ME_ALERT, !force_hostile ? MHITNOT : MHITYOU);
+
+ mons_make_god_gift(mon, GOD_YREDELEMNUL);
+
+ if (!quiet)
+ {
+ mprf("%s's soul %s.", name_cap_the.c_str(),
+ !force_hostile ? "is now yours" : "fights you");
+ }
+}
+
static void _print_converted_orc_speech(const std::string key,
monsters *mon,
msg_channel_type channel)
diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h
index 011a85d752..3b2731039b 100644
--- a/crawl-ref/source/religion.h
+++ b/crawl-ref/source/religion.h
@@ -100,6 +100,8 @@ bool yred_injury_mirror(bool actual = true);
bool beogh_water_walk();
void beogh_idol_revenge();
void good_god_holy_attitude_change(monsters *holy);
+void yred_make_enslaved_soul(monsters *mon, bool force_hostile = false,
+ bool quiet = false, bool allow_fail = true);
void beogh_convert_orc(monsters *orc, bool emergency,
bool converted_by_follower = false);
bool is_holy_item(const item_def& item);