summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJude Brown <bookofjude@users.sourceforge.net>2009-12-24 20:34:29 +1000
committerJude Brown <bookofjude@users.sourceforge.net>2009-12-24 21:17:11 +1000
commit9fcc7c41761ea968d0da075f2974a8908a2ef19b (patch)
tree85c8495c846722906b4832ba3d5de430532d95f6
parentbcc249f77c1459b75b7a4f96b018a15569e675b3 (diff)
downloadcrawl-ref-9fcc7c41761ea968d0da075f2974a8908a2ef19b.tar.gz
crawl-ref-9fcc7c41761ea968d0da075f2974a8908a2ef19b.zip
New (rakshasa) unique: Mara, Lord of Ilusions.
This re-uses several ideas from the Emerald Eye FR, and implements the spell "Mislead": this grants the "Misled" status effect to the player, and causes on-level monsters to appear (glyph, name and tile) to be other monsters. Only their appearance is changed. Healing potions and rain remove this status effect. Mara also has a beefed-up version of the Rakshasa clone spell: it instead creates an "exact" clone (slightly altered spells list, and non-unique status for his clones). Only two will ever be created at once. Finally, he has the spell "Summon Player Ghost". This spell creates a ghost of the player (though marked as a summon, meaning that it (shouldn't) register a milestone, though it will be treated as a self-ghost kill) that is hostile. Only one of these should be existant at any one point in time. I'm pretty sure that I haven't broken anything, but would definitely appreciate someone sanity-checking this commit. Known minor issue: Kirke's summon ugly things being cast while having the Misled status causes them to show up with no glyph.
-rw-r--r--crawl-ref/source/cloud.cc6
-rw-r--r--crawl-ref/source/dat/database/monspeak.txt50
-rw-r--r--crawl-ref/source/dat/database/monspell.txt24
-rw-r--r--crawl-ref/source/dat/database/quotes.txt10
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt4
-rw-r--r--crawl-ref/source/describe.cc24
-rw-r--r--crawl-ref/source/directn.cc11
-rw-r--r--crawl-ref/source/effects.cc7
-rw-r--r--crawl-ref/source/enum.h9
-rw-r--r--crawl-ref/source/hiscores.cc4
-rw-r--r--crawl-ref/source/it_use2.cc1
-rw-r--r--crawl-ref/source/kills.cc7
-rw-r--r--crawl-ref/source/main.cc15
-rw-r--r--crawl-ref/source/mon-abil.cc2
-rw-r--r--crawl-ref/source/mon-cast.cc249
-rw-r--r--crawl-ref/source/mon-cast.h1
-rw-r--r--crawl-ref/source/mon-data.h26
-rw-r--r--crawl-ref/source/mon-gear.cc8
-rw-r--r--crawl-ref/source/mon-info.cc39
-rw-r--r--crawl-ref/source/mon-spll.h22
-rw-r--r--crawl-ref/source/mon-util.cc14
-rw-r--r--crawl-ref/source/monster.cc54
-rw-r--r--crawl-ref/source/monster.h1
-rw-r--r--crawl-ref/source/output.cc3
-rw-r--r--crawl-ref/source/player.cc5
-rw-r--r--crawl-ref/source/player.h1
-rw-r--r--crawl-ref/source/rltiles/dc-mon.txt5
-rw-r--r--crawl-ref/source/rltiles/dc-mon/unique/mara.pngbin0 -> 1430 bytes
-rw-r--r--crawl-ref/source/show.cc3
-rw-r--r--crawl-ref/source/showsymb.cc11
-rw-r--r--crawl-ref/source/spl-data.h178
-rw-r--r--crawl-ref/source/tags.h2
-rw-r--r--crawl-ref/source/tilepick.cc8
33 files changed, 680 insertions, 124 deletions
diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc
index 9678edc16c..638432f504 100644
--- a/crawl-ref/source/cloud.cc
+++ b/crawl-ref/source/cloud.cc
@@ -765,6 +765,12 @@ void in_a_cloud()
if (you.duration[DUR_FIRE_SHIELD])
you.duration[DUR_FIRE_SHIELD] = 1;
+ if (you.misled())
+ {
+ mpr("The rain washes away illusions!", MSGCH_DURATION);
+ you.duration[DUR_MISLED] = 0;
+ }
+
mpr("You are standing in the rain.");
break;
diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt
index 1b63fbcbcd..a9380051ec 100644
--- a/crawl-ref/source/dat/database/monspeak.txt
+++ b/crawl-ref/source/dat/database/monspeak.txt
@@ -2552,6 +2552,56 @@ Louise
@_mercenary_guard_@
### END Louise
%%%%
+############ MARA ### A powerful demon lord, Lord of Illusions
+Mara
+
+@_Mara_common_@
+
+w:5
+
+@_Mara_rare_@
+%%%%
+_Mara_common_
+
+VISUAL:@The_monster@'s face changes twice.
+
+@The_monster@ says, "I mislead the hearts of men!"
+
+@The_monster@ says, "What good are steel and magic against my illusions?"
+
+# Buddhism
+@The_monster@ says, "Entranced by some poetic flight, are you?"
+
+@The_monster@ says, "Bound art thou by all the snares!"
+
+# And original
+@The_monster@ growls.
+
+VISUAL SPELL:@The_monster@ gestures.
+
+VISUAL SPELL:@The_monster@ points ostentatiously.
+
+SPELL:@The_monster@ weaves an illusion.
+%%%%
+_Mara_rare_
+
+# Lord of Light
+@The_monster@ screams, "Tenfold be your damnation!"
+
+@The_monster@'s features momentarily become yours. "Wouldst slay thyself?"
+
+@The_monster@ says, "I stand as high above you as a star above the ocean's bottom."
+
+@The_monster@ smirks. "I am one who seeks the Path and the Right."
+
+@The_monster@ says, "Thou art lean, ill-favoured, death is in thy neighbourhood!"
+
+@The_monster@ says, "Difficult is the way of exertion, difficult to pass, difficult to enter upon."
+
+@The_monster@ calls, "Stop! In seven days from now the wheel of empire will appear!"
+
+@The_monster@ boasts, "I am lord over the four continents and the two thousand adjacent isles."
+%%%%
############ MARGERY ### A powerful sorceress, guarding the ORB
Margery
diff --git a/crawl-ref/source/dat/database/monspell.txt b/crawl-ref/source/dat/database/monspell.txt
index 0ce22a12e5..c1556fcc50 100644
--- a/crawl-ref/source/dat/database/monspell.txt
+++ b/crawl-ref/source/dat/database/monspell.txt
@@ -85,6 +85,30 @@ Greater Healing cast
@The_monster@ briefly glows brightly.
%%%%
+Mislead cast
+
+@The_monster@ weaves an illusion.
+
+@The_monster@ misleads you.
+
+@The_monster@ clouds your mind.
+%%%%
+unseen Mislead cast
+
+Something tries to mislead you!
+
+Something weaves an illusion around you!
+%%%%
+Mara Summon cast
+
+@The_monster@ weaves an illusion.
+
+@The_monster@ shimmers.
+%%%%
+unseen Mara summon cast
+
+Something weaves an illusion!
+%%%%
Symbol of Torment cast
@The_monster@ calls on the powers of Hell!
diff --git a/crawl-ref/source/dat/database/quotes.txt b/crawl-ref/source/dat/database/quotes.txt
index 436eb7ab71..0f0e8d5920 100644
--- a/crawl-ref/source/dat/database/quotes.txt
+++ b/crawl-ref/source/dat/database/quotes.txt
@@ -756,6 +756,16 @@ dreadful nostrils and making ring waves around him, which can reach many
miles. Could one doubt that this is the Leviathan of Job?"
-Jacob Wallenberg, "Min son på galejan", 1781.
%%%%
+Mara
+
+"This night the Lord of Illusion passed among you, Mara, mighty among
+ dreamers, mighty for ill. He did come upon another who may work with the
+ stuff of dreams in a different way. He did meet with Dharma, who may expel
+ a dreamer from his dream. They did struggle, and the Lord Mara is no more.
+ Why did they struggle, deathgod against illusionist? You say their ways are
+ incomprehensible, being the ways of gods. This is not the answer."
+ -Roger Zelazny, "Lord of Light", 1967
+%%%%
Menkaure
"Ye men of Egypt, ye have heard your king!
diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt
index 131c577bdb..da05efc32f 100644
--- a/crawl-ref/source/dat/descript/monsters.txt
+++ b/crawl-ref/source/dat/descript/monsters.txt
@@ -1042,6 +1042,10 @@ manticore
A hideous cross-breed, bearing the features of a human and a lion, with great bat-like wings. Its tail bristles with spikes that can be loosed at potential prey.
%%%%
+Mara
+
+This tall and powerful demon is Mara, Lord of Illusions, mighty among dreamers. He is capable of creating intricately detailed illusions, able to mislead even the minds of the mightiest and most brilliant spell-casters.
+%%%%
merfolk
Half fish, half man, the merfolk are citizens of both water and land, and they'll fiercely protect their chosen territory.
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 8846f838ea..95473fc4a1 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -2731,16 +2731,21 @@ static std::string _monster_stat_description(const monsters& mon)
<< ".$";
}
- // Magic resistance at MAG_IMMUNE.
- if (mons_immune_magic(&mon))
- result << pronoun << " is immune to magical enchantments.$";
- else // How resistant is it? Same scale as the player.
+ // Magic resistance at MAG_IMMUNE, but not for Rs, as there is then
+ // too much information leak.
+ if (mon.type != MONS_RAKSHASA && mon.type != MONS_MARA
+ && mon.type != MONS_RAKSHASA_FAKE && mon.type != MONS_MARA_FAKE)
{
- const int mr = mon.res_magic();
- if (mr >= 10)
+ if (mons_immune_magic(&mon))
+ result << pronoun << " is immune to magical enchantments.$";
+ else // How resistant is it? Same scale as the player.
{
- result << pronoun << make_stringf(" is %s resistant to magic.$",
- magic_res_adjective(mr).c_str());
+ const int mr = mon.res_magic();
+ if (mr >= 10)
+ {
+ result << pronoun << make_stringf(" is %s resistant to magic.$",
+ magic_res_adjective(mr).c_str());
+ }
}
}
@@ -2929,7 +2934,8 @@ void get_monster_db_desc(const monsters& mons, describe_info &inf,
<< " is incapable of using stairs.$";
}
- if (mons.is_summoned() && mons.type != MONS_RAKSHASA_FAKE)
+ if (mons.is_summoned() && (mons.type != MONS_RAKSHASA_FAKE
+ && mons.type != MONS_MARA_FAKE))
{
inf.body << "$" << "This monster has been summoned, and is thus only "
"temporary. Killing it yields no experience, nutrition "
diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc
index 1526fd6255..518d21b23c 100644
--- a/crawl-ref/source/directn.cc
+++ b/crawl-ref/source/directn.cc
@@ -3140,8 +3140,11 @@ static std::string _get_monster_desc(const monsters *mon)
else if (mon->neutral()) // don't differentiate between permanent or not
text += pronoun + " is indifferent to you.\n";
- if (mon->is_summoned() && mon->type != MONS_RAKSHASA_FAKE)
+ if (mon->is_summoned() && (mon->type != MONS_RAKSHASA_FAKE
+ && mon->type != MONS_MARA_FAKE))
+ {
text += pronoun + " has been summoned.\n";
+ }
if (mon->haloed())
text += pronoun + " is illuminated by a divine halo.\n";
@@ -3242,7 +3245,8 @@ std::string get_monster_equipment_desc(const monsters *mon, bool full_desc,
std::string weap = "";
// We don't report rakshasa equipment in order not to give away the
- // true rakshasa when it summons.
+ // true rakshasa when it summons. But Mara is fine, because his weapons
+ // and armour are cloned with him.
if (mon->type != MONS_DANCING_WEAPON
&& (mon->type != MONS_RAKSHASA || mon->friendly()))
@@ -3258,7 +3262,8 @@ std::string get_monster_equipment_desc(const monsters *mon, bool full_desc,
}
// Print the rest of the equipment only for full descriptions.
- if (full_desc && (mon->type != MONS_RAKSHASA || mon->friendly()))
+ if (full_desc && ((mon->type != MONS_RAKSHASA && mon->type != MONS_MARA
+ && mon->type != MONS_MARA_FAKE) || mon->friendly()))
{
const int mon_arm = mon->inv[MSLOT_ARMOUR];
const int mon_shd = mon->inv[MSLOT_SHIELD];
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index e9894917b0..30687dd58d 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -937,6 +937,13 @@ void direct_effect(monsters *source, spell_type spell,
mons_cast_haunt(source);
break;
+ case SPELL_MISLEAD:
+ if (!def)
+ mons_cast_mislead(source);
+ else
+ defender->confuse(source, source->hit_dice * 12);
+ break;
+
default:
ASSERT(false);
}
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 94d2b6328d..322a67c270 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1203,6 +1203,7 @@ enum duration_type
DUR_SLIMIFY,
DUR_TIME_STEP,
DUR_ICEMAIL_DEPLETED, // Wait this many turns for full Icemail
+ DUR_MISLED,
NUM_DURATIONS
};
@@ -2060,6 +2061,8 @@ enum monster_type // (int) menv[].type
MONS_CRAZY_YIUF,
MONS_SLAVE,
MONS_GIANT_LEECH,
+ MONS_MARA,
+ MONS_MARA_FAKE,
// Testing monsters
MONS_TEST_SPAWNER,
@@ -2314,6 +2317,8 @@ enum mon_spellbook_type
MST_AIZUL,
MST_EXECUTIONER,
MST_HAROLD,
+ MST_MARA,
+ MST_MARA_FAKE,
MST_TEST_SPAWNER = 200,
NUM_MSTYPES,
@@ -2912,6 +2917,10 @@ enum spell_type
SPELL_BLINK_CLOSE,
SPELL_BLINK_RANGE,
SPELL_BLINK_AWAY,
+ SPELL_MISLEAD,
+ SPELL_FAKE_MARA_SUMMON,
+ SPELL_SUMMON_RAKSHASA,
+ SPELL_SUMMON_PLAYER_GHOST,
NUM_SPELLS
};
diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc
index 4fa9d16826..a64b191e73 100644
--- a/crawl-ref/source/hiscores.cc
+++ b/crawl-ref/source/hiscores.cc
@@ -1200,6 +1200,10 @@ std::string scorefile_entry::death_source_desc() const
return ("");
}
+ // XXX: Deals specially with Mara's clones.
+ if (death_source == MONS_MARA_FAKE)
+ return ("an illusion of Mara");
+
// XXX no longer handles mons_num correctly! FIXME
return (!death_source_name.empty() ?
death_source_name : mons_type_name(death_source, DESC_NOCAP_A));
diff --git a/crawl-ref/source/it_use2.cc b/crawl-ref/source/it_use2.cc
index bc916fe007..ccca70a29d 100644
--- a/crawl-ref/source/it_use2.cc
+++ b/crawl-ref/source/it_use2.cc
@@ -76,6 +76,7 @@ bool potion_effect(potion_type pot_eff, int pow, bool drank_it, bool was_known)
you.rotting = 0;
you.disease = 0;
you.duration[DUR_CONF] = 0;
+ you.duration[DUR_MISLED] = 0;
break;
case POT_HEAL_WOUNDS:
diff --git a/crawl-ref/source/kills.cc b/crawl-ref/source/kills.cc
index fa7e0a3924..f0ea8ee3f0 100644
--- a/crawl-ref/source/kills.cc
+++ b/crawl-ref/source/kills.cc
@@ -280,7 +280,7 @@ void Kills::merge(const Kills &k)
void Kills::record_kill(const struct monsters *mon)
{
// Handle player ghosts separately.
- if (mon->type == MONS_PLAYER_GHOST)
+ if (mon->type == MONS_PLAYER_GHOST && !mon->is_summoned())
{
record_ghost_kill(mon);
return ;
@@ -533,6 +533,9 @@ std::string kill_def::base_name(const kill_monster_desc &md) const
case MONS_RAKSHASA_FAKE:
name = "illusory " + name;
break;
+ case MONS_MARA_FAKE:
+ name = "illusory " + name;
+ break;
}
return name;
}
@@ -622,7 +625,7 @@ kill_ghost::kill_ghost(const monsters *mon)
// Check whether this is really a ghost, since we also have to handle
// the Pandemonic demons.
- if (mon->type == MONS_PLAYER_GHOST)
+ if (mon->type == MONS_PLAYER_GHOST && !mon->is_summoned())
ghost_name = "The ghost of " + get_ghost_description(*mon, true);
}
diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc
index d3feb08c00..c1b4c6d615 100644
--- a/crawl-ref/source/main.cc
+++ b/crawl-ref/source/main.cc
@@ -1296,6 +1296,12 @@ static void _go_upstairs()
if (_marker_vetoes_stair())
return;
+ if (you.duration[DUR_MISLED])
+ {
+ mpr("Away from their source, illusions no longer mislead you.", MSGCH_DURATION);
+ you.duration[DUR_MISLED] = 0;
+ }
+
tag_followers(); // Only those beside us right now can follow.
start_delay(DELAY_ASCENDING_STAIRS,
1 + (you.burden_state > BS_UNENCUMBERED));
@@ -1367,6 +1373,12 @@ static void _go_downstairs()
if (!check_annotation_exclusion_warning())
return;
+ if (you.duration[DUR_MISLED])
+ {
+ mpr("Away from their source, illusions no longer mislead you.", MSGCH_DURATION);
+ you.duration[DUR_MISLED] = 0;
+ }
+
if (shaft)
{
start_delay( DELAY_DESCENDING_STAIRS, 0, you.your_level );
@@ -2427,6 +2439,7 @@ static void _decrement_durations()
_decrement_a_duration(DUR_LOWERED_MR, delay, "You feel more resistant to magic.");
_decrement_a_duration(DUR_SLIMIFY, delay, "You feel less slimy.",
coinflip(), "Your slime is starting to congeal.");
+ _decrement_a_duration(DUR_MISLED, delay, "Your thoughts are your own once more.");
if (you.duration[DUR_PARALYSIS] || you.petrified())
{
@@ -4618,7 +4631,7 @@ static void _compile_time_asserts()
COMPILE_CHECK(SP_VAMPIRE == 30 , c3);
COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4);
COMPILE_CHECK(SPELL_RETURNING_AMMUNITION == 162 , c5);
- COMPILE_CHECK(NUM_SPELLS == 211 , c6);
+ COMPILE_CHECK(NUM_SPELLS == 215 , c6);
//jmf: NEW ASSERTS: we ought to do a *lot* of these
COMPILE_CHECK(NUM_SPECIES < SP_UNKNOWN , c7);
diff --git a/crawl-ref/source/mon-abil.cc b/crawl-ref/source/mon-abil.cc
index 32a764bcb1..5325d3f27b 100644
--- a/crawl-ref/source/mon-abil.cc
+++ b/crawl-ref/source/mon-abil.cc
@@ -1043,6 +1043,8 @@ bool mon_special_ability(monsters *monster, bolt & beem)
case MONS_BLINK_FROG:
case MONS_KILLER_KLOWN:
case MONS_PRINCE_RIBBIT:
+ case MONS_MARA:
+ case MONS_MARA_FAKE:
case MONS_GOLDEN_EYE:
if (one_chance_in(7) || monster->caught() && one_chance_in(3))
used = monster_blink(monster);
diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc
index 8a6397d99f..761521f7ab 100644
--- a/crawl-ref/source/mon-cast.cc
+++ b/crawl-ref/source/mon-cast.cc
@@ -16,11 +16,14 @@
#include "fprop.h"
#include "fight.h"
#include "ghost.h"
+#include "items.h"
#include "misc.h"
+#include "message.h"
#include "mon-behv.h"
#include "mon-iter.h"
#include "mon-place.h"
#include "terrain.h"
+#include "tutorial.h"
#include "mgen_data.h"
#include "coord.h"
#include "mon-speak.h"
@@ -39,6 +42,7 @@
#include "teleport.h"
#include "view.h"
#include "viewchar.h"
+#include "xom.h"
static bool _valid_mon_spells[NUM_SPELLS];
@@ -727,7 +731,8 @@ static bool _los_free_spell(spell_type spell_cast)
|| spell_cast == SPELL_SMITING
|| spell_cast == SPELL_HAUNT
|| spell_cast == SPELL_FIRE_STORM
- || spell_cast == SPELL_AIRSTRIKE);
+ || spell_cast == SPELL_AIRSTRIKE
+ || spell_cast == SPELL_MISLEAD);
}
// Set up bolt structure for monster spell casting.
@@ -762,6 +767,7 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
case SPELL_BRAIN_FEED:
pbolt.type = DMNBM_BRAIN_FEED;
return (true);
+ case SPELL_MISLEAD:
case SPELL_SMITING:
case SPELL_AIRSTRIKE:
pbolt.type = DMNBM_SMITING;
@@ -781,6 +787,9 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
case SPELL_VAMPIRE_SUMMON:
case SPELL_SHADOW_CREATURES: // summon anything appropriate for level
case SPELL_FAKE_RAKSHASA_SUMMON:
+ case SPELL_FAKE_MARA_SUMMON:
+ case SPELL_SUMMON_PLAYER_GHOST:
+ case SPELL_SUMMON_RAKSHASA:
case SPELL_SUMMON_DEMON:
case SPELL_SUMMON_UGLY_THING:
case SPELL_ANIMATE_DEAD:
@@ -814,6 +823,7 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
case SPELL_CHAIN_LIGHTNING: // the only user is reckless
case SPELL_SUMMON_EYEBALLS:
case SPELL_SUMMON_BUTTERFLIES:
+ case SPELL_MISLEAD:
return (true);
default:
if (check_validity)
@@ -1542,6 +1552,130 @@ void mons_cast_haunt(monsters *monster)
_pick_random_wraith, random_range(3, 6), GOD_NO_GOD, &fpos);
}
+int _count_mara_fakes()
+{
+ int count = 0;
+ for (monster_iterator mi; mi; ++mi)
+ {
+ if (mi->type == MONS_MARA_FAKE)
+ count++;
+ }
+
+ return count;
+}
+
+bool _unsuitable_misled_monster(monster_type mons)
+{
+ return (mons_is_unique(mons) || mons_is_mimic(mons)
+ || mons_class_is_stationary(mons) || mons_genus(mons) == MONS_DRACONIAN
+ || mons == MONS_DANCING_WEAPON || mons == MONS_UGLY_THING
+ || mons == MONS_VERY_UGLY_THING || mons == MONS_ZOMBIE_SMALL
+ || mons == MONS_ZOMBIE_LARGE || mons == MONS_SKELETON_SMALL
+ || mons == MONS_SKELETON_LARGE || mons == MONS_SIMULACRUM_SMALL
+ || mons == MONS_SIMULACRUM_LARGE || mons == MONS_SPECTRAL_THING
+ || mons == MONS_SLIME_CREATURE || mons == MONS_BALLISTOMYCETE
+ || mons == MONS_HYDRA || mons == MONS_PLAYER_GHOST
+ || mons == MONS_SHAPESHIFTER || mons == MONS_PANDEMONIUM_DEMON
+ || mons == MONS_KILLER_KLOWN || mons == MONS_KRAKEN
+ || mons == MONS_KRAKEN_TENTACLE
+ || mons == MONS_GLOWING_SHAPESHIFTER);
+}
+
+monster_type _get_misled_monster (monsters *monster)
+{
+ monster_type mons = random_monster_at_grid(monster->pos());
+ if (_unsuitable_misled_monster(mons))
+ mons = random_monster_at_grid(monster->pos());
+
+ if (_unsuitable_misled_monster(mons))
+ return (MONS_GIANT_BAT);
+
+ return mons;
+}
+
+int _update_mislead_monsters(monsters* monster)
+{
+ int count = 0;
+
+ for (monster_iterator mi; mi; ++mi)
+ {
+ if (*mi == monster)
+ continue;
+
+ // Don't affect uniques, named monsters, and monsters with special tiles.
+ if (mons_is_unique(mi->type) || !mi->mname.empty()
+ || mi->props.exists("monster_tile") || mi->props.exists("mislead_as"))
+ {
+ continue;
+ }
+ else
+ {
+ mi->props["mislead_as"] = short(_get_misled_monster(*mi));
+ count++;
+ }
+ }
+
+ return count;
+}
+
+void mons_cast_mislead(monsters *monster)
+{
+ // This really only affects the player; it causes confusion when cast on
+ // non-player foes, but that is dealt with inside ye-great-Switch-of-Doom.
+ if (monster->foe != MHITYOU)
+ return;
+
+ // Prevents Mislead spam by Mara and co. {due}
+ if (you.duration[DUR_MISLED] > 10 && one_chance_in(3))
+ return;
+
+ if (wearing_amulet(AMU_CLARITY))
+ {
+ mpr("Your vision blurs momentarily.");
+ return;
+ }
+
+ _update_mislead_monsters(monster);
+
+ const int old_value = you.duration[DUR_MISLED];
+ you.increase_duration(DUR_MISLED, monster->hit_dice * 12 / 3, 50);
+ if (you.duration[DUR_MISLED] > old_value)
+ {
+ you.check_awaken(500);
+
+ if (old_value <= 0)
+ {
+ mpr("But for a moment, strange images dance in front of your eyes.", MSGCH_WARN);
+#ifdef USE_TILE
+ tiles.add_overlay(you.pos(), tileidx_zap(MAGENTA));
+ update_screen();
+#else
+ flash_view(MAGENTA);
+#endif
+ more();
+ }
+ else
+ mpr("You are even more misled!", MSGCH_WARN);
+
+ learned_something_new(TUT_YOU_ENCHANTED);
+
+ xom_is_stimulated((you.duration[DUR_MISLED] - old_value)
+ / BASELINE_DELAY);
+ }
+
+ return;
+}
+
+bool _find_players_ghost ()
+{
+ bool found = false;
+ for (monster_iterator mi; mi; ++mi)
+ if (mi->type == MONS_PLAYER_GHOST && mi->mname == you.your_name)
+ found = true;
+
+ return found;
+}
+
void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
bool do_noise, bool special_ability)
{
@@ -1605,8 +1739,8 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
const bool wizard = monster->is_actual_spellcaster();
god_type god = (priest || !(priest || wizard)) ? monster->god : GOD_NO_GOD;
- // Used for summon X elemental, and nothing else. {bookofjude}
- monster_type el_summon_type = MONS_NO_MONSTER;
+ // Used for summon X elemental and nothing else. {bookofjude}
+ monster_type summon_type = MONS_NO_MONSTER;
switch (spell_cast)
{
@@ -1667,20 +1801,20 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
return;
case SPELL_WATER_ELEMENTALS:
- if (el_summon_type == MONS_NO_MONSTER)
- el_summon_type = MONS_WATER_ELEMENTAL;
+ if (summon_type == MONS_NO_MONSTER)
+ summon_type = MONS_WATER_ELEMENTAL;
// Deliberate fall through
case SPELL_EARTH_ELEMENTALS:
- if (el_summon_type == MONS_NO_MONSTER)
- el_summon_type = MONS_EARTH_ELEMENTAL;
+ if (summon_type == MONS_NO_MONSTER)
+ summon_type = MONS_EARTH_ELEMENTAL;
// Deliberate fall through
case SPELL_AIR_ELEMENTALS:
- if (el_summon_type == MONS_NO_MONSTER)
- el_summon_type = MONS_AIR_ELEMENTAL;
+ if (summon_type == MONS_NO_MONSTER)
+ summon_type = MONS_AIR_ELEMENTAL;
// Deliberate fall through
case SPELL_FIRE_ELEMENTALS:
- if (el_summon_type == MONS_NO_MONSTER)
- el_summon_type = MONS_FIRE_ELEMENTAL;
+ if (summon_type == MONS_NO_MONSTER)
+ summon_type = MONS_FIRE_ELEMENTAL;
if (_mons_abjured(monster, monsterNearby))
return;
@@ -1690,11 +1824,42 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
for (sumcount = 0; sumcount < sumcount2; sumcount++)
{
create_monster(
- mgen_data(el_summon_type, SAME_ATTITUDE(monster), monster,
+ mgen_data(summon_type, SAME_ATTITUDE(monster), monster,
+ 3, spell_cast, monster->pos(), monster->foe, 0, god));
+ }
+ return;
+
+ case SPELL_SUMMON_RAKSHASA:
+ sumcount2 = 1 + random2(4) + random2(monster->hit_dice / 7 + 1);
+
+ for (sumcount = 0; sumcount < sumcount2; sumcount++)
+ {
+ create_monster(
+ mgen_data(MONS_RAKSHASA, SAME_ATTITUDE(monster), monster,
3, spell_cast, monster->pos(), monster->foe, 0, god));
}
return;
+ case SPELL_SUMMON_PLAYER_GHOST:
+
+ // Do nothing in the arena; this could instead create a ghost of an
+ // existant monster, but that would require the spell being dealth with
+ // as a bolt instead.
+ if (crawl_state.arena)
+ return;
+
+ // Only summon one ghost.
+ if (_find_players_ghost())
+ return;
+
+ mpr("There is a horrible, sudden wrenching feeling in your soul!", MSGCH_WARN);
+
+ create_monster(
+ mgen_data(MONS_PLAYER_GHOST, SAME_ATTITUDE(monster), monster,
+ 6, spell_cast, monster->pos(), monster->foe, 0, god));
+
+ return;
+
case SPELL_KRAKEN_TENTACLES:
{
int kraken_index = monster->mindex();
@@ -1737,6 +1902,66 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
mpr("Tentacles burst out of the water!");
return;
}
+ case SPELL_FAKE_MARA_SUMMON:
+ // We only want there to be two fakes, which, plus Mara, means
+ // a total of three Maras; if we already have two, give up, otherwise
+ // we want to summon either one more or two more.
+ sumcount2 = 2 - _count_mara_fakes();
+
+ for (sumcount = 0; sumcount < sumcount2; sumcount++)
+ {
+ mgen_data summ_mon = mgen_data(MONS_MARA_FAKE, SAME_ATTITUDE(monster),
+ monster, 3, spell_cast, monster->pos(),
+ monster->foe, 0, god);
+ // This is somewhat hacky, to prevent "A Mara", and such, as MONS_FAKE_MARA
+ // is not M_UNIQUE.
+ summ_mon.mname = "Mara";
+ summ_mon.extra_flags |= MF_NAME_REPLACE;
+
+ int created = create_monster(summ_mon);
+ if (created == -1)
+ continue;
+
+ // Mara's clones are special; they have the same stats as him, and
+ // are exact clones, so they are created damaged if necessary, with
+ // identical enchants and with the same items.
+ monsters *new_fake = &menv[created];
+ new_fake->hit_points = monster->hit_points;
+ new_fake->max_hit_points = monster->max_hit_points;
+ mon_enchant_list::iterator ei;
+ for (ei = monster->enchantments.begin(); ei != monster->enchantments.end(); ++ei)
+ {
+ new_fake->enchantments.insert(*ei);
+ }
+
+ // Code basically lifted from clone_monster. In theory, it only needs
+ // to copy weapon and armour slots; instead, copy the whole inventory.
+ for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
+ {
+ const int old_index = monster->inv[i];
+
+ if (old_index == NON_ITEM)
+ continue;
+
+ const int new_index = get_item_slot(0);
+ if (new_index == NON_ITEM)
+ {
+ new_fake->unequip(mitm[old_index], i, 0, true);
+ new_fake->inv[i] = NON_ITEM;
+ continue;
+ }
+
+ new_fake->inv[i] = new_index;
+ mitm[new_index] = mitm[old_index];
+ mitm[new_index].set_holding_monster(new_fake->mindex());
+
+ // Mark items as summoned, so there's no way to get three nice
+ // weapons or such out of him.
+ mitm[new_index].flags |= ISFLAG_SUMMONED;
+ }
+ }
+ return;
+
case SPELL_FAKE_RAKSHASA_SUMMON:
sumcount2 = (coinflip() ? 2 : 3);
diff --git a/crawl-ref/source/mon-cast.h b/crawl-ref/source/mon-cast.h
index 896c377ceb..22629b4f9a 100644
--- a/crawl-ref/source/mon-cast.h
+++ b/crawl-ref/source/mon-cast.h
@@ -27,5 +27,6 @@ bool setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
bool check_validity = false);
void mons_cast_haunt(monsters *monster);
+void mons_cast_mislead(monsters *monster);
#endif
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index 7dff442836..c0c251c67b 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -4673,6 +4673,32 @@ static monsterentry mondata[] = {
},
{
+ MONS_MARA, 'R', LIGHTRED, "Mara",
+ M_SPELLCASTER | M_SEE_INVIS | M_ACTUAL_SPELLS | M_SPEAKS | M_UNIQUE,
+ MR_RES_POISON | MR_RES_ELEC,
+ 0, 15, MONS_RAKSHASA, MONS_RAKSHASA, MH_DEMONIC, -10,
+ { {AT_HIT, AF_PLAIN, 40}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 20, 0, 0, 140 },
+ 15, 15, MST_MARA, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT,
+ I_HIGH, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY,
+ MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM
+},
+
+// Illusions of Mara. Only two ever exist at the one time; identical stats to
+// Mara.
+{
+ MONS_MARA_FAKE, 'R', LIGHTRED, "Mara",
+ M_SPELLCASTER | M_SEE_INVIS | M_ACTUAL_SPELLS | M_SPEAKS,
+ MR_RES_POISON | MR_RES_ELEC,
+ 0, 15, MONS_RAKSHASA_FAKE, MONS_RAKSHASA_FAKE, MH_DEMONIC, MAG_IMMUNE,
+ { {AT_HIT, AF_PLAIN, 40}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 20, 0, 0, 140 },
+ 15, 15, MST_MARA_FAKE, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT,
+ I_HIGH, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY,
+ MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM
+},
+
+{
// Snorg can go berserk.
MONS_SNORG, 'T', LIGHTGREEN, "Snorg",
M_UNIQUE | M_WARM_BLOOD | M_SPEAKS,
diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc
index 1c261102a9..01edc35951 100644
--- a/crawl-ref/source/mon-gear.cc
+++ b/crawl-ref/source/mon-gear.cc
@@ -364,6 +364,13 @@ static item_make_species_type _give_weapon(monsters *mon, int level,
0);
break;
+ case MONS_MARA:
+ item.base_type = OBJ_WEAPONS;
+ item.sub_type = random_choose(WPN_DEMON_BLADE, WPN_DEMON_TRIDENT,
+ WPN_DEMON_WHIP, -1);
+ level = MAKE_GOOD_ITEM;
+ break;
+
case MONS_DEEP_ELF_FIGHTER:
case MONS_DEEP_ELF_HIGH_PRIEST:
case MONS_DEEP_ELF_KNIGHT:
@@ -1440,6 +1447,7 @@ void give_armour(monsters *mon, int level)
case MONS_DRACONIAN_KNIGHT:
case MONS_WIZARD:
case MONS_ILSUIW:
+ case MONS_MARA:
if (item_race == MAKE_ITEM_RANDOM_RACE)
item_race = MAKE_ITEM_NO_RACE;
item.base_type = OBJ_ARMOUR;
diff --git a/crawl-ref/source/mon-info.cc b/crawl-ref/source/mon-info.cc
index c62e6aaf1c..07b0e4eab8 100644
--- a/crawl-ref/source/mon-info.cc
+++ b/crawl-ref/source/mon-info.cc
@@ -42,6 +42,8 @@ monster_info::monster_info(const monsters *m)
int mtype = m->type;
if (mtype == MONS_RAKSHASA_FAKE)
mtype = MONS_RAKSHASA;
+ else if (mtype == MONS_MARA_FAKE)
+ mtype = MONS_MARA;
// Currently, difficulty is defined as "average hp".
m_difficulty = mons_difficulty(mtype);
@@ -79,12 +81,21 @@ bool monster_info::less_than(const monster_info& m1,
int m1type = m1.m_mon->type;
int m2type = m2.m_mon->type;
+ if (!crawl_state.arena && you.misled())
+ {
+ m1type = m1.m_mon->get_mislead_type();
+ m2type = m2.m_mon->get_mislead_type();
+ }
// Don't differentiate real rakshasas from fake ones.
if (m1type == MONS_RAKSHASA_FAKE)
m1type = MONS_RAKSHASA;
+ else if (m1type == MONS_MARA_FAKE)
+ m1type = MONS_MARA;
if (m2type == MONS_RAKSHASA_FAKE)
m2type = MONS_RAKSHASA;
+ else if (m2type == MONS_MARA_FAKE)
+ m2type = MONS_MARA;
// Force plain but different coloured draconians to be treated like the
// same sub-type.
@@ -207,11 +218,15 @@ void monster_info::to_string(int count, std::string& desc,
int& desc_color) const
{
std::ostringstream out;
+ monster_type type = m_mon->type;
+ if (!crawl_state.arena && you.misled())
+ type = m_mon->get_mislead_type();
+
if (count == 1)
{
- if (mons_is_mimic(m_mon->type))
- out << mons_type_name(m_mon->type, DESC_PLAIN);
+ if (mons_is_mimic(type))
+ out << mons_type_name(type, DESC_PLAIN);
else
out << m_mon->full_name(DESC_PLAIN);
}
@@ -220,7 +235,9 @@ void monster_info::to_string(int count, std::string& desc,
// Don't pluralise uniques, ever. Multiple copies of the same unique
// are unlikely in the dungeon currently, but quite common in the
// arena. This prevens "4 Gra", etc. {due}
- if (mons_is_unique(m_mon->type))
+ // Unless it's Mara, who summons illusions of himself.
+ if (mons_is_unique(type) && type != MONS_MARA
+ && type != MONS_MARA_FAKE)
{
out << count << " "
<< m_mon->name(DESC_PLAIN);
@@ -228,18 +245,18 @@ void monster_info::to_string(int count, std::string& desc,
// Don't differentiate between dancing weapons, mimics, (very)
// ugly things or draconians of different types.
else if (m_fullname
- && m_mon->type != MONS_DANCING_WEAPON
- && mons_genus(m_mon->type) != MONS_DRACONIAN
- && m_mon->type != MONS_UGLY_THING
- && m_mon->type != MONS_VERY_UGLY_THING
- && !mons_is_mimic(m_mon->type)
+ && type != MONS_DANCING_WEAPON
+ && mons_genus(type) != MONS_DRACONIAN
+ && type != MONS_UGLY_THING
+ && type != MONS_VERY_UGLY_THING
+ && !mons_is_mimic(type)
&& m_mon->mname.empty())
{
out << count << " "
<< pluralise(m_mon->name(DESC_PLAIN));
}
- else if (m_mon->type >= MONS_DRACONIAN
- && m_mon->type <= MONS_PALE_DRACONIAN)
+ else if (type >= MONS_DRACONIAN
+ && type <= MONS_PALE_DRACONIAN)
{
out << count << " "
<< pluralise(mons_type_name(MONS_DRACONIAN, DESC_PLAIN));
@@ -247,7 +264,7 @@ void monster_info::to_string(int count, std::string& desc,
else
{
out << count << " "
- << pluralise(mons_type_name(m_mon->type, DESC_PLAIN));
+ << pluralise(mons_type_name(type, DESC_PLAIN));
}
}
diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h
index a5aaa6526b..526d37f378 100644
--- a/crawl-ref/source/mon-spll.h
+++ b/crawl-ref/source/mon-spll.h
@@ -1297,6 +1297,28 @@
}
},
+ { MST_MARA,
+ {
+ SPELL_MISLEAD,
+ SPELL_FAKE_MARA_SUMMON,
+ SPELL_LIGHTNING_BOLT,
+ SPELL_SUMMON_PLAYER_GHOST,
+ SPELL_PAIN,
+ SPELL_TELEPORT_SELF,
+ }
+ },
+
+ { MST_MARA_FAKE,
+ {
+ SPELL_MISLEAD,
+ SPELL_NO_SPELL,
+ SPELL_LIGHTNING_BOLT,
+ SPELL_NO_SPELL,
+ SPELL_PAIN,
+ SPELL_TELEPORT_SELF,
+ }
+ },
+
{ MST_TEST_SPAWNER,
{
SPELL_SHADOW_CREATURES,
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 9420d1d78c..3687e1711c 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -148,7 +148,8 @@ void init_mon_name_cache()
if (mon == MONS_RAKSHASA_FAKE
|| mon == MONS_ARMOUR_MIMIC
|| mon == MONS_SCROLL_MIMIC
- || mon == MONS_POTION_MIMIC)
+ || mon == MONS_POTION_MIMIC
+ || mon == MONS_MARA_FAKE)
{
// Keep previous entry.
continue;
@@ -2569,6 +2570,7 @@ static bool _ms_los_spell(spell_type monspell)
if (monspell == SPELL_SMITING
|| monspell == SPELL_AIRSTRIKE
|| monspell == SPELL_HAUNT
+ || monspell == SPELL_MISLEAD
|| spell_typematch(monspell, SPTYP_SUMMONING))
{
return (true);
@@ -2584,7 +2586,8 @@ static bool _ms_ranged_spell(spell_type monspell, bool attack_only = false,
// Check for Smiting specially, so it's not filtered along
// with the summon spells.
if (attack_only
- && (monspell == SPELL_SMITING || monspell == SPELL_AIRSTRIKE))
+ && (monspell == SPELL_SMITING || monspell == SPELL_AIRSTRIKE
+ || monspell == SPELL_MISLEAD))
{
return (true);
}
@@ -2732,6 +2735,10 @@ const char *mons_pronoun(monster_type mon_type, pronoun_type variant,
{
gender = GENDER_FEMALE;
}
+ // Mara's fakes aren't a unique, but should still be classified
+ // as male.
+ else if (mon_type == MONS_MARA_FAKE)
+ gender = GENDER_MALE;
else if (mons_is_unique(mon_type) && mon_type != MONS_PLAYER_GHOST)
{
switch (mon_type)
@@ -2812,7 +2819,8 @@ bool mons_has_smite_attack(const monsters *monster)
|| hspell_pass[i] == SPELL_SMITING
|| hspell_pass[i] == SPELL_HELLFIRE_BURST
|| hspell_pass[i] == SPELL_FIRE_STORM
- || hspell_pass[i] == SPELL_AIRSTRIKE)
+ || hspell_pass[i] == SPELL_AIRSTRIKE
+ || hspell_pass[i] == SPELL_MISLEAD)
{
return (true);
}
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index 55b342a903..4cefe30857 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -326,6 +326,7 @@ int monsters::body_weight() const
case MONS_SPECTRAL_WARRIOR:
case MONS_ELECTRIC_GOLEM:
case MONS_RAKSHASA_FAKE:
+ case MONS_MARA_FAKE:
return (0);
case MONS_ZOMBIE_SMALL:
@@ -2039,10 +2040,14 @@ static std::string _invalid_monster_str(monster_type type)
static std::string _str_monam(const monsters& mon, description_level_type desc,
bool force_seen)
{
- if (mon.type == MONS_NO_MONSTER)
+ monster_type type = mon.type;
+ if (!crawl_state.arena && you.misled())
+ type = mon.get_mislead_type();
+
+ if (type == MONS_NO_MONSTER)
return ("DEAD MONSTER");
- else if (invalid_monster_type(mon.type) && mon.type != MONS_PROGRAM_BUG)
- return _invalid_monster_str(mon.type);
+ else if (invalid_monster_type(type) && type != MONS_PROGRAM_BUG)
+ return _invalid_monster_str(type);
const bool arena_submerged = crawl_state.arena && !force_seen
&& mon.submerged();
@@ -2065,10 +2070,10 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
// Various special cases:
// non-gold mimics, dancing weapons, ghosts, Pan demons
- if (mons_is_mimic(mon.type))
+ if (mons_is_mimic(type))
return (get_mimic_item(&mon).name(desc));
- if (mon.type == MONS_DANCING_WEAPON && mon.inv[MSLOT_WEAPON] != NON_ITEM)
+ if (type == MONS_DANCING_WEAPON && mon.inv[MSLOT_WEAPON] != NON_ITEM)
{
unsigned long ignore_flags = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;
bool use_inscrip = true;
@@ -2085,16 +2090,16 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
}
if (desc == DESC_DBNAME)
- return (get_monster_data(mon.type)->name);
+ return (get_monster_data(type)->name);
- if (mon.type == MONS_PLAYER_GHOST)
+ if (type == MONS_PLAYER_GHOST)
return (apostrophise(mon.mname) + " ghost");
// Some monsters might want the name of a different creature.
- monster_type nametype = mon.type;
+ monster_type nametype = type;
// Tack on other prefixes.
- switch (mon.type)
+ switch (type)
{
case MONS_ZOMBIE_SMALL: case MONS_ZOMBIE_LARGE:
case MONS_SKELETON_SMALL: case MONS_SKELETON_LARGE:
@@ -2159,7 +2164,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
result += "submerged ";
// Tack on other prefixes.
- switch (mon.type)
+ switch (type)
{
case MONS_UGLY_THING:
case MONS_VERY_UGLY_THING:
@@ -2185,7 +2190,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
break;
}
- if (mon.type == MONS_SLIME_CREATURE && desc != DESC_DBNAME)
+ if (type == MONS_SLIME_CREATURE && desc != DESC_DBNAME)
{
ASSERT(mon.number <= 5);
const char* cardinals[] = {"buggy ", "", "large ", "very large ",
@@ -2193,7 +2198,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
result += cardinals[mon.number];
}
- if (mon.type == MONS_BALLISTOMYCETE && desc != DESC_DBNAME)
+ if (type == MONS_BALLISTOMYCETE && desc != DESC_DBNAME)
{
result += mon.number ? "active " : "";
}
@@ -2231,7 +2236,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
}
// Add suffixes.
- switch (mon.type)
+ switch (type)
{
case MONS_ZOMBIE_SMALL:
case MONS_ZOMBIE_LARGE:
@@ -2260,7 +2265,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
result.insert(1, "n");
}
- if (mons_is_unique(mon.type) && starts_with(result, "the "))
+ if (mons_is_unique(type) && starts_with(result, "the "))
{
switch (desc)
{
@@ -2278,7 +2283,7 @@ static std::string _str_monam(const monsters& mon, description_level_type desc,
{
// If momentarily in original form, don't display "shaped
// shifter".
- if (mons_genus(mon.type) != MONS_SHAPESHIFTER)
+ if (mons_genus(type) != MONS_SHAPESHIFTER)
result += " shaped shifter";
}
@@ -2350,7 +2355,10 @@ std::string monsters::full_name(description_level_type desc,
}
}
- const int _type = mons_is_zombified(this) ? base_monster : type;
+ int _type = mons_is_zombified(this) ? base_monster : type;
+ if (!crawl_state.arena && you.misled())
+ _type = get_mislead_type();
+
if (mons_genus(_type) == MONS_HYDRA && flag == 0)
return (title);
@@ -3720,7 +3728,11 @@ void monsters::ghost_init()
enchantments.clear();
ench_countdown = 0;
- find_place_to_live();
+ // Summoned player ghosts are already given a position; calling this
+ // in those instances will cause a segfault. Instead, check to see
+ // if we have a home first. {due}
+ if (!in_bounds(pos()))
+ find_place_to_live();
}
void monsters::uglything_init(bool only_mutate)
@@ -5705,6 +5717,14 @@ const monsterentry *monsters::find_monsterentry() const
: get_monster_data(type);
}
+monster_type monsters::get_mislead_type() const
+{
+ if (props.exists("mislead_as"))
+ return static_cast<monster_type>(props["mislead_as"].get_short());
+ else
+ return type;
+}
+
int monsters::action_energy(energy_use_type et) const
{
bool swift = has_ench(ENCH_SWIFT);
diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h
index c17aeee6ce..400ad01769 100644
--- a/crawl-ref/source/monster.h
+++ b/crawl-ref/source/monster.h
@@ -117,6 +117,7 @@ public:
bool has_base_name() const;
const monsterentry *find_monsterentry() const;
+ monster_type get_mislead_type() const;
void init_experience();
diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc
index 04d4b9a7ca..4da287188f 100644
--- a/crawl-ref/source/output.cc
+++ b/crawl-ref/source/output.cc
@@ -734,6 +734,9 @@ static void _get_status_lights(std::vector<status_light>& out)
if (you.duration[DUR_LIQUID_FLAMES])
out.push_back(status_light(RED, "Fire"));
+ if (you.duration[DUR_MISLED])
+ out.push_back(status_light(LIGHTMAGENTA, "Misled"));
+
if (you.duration[DUR_POISONING])
{
int color = _bad_ench_colour( you.duration[DUR_POISONING], 5, 10 );
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index fb6b425834..4e5cf2dc77 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -6895,6 +6895,11 @@ bool player::invisible() const
return (duration[DUR_INVIS] && !backlit());
}
+bool player::misled() const
+{
+ return (duration[DUR_MISLED]);
+}
+
bool player::visible_to(const actor *looker) const
{
if (crawl_state.arena)
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index 285d438c0c..d7f5306c08 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -325,6 +325,7 @@ public:
bool is_levitating() const;
bool cannot_speak() const;
bool invisible() const;
+ bool misled() const;
bool can_see_invisible() const;
bool can_see_invisible(bool unid) const;
bool visible_to(const actor *looker) const;
diff --git a/crawl-ref/source/rltiles/dc-mon.txt b/crawl-ref/source/rltiles/dc-mon.txt
index dd347bd4fb..6809b0cd93 100644
--- a/crawl-ref/source/rltiles/dc-mon.txt
+++ b/crawl-ref/source/rltiles/dc-mon.txt
@@ -232,9 +232,14 @@ azrael MONS_AZRAEL
efreet MONS_EFREET
## Rakshasa ('R')
+%sdir dc-mon/unique
+mara MONS_MARA
+mara MONS_MARA_FAKE
+%sdir dc-mon/demons
rakshasa MONS_RAKSHASA
rakshasa MONS_RAKSHASA_FAKE
+
## Abominations ('X')
abomination_large MONS_ABOMINATION_LARGE
abomination_large1
diff --git a/crawl-ref/source/rltiles/dc-mon/unique/mara.png b/crawl-ref/source/rltiles/dc-mon/unique/mara.png
new file mode 100644
index 0000000000..2cb82ea411
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-mon/unique/mara.png
Binary files differ
diff --git a/crawl-ref/source/show.cc b/crawl-ref/source/show.cc
index f6fc5cad9e..d6c3ff577a 100644
--- a/crawl-ref/source/show.cc
+++ b/crawl-ref/source/show.cc
@@ -360,7 +360,8 @@ void show_def::_update_monster(const monsters* mons)
_set_backup(e);
grid(e).cls = SH_MONSTER;
- grid(e).mons = mons->type;
+ grid(e).mons = (!crawl_state.arena && you.misled()) ?
+ mons->get_mislead_type() : mons->type;
grid(e).colour = get_mons_glyph(mons).col;
#ifdef USE_TILE
diff --git a/crawl-ref/source/showsymb.cc b/crawl-ref/source/showsymb.cc
index 1d3c4e7dae..50a6272d41 100644
--- a/crawl-ref/source/showsymb.cc
+++ b/crawl-ref/source/showsymb.cc
@@ -56,6 +56,11 @@ glyph get_show_glyph(show_type object)
static int _get_mons_colour(const monsters *mons)
{
int col = mons->colour;
+ if (!crawl_state.arena && you.misled())
+ {
+ const monsterentry* mdat = get_monster_data(mons->get_mislead_type());
+ col = mdat->colour;
+ }
if (mons->berserk())
col = RED;
@@ -125,7 +130,11 @@ glyph get_item_glyph(const item_def *item)
glyph get_mons_glyph(const monsters *mons)
{
glyph g;
- g.ch = mons_char(mons->type);
+
+ if (!crawl_state.arena && you.misled())
+ g.ch = mons_char(mons->get_mislead_type());
+ else
+ g.ch = mons_char(mons->type);
g.col = _get_mons_colour(mons);
return (g);
}
diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h
index 27f1ea2632..3adc30d1d3 100644
--- a/crawl-ref/source/spl-data.h
+++ b/crawl-ref/source/spl-data.h
@@ -2500,93 +2500,145 @@
{
SPELL_FLAME_AMMUNITION, "Flame Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_FIRE,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 3,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_FIRE,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 3,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_FROST_AMMUNITION, "Frost Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_ICE,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 3,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_ICE,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 3,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_SHOCKING_AMMUNITION, "Shocking Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_AIR,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 5,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_AIR,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 5,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_EXPLODING_AMMUNITION, "Exploding Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_FIRE | SPTYP_AIR,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 5,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_FIRE | SPTYP_AIR,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 5,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_WARP_AMMUNITION, "Warp Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 5,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 5,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_REAPING_AMMUNITION, "Reaping Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_NECROMANCY,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 7,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_NECROMANCY,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 7,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
},
{
SPELL_RETURNING_AMMUNITION, "Returning Ammunition",
- SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION,
- SPFLAG_HELPFUL | SPFLAG_BATTLE,
- 3,
- 0,
- -1,-1,
- 0,
- NULL,
- false,
- true
+ SPTYP_ENCHANTMENT | SPTYP_TRANSLOCATION,
+ SPFLAG_HELPFUL | SPFLAG_BATTLE,
+ 3,
+ 0,
+ -1,-1,
+ 0,
+ NULL,
+ false,
+ true
+},
+
+{
+ SPELL_FAKE_MARA_SUMMON, "Mara Summon",
+ SPTYP_SUMMONING,
+ SPFLAG_MONSTER,
+ 5,
+ 0,
+ -1, -1,
+ 0,
+ NULL,
+ false,
+ false
+},
+
+{
+ SPELL_SUMMON_RAKSHASA, "Summon Rakshasa",
+ SPTYP_SUMMONING,
+ SPFLAG_MONSTER,
+ 5,
+ 0,
+ -1, -1,
+ 0,
+ NULL,
+ false,
+ false
+},
+
+{
+ SPELL_MISLEAD, "Mislead",
+ SPTYP_ENCHANTMENT,
+ SPFLAG_TARGET | SPFLAG_NOT_SELF,
+ 5,
+ 200,
+ LOS_RADIUS, LOS_RADIUS,
+ 0,
+ NULL,
+ false,
+ false
+},
+
+{
+ SPELL_SUMMON_PLAYER_GHOST, "Summon Player Ghost",
+ SPTYP_SUMMONING,
+ SPFLAG_MONSTER,
+ 5,
+ 0,
+ -1, -1,
+ 0,
+ NULL,
+ false,
+ false
},
{
diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h
index 33cc24396a..26ecf81233 100644
--- a/crawl-ref/source/tags.h
+++ b/crawl-ref/source/tags.h
@@ -40,7 +40,7 @@ enum tag_file_type // file types supported by tag system
enum tag_major_version
{
TAG_MAJOR_START = 5,
- TAG_MAJOR_VERSION = 10
+ TAG_MAJOR_VERSION = 11
};
// Minor version will be reset to zero when major version changes.
diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc
index 0fe8bbbb32..725947c13c 100644
--- a/crawl-ref/source/tilepick.cc
+++ b/crawl-ref/source/tilepick.cc
@@ -121,6 +121,8 @@ int tileidx_monster_base(const monsters *mon, bool detected)
bool in_water = feat_is_water(grd(mon->pos()));
int type = mon->type;
+ if (!crawl_state.arena && you.misled())
+ type = mon->get_mislead_type();
// Show only base class for detected monsters.
if (detected)
@@ -1014,6 +1016,12 @@ int tileidx_monster_base(const monsters *mon, bool detected)
case MONS_EROLCHA:
return TILEP_MONS_EROLCHA;
+ // rakshasas ('R')
+ case MONS_MARA:
+ return TILEP_MONS_MARA;
+ case MONS_MARA_FAKE:
+ return TILEP_MONS_MARA_FAKE;
+
// trolls ('T')
case MONS_PURGY:
return TILEP_MONS_PURGY;