summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/docs/monster_speech.txt36
-rw-r--r--crawl-ref/source/dat/database/monspeak.txt10
-rw-r--r--crawl-ref/source/monspeak.cc81
-rw-r--r--crawl-ref/source/monstuff.cc28
4 files changed, 106 insertions, 49 deletions
diff --git a/crawl-ref/docs/monster_speech.txt b/crawl-ref/docs/monster_speech.txt
index 17579e9067..bb5ea8ecc2 100644
--- a/crawl-ref/docs/monster_speech.txt
+++ b/crawl-ref/docs/monster_speech.txt
@@ -92,6 +92,11 @@ carefully constructed monster speech never gets printed, and this
documentation also doesn't help you solve the problem, you might want
to post a bug report on Dungeon Crawl's SourceForge site [1].
+The exception to the above is when the monster goes away due to dying, being
+banished, or a summoned monster being abjured or having it's time run out. In
+that case the monster always speaks if the player can see the grid the monster
+is on (assuming that there's a speech entry defined for that occasion, of
+course).
B. A simple example
====================
@@ -386,13 +391,34 @@ and the only hardcoded keywords are "noisy weapon" for weapons with
the noises property, and "singing sword" for (who'd have guessed?) the
Singing Sword.
+Death speech
+------------
+You can define messages for the monster to give for when it goes away in three
+different manners:
+
+* If it really died, then the game will look up a speech entry with the
+ same keys as usual, but with " killed" appended to all the keys.
+
+* If it was banished, then the game will append " banished" to all the
+ lookup keys.
+
+* If the monster was summoned and rather than being killed was abjured or
+ ran out of time, then the game will append " unsummoned" to all of the
+ lookup keys.
+
+The game will always do a lookup in these circumstances if the player can see
+the square the monster is on, so if you only want a death message to be given
+occasionally then make one of the messages "__NONE" and give it a high weight.
+
+Of course, if no keys with the given suffix are in the database then the
+monster will say nothing in that circumstance.
+
Special monster speech
----------------------
-Rarely, monster speech will also rely on hard-coded keys, such as
-Boris' "return_speech". If such a hard-coded key is changed or removed,
-the speech in question will simply not be printed. This may look odd in
-the game, but will have no other effect. Sometimes, default messages
-will be output instead.
+Rarely, monster speech will also rely on hard-coded keys. If such a hard-coded
+key is changed or removed, the speech in question will simply not be printed.
+This may look odd in the game, but will have no other effect. Sometimes,
+default messages will be output instead.
God speech
----------
diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt
index 2ed980020b..b300f636cb 100644
--- a/crawl-ref/source/dat/database/monspeak.txt
+++ b/crawl-ref/source/dat/database/monspeak.txt
@@ -1245,7 +1245,7 @@ _Boris_rare_
@The_monster@ says @to_foe@, "Join the legion of my servants."
%%%%
-Boris return_speech
+Boris killed
@_Boris_return_common_@
@@ -1260,9 +1260,9 @@ _Boris_return_common_
@The_monster@ says @to_foe@, "This isn't over yet!"
-@The_monster@ says @to_foe@, "I'll be back!"
+@The_monster@ says, "I'll be back!"
-@The_monster@ says @to_foe@, "This isn't the end, it's only just beginning!"
+@The_monster@ says, "This isn't the end, it's only just beginning!"
%%%%
_Boris_return_rare_
@@ -1271,6 +1271,10 @@ _Boris_return_rare_
@The_monster@ says @to_foe@, "You cannot defeat me so easily!"
@The_monster@ says @to_foe@, "We will meet again!"
+%%%%
+Boris unsummoned
+
+@The_monster@ says, "You can't fire me, I quit!"
## END Boris ##
%%%%
# An adventurer hating competition
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index 1d9a7c77b6..51496f4b49 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -267,11 +267,19 @@ static std::string __get_speak_string(const std::vector<std::string> &prefixes,
}
static std::string _get_speak_string(const std::vector<std::string> &prefixes,
- const std::string &key,
+ std::string key,
const monsters *monster,
bool no_player, bool no_foe,
bool no_foe_name, bool no_god)
{
+ int duration = 1;
+ if (monster->hit_points <= 0)
+ key += " killed";
+ else if (monster->flags & MF_BANISHED)
+ key += " banished";
+ else if (monster->is_summoned(&duration) && duration <= 0)
+ key += " unsummoned";
+
std::string msg;
for (int tries = 0; tries < 10; tries++)
{
@@ -354,44 +362,55 @@ static bool _polyd_can_speak(const monsters* monster)
// Returns true if something is said.
bool mons_speaks(const monsters *monster)
{
- // Invisible monster tries to remain unnoticed. Unless they're
- // confused, since then they're too confused to realize they
- // should stay silent, but only if the player can see them, so as
- // to not have to deal with cases of speaking monsters which the
- // player can't see.
- if (monster->invisible() && !(player_monster_visible(monster)
- && monster->has_ench(ENCH_CONFUSION)))
- {
- return (false);
- }
+ ASSERT(!invalid_monster_class(monster->type));
+
+ // Monsters always talk on death, even if invisible/silenced/etc
+ int duration = -1;
+ const bool force_speak = !monster->alive()
+ || !(monster->flags & MF_BANISHED)
+ || (monster->is_summoned(&duration) && duration <= 0);
+
+ if (!force_speak)
+ {
+ // Invisible monster tries to remain unnoticed. Unless they're
+ // confused, since then they're too confused to realize they
+ // should stay silent, but only if the player can see them, so as
+ // to not have to deal with cases of speaking monsters which the
+ // player can't see.
+ if (monster->invisible() && !(player_monster_visible(monster)
+ && monster->has_ench(ENCH_CONFUSION)))
+ {
+ return (false);
+ }
+
+ // Silenced monsters only "speak" 1/3 as often as non-silenced,
+ // unless they're normally silent (S_SILENT). Use
+ // get_monster_data(monster->type) to bypass mon_shouts()
+ // replacing S_RANDOM with a random value.
+ if (silenced(monster->pos())
+ && get_monster_data(monster->type)->shouts != S_SILENT)
+ {
+ if (!one_chance_in(3))
+ return (false);
+ }
- // Silenced monsters only "speak" 1/3 as often as non-silenced,
- // unless they're normally silent (S_SILENT). Use
- // get_monster_data(monster->type) to bypass mon_shouts()
- // replacing S_RANDOM with a random value.
- if (silenced(monster->pos())
- && get_monster_data(monster->type)->shouts != S_SILENT)
- {
- if (!one_chance_in(3))
+ // Berserk monsters just want your hide.
+ if (monster->has_ench(ENCH_BERSERK))
return (false);
- }
-
- // Berserk monsters just want your hide.
- if (monster->has_ench(ENCH_BERSERK))
- return (false);
- // Monsters in a battle frenzy are likewise occupied.
- if (monster->has_ench(ENCH_BATTLE_FRENZY) && !one_chance_in(3))
- return (false);
+ // Monsters in a battle frenzy are likewise occupied.
+ if (monster->has_ench(ENCH_BATTLE_FRENZY) && !one_chance_in(3))
+ return (false);
- // Charmed monsters aren't too expressive.
- if (monster->has_ench(ENCH_CHARM) && !one_chance_in(3))
- return (false);
+ // Charmed monsters aren't too expressive.
+ if (monster->has_ench(ENCH_CHARM) && !one_chance_in(3))
+ return (false);
+ }
std::vector<std::string> prefixes;
if (mons_neutral(monster))
{
- if (coinflip()) // Neutrals speak half as often.
+ if (!force_speak && coinflip()) // Neutrals speak half as often.
return (false);
prefixes.push_back("neutral");
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 6e4d895468..6be24e7ffe 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -996,6 +996,11 @@ void monster_die(monsters *monster, killer_type killer,
if (invalid_monster(monster))
return;
+ // If a monster was banished to the abyss and then killed there,
+ // then it's death wasn't a banishment.
+ if (you.level_type == LEVEL_ABYSS)
+ monster->flags &= ~MF_BANISHED;
+
if (!silent && _monster_avoided_death(monster, killer, killer_index))
return;
@@ -1012,7 +1017,9 @@ void monster_die(monsters *monster, killer_type killer,
remove_auto_exclude(monster);
int summon_type = 0;
- const bool summoned = mons_is_summoned(monster, NULL, &summon_type);
+ int duration = 0;
+ const bool summoned = mons_is_summoned(monster, &duration,
+ &summon_type);
const int monster_killed = monster_index(monster);
const bool hard_reset = testbits(monster->flags, MF_HARD_RESET);
const bool gives_xp = !summoned
@@ -1574,17 +1581,18 @@ void monster_die(monsters *monster, killer_type killer,
monster->foe = killer_index;
}
- if (monster->type == MONS_BORIS && monster->foe != MHITNOT && !in_transit)
+
+ if (!silent && !wizard && see_grid(monster->pos()))
{
- // XXX: Actual blood curse effect for Boris? -- bwr
+ // Make sure that the monster looks dead.
+ if (monster->alive() && !in_transit && (!summoned || duration > 0))
+ monster->hit_points = -1;
+ mons_speaks(monster);
+ }
- // Provide the player with an ingame clue to Boris' return. -- bwr
- std::string msg = getSpeakString("Boris return_speech");
- if (!msg.empty())
- {
- msg = do_mon_str_replacements(msg, monster);
- mpr(msg.c_str(), MSGCH_TALK);
- }
+ if (monster->type == MONS_BORIS && !in_transit)
+ {
+ // XXX: Actual blood curse effect for Boris? -- bwr
// Now that Boris is dead, he's a valid target for monster
// creation again. -- bwr