some time, though. I added a new prefix "related" if the player character has the same genus as the monster. My new prefix additions (hostile, related and religion) can now be skipped in the speech lookup, which fixes a couple of problems where several monsters were forced silent because of lookup problems. Also clean up the lookup process for silence, allow charmed monsters to sometimes speak (if rarely) and added several debugging statements that are also mentioned in monster_speech.txt that is now clearer than ever before (I hope).
Fixed a couple of bugs in the monster shape calculation where the old glyphs were still being used. Also, a Draconian character eating any type of Draconian will now count as cannibalism.
Also: FR 1894060: Level annotations now prompt for confirmation if they contain an exclamation mark (rather than "WARN"). Fix 1859443: Arriving on a square via staircase now calls request_autopickup and will describe items on the square.
Oh, and fix init.txt to really allow reading in macros from external files.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3604 c06c8d41-db1a-0410-9941-cceddc491573
6GSGCC5JQJ5NOKX36UHRNOCXNHDBS2A2TDMAR34UBOGWE2DORXIQC
HEEAPXAXYJORP7NEYLG62OD6QUCM2AZUWGK6EMWMZY6DPHWK6RGAC
PQ3SLWFD5CF33ZHBG2V7YJEKAL6HTSDYOV25OKUTBCW2QF7TL4AAC
TVRAEEYYHT3XGS25RTGXX4RVJM35JW3YOENR6M4XKQYBX3TLY5KAC
JDM5R3HYGXKQKZWY35QZ2KOB24TFZ3FW2PCNXCRCMWG72AZC5ZXQC
64RZSOR4B4T5QO3FAERBTH2QJIQFSCIFHI4WYK3MDAEAZVAHRLVQC
2VDLCWQOJPOXPJQ7XYGOWZG5P2AE44DGFHC76WI4GBOOK4B7FQTQC
M5ZDZJBTOJ7SWQPZZQPC24JYZKP26MWSRDHXBWQE2MPPL6WCXOIQC
2H32CFFM2FNS63JJPNM2S6HMO543EX72GMPOU5GI6HTMQYPL6I3AC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
3QLM46S44Z7GDLWPH3VHBMW2RSWZAOLGJMG2BDKNGUOZIM4IX6WAC
J6APXOT4QOGQFONWB7G546VTVF6QG42HVOROMHF7YBDJPR4K26OAC
AIIVH43Z5X3GTPFY4FXQRZPG6Y7QPH2KJ47VM2Q43PCGGD5MTMOAC
KZIHM6RUX43HHKXG6HGJHVEEYUPVVNBFIWMT4SKPD2GAH5ZMA3KAC
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
YM3U5YZEFIYLRJVDWQCSP75JL56WC36XLQSL5ZBT4IWSXYJRBCBAC
G5WLU3B4MR3ZDJLGR6OHNIMLZXGQS2EWBJ5YYY5J2GWM6DTD44BAC
D4MYPHZGUOL7HPS53DUUM4QSPIR5W3227DENDB4OBNDVQ4BU5VDAC
G7CTMQ3VNTAB73ZI3LNZHKTAJ5LEQEGG772MVFQQ5XXLCMJVORTQC
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC
XX3TYGTDZY7AT53JJDTMKQXOYL4TUPVXXE2WY53UKZZOZ4UXDTQQC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
BMHUBADDGIOZRVN4P3O5QKIDUYD4RFWBS7MP5X6LZWAYHUBRVD2QC
O3I7TURGIE6BXI6VXLWVTITPWNP3N5X4EEOBXLIZMDIKDIKPKUVQC
WL5WZXFJ6TONUQRSHUY4GQ5USU47ILWNN5X2JDQZO4CRJJZSRQIAC
RVST2QHYJ757ZHK4AUJ5NGPDZ44AD6RVFVXYPKQIBJXZBDNUCHXQC
FYD4A5TIETIV2ZLFYWGHXANU6WQFKMVREHM7OZY2TAXSBMMCDLJAC
if (transform)
{
switch (you.attribute[ATTR_TRANSFORMATION])
{
case TRAN_AIR:
return (false);
// unique monsters
case TRAN_BAT:
return (mon == MONS_GIANT_BAT);
case TRAN_ICE_BEAST:
return (mon == MONS_ICE_BEAST);
case TRAN_SERPENT_OF_HELL:
return (mon == MONS_SERPENT_OF_HELL);
// compare with monster *species*
case TRAN_LICH:
return (mons_species(mon) == MONS_LICH);
// compare with monster *genus*
case TRAN_SPIDER:
return (mons_genus(mon) == MONS_WOLF_SPIDER);
case TRAN_DRAGON:
return (mons_genus(mon) == MONS_DRAGON); // includes all drakes
default:
break; // check real (non-transformed) form
}
}
static std::string get_speak_string(const std::vector<std::string> prefixes,
// Try the exact key lookup along with the entire prefix list.
// If that fails, start ignoring hostile/religion/silence, in that order,
// first skipping hostile, then hostile *and* religion, then all three.
static std::string try_exact_string(const std::vector<std::string> prefixes,
if (prefixes[i] == "silenced")
{
if (ignore_silenced)
continue;
silenced = true;
}
prefix += prefixes[i];
prefix += " ";
if (prefixes[i] == "hostile")
{
if (ignore_hostile)
continue;
hostile = true;
}
else if (prefixes[i] == "related")
{
if (ignore_related)
continue;
related = true;
}
else if (prefixes[i] == "silenced")
{
if (ignore_silenced)
continue;
silenced = true;
}
else if (prefixes[i] == "beogh" || prefixes[i] == "good god"
|| prefixes[i] == "evil god")
{
if (ignore_religion)
continue;
religion = true;
}
prefix += prefixes[i];
prefix += " ";
}
msg = getSpeakString(prefix + key);
if (msg.empty())
{
if (hostile)
msg = try_exact_string(prefixes, key, true);
else if (related)
msg = try_exact_string(prefixes, key, true, true);
else if (religion)
msg = try_exact_string(prefixes, key, true, true, true);
// 50% use non-verbal monster speech,
// 50% try for more general silenced monster message instead
else if (silenced && coinflip())
msg = try_exact_string(prefixes, key, true, true, true, true);
// charmed monsters aren't too expressive
if (monster->has_ench(ENCH_CHARM))
return false;
// results in wizard mode.)
if (you.religion == GOD_BEOGH && mons_genus(monster->type) == MONS_ORC)
// results in wizard mode.) Don't count charmed orcs.
if (you.religion == GOD_BEOGH && mons_genus(monster->type) == MONS_ORC
&& !monster->has_ench(ENCH_CHARM))
{
#ifdef DEBUG_MONSPEAK
{
std::string prefix = "";
const int size = prefixes.size();
for (int i = 0; i < size; i++)
{
prefix += prefixes[i];
prefix += " ";
}
mprf(MSGCH_DIAGNOSTICS, "monster speech lookup for %s: prefix = %s",
monster->name(DESC_PLAIN).c_str(), prefix.c_str());
}
#endif
if (mons_species(monster->type) != monster->type)
{
msg = get_speak_string(prefixes,
mons_type_name(mons_species(monster->type), DESC_PLAIN),
msg = get_speak_string(prefixes,
mons_type_name(mons_genus(monster->type), DESC_PLAIN),
}
// Still nothing found? Try monster genus!
if ((msg.empty() || msg == "__NEXT")
&& mons_genus(monster->type) != monster->type)
{
msg = get_speak_string(prefixes,
mons_type_name(mons_genus(monster->type), DESC_PLAIN),
monster);
}
}
static std::string pluralise_player_genus()
{
std::string sp = species_name(you.species, 1, true);
if (player_genus(GENPC_ELVEN, you.species)
|| player_genus(GENPC_DWARVEN, you.species))
{
sp = sp.substr(0, sp.find("f"));
sp += "ves";
}
else if (you.species != SP_DEMONSPAWN)
sp += "s";
return (sp);
msg = replace_all(msg, "@species_insult_adj1@", get_species_insult("adj1"));
msg = replace_all(msg, "@species_insult_adj2@", get_species_insult("adj2"));
msg = replace_all(msg, "@species_insult_noun@", get_species_insult("noun"));
if (msg.find("@species_insult_") != std::string::npos)
{
msg = replace_all(msg, "@species_insult_adj1@", get_species_insult("adj1"));
msg = replace_all(msg, "@species_insult_adj2@", get_species_insult("adj2"));
msg = replace_all(msg, "@species_insult_noun@", get_species_insult("noun"));
}
case 'm': // minotaurs, manticores, and snails/slugs/etc
if (type == MONS_MINOTAUR)
return(MON_SHAPE_HUMANOID);
else if (type == MONS_MANTICORE)
return(MON_SHAPE_QUADRUPED);
else
return(MON_SHAPE_SNAIL);
case 'm': // merfolk
return(MON_SHAPE_HUMANOID);
if (you.religion == GOD_ZIN && mons_intel(item.plus) >= I_NORMAL)
simple_god_message(" expects more respect for this departed "
"soul.");
else if (is_good_god(you.religion) && is_player_same_species(item.plus))
if (is_good_god(you.religion) && is_player_same_species(item.plus))
%%%%
friendly_family
@The_monster@ says, "I like you, cousin!"
@The_monster@ says, "Family always sticks together, @player_name@!"
@The_monster@ says, "@player_genus@kind's got to stick together!"
@The_monster@ shouts, "@player_genus_plural@ are the best!"
@The_monster@ says happily, "You and me, we're family."
# Not currently used:
#Thou @generic_insult@!
%%%%
body_or_spiritual_part
w:3
@important_body_part@
w:1
@important_spiritual_part@
%%%%
will_or_shall
will
shall
%%%%
feast_or_devour
feast on
devour
%%%%
#######################################################
# generic insults consists of three random parts
#######################################################
generic_insult
@insult_adjective1@ @insult_adjective2@ @insult_noun@
important_body_part
head
brain
heart
viscera
eyes
lungs
liver
throat
neck
skull
spine
%%%%
important_spiritual_part
soul
spirit
inner light
hope
faith
will
heart
mind
sanity
fortitude
life force
%%%%
meal
meal
breakfast
lunch
dinner
supper
repast
snack
victuals
refection
junket
luncheon
snackling
curdle
snacklet
mouthful
%%%%
meal
meal
breakfast
lunch
dinner
supper
repast
snack
victuals
refection
junket
luncheon
snackling
curdle
snacklet
mouthful
%%%%
#######################################################
# generic insults consist of three random parts
#######################################################
generic_insult
@insult_adjective1@ @insult_adjective2@ @insult_noun@
%%%%
insult.txt handles insults thrown at you by imps and demons.
Monster speech probabilities
============================
Not all monsters are equally likely to speak. Rather there are
different chances involved depending on several attributes, and most of
the time the database lookup stage isn't even reached.
The player will only ever hear monsters speak if they are nearby, and
monsters will only speak if they are not asleep, not submerged in
water, air or lava, and not wandering around aimlessly (unless neutral).
Berserk monsters are too busy killing and maiming to speak. Also, invisible
monsters the player can't see (for lack of see invisible) will always stay
silent, unless confused.
Monsters capable of speech (i.e. all intelligent humanoid monsters, as
well as all uniques and some non-unique demons) have a base chance of
1/21 of speaking, while humanoid monsters incapable of speech will
never communicate with the player in any form.
Non-humanoid monsters get a 1/84 probability of "speaking" per turn
(non-verbal actions, more like). This chance is divided by another 10,
if the monster in question was generated as a member of a group.
Chances are again doubled if this non-humanoid monster is fleeing, and
doubled again if confused.
wpnnoise.txt handles randart weapons with the noises property.
Neutral monsters only speak half as often, and for charmed monsters the
probability is divided by 3. The same applies to silenced monsters, i.e.
monsters that are not naturally silent will only get to even attempt to speak in
one out of three tries where the above chances hold.
Note that the definition of which monsters are capable of speech is
entirely hardcoded. We think we made this apply to all sensible
monsters, i.e. all intelligent humanoid monsters, but of course it is
possible we've overlooked something, so if you find that your
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].
If you have a look at these files, you'll see that all entries have
basically the same structure: a key, followed by one or more values.
If you have a look at some of the speech files, you'll see that all
entries have basically the same structure: a key, followed by one or
more values.
the game generally treats neutral monsters like hostiles as they still
pose a danger to players.
the game generally treats neutral monsters like hostiles since they
still pose a danger to players.
The prefix "related" is added if the player and the monster share the
same genus, e.g. if you're playing a Sludge Elf, and the monster in
question is a deep elf blademaster, you both are Elves and the monster
speech may reflect that. It's currently only used for friendly
humanoids who will now sometimes mention that you're family, if you
are.
added to the list. If you worship one of the good gods (Zin, The
Shining One, or Elyvilon) the prefix "good god" is used instead.
Conversely, worshippers of one of the evil gods (Yredelemnul,
Kikubaaqudgha, Makhleb, Lugonu, Xom, Beogh, or Vehumet) will use the
prefix "evil god".
added to the list. If you worship one of the good gods (Zin, Elyvilon,
or The Shining One) the prefix "good god" is used instead.
Conversely, worshippers of one of the evil gods (Yredelemnul, Makhleb,
Kikubaaqudgha, Lugonu, Xom, Beogh, or Vehumet) will use the prefix
"evil god".
First we search for the whole database string in combination with the
monster name. If that didn't yield any results, reading from left to
right, combinations are tested, beginning at three prefixes and ending
at none. At this stage the list of prefixes is always prefixed itself
with "default". This ensures that, for example, fleeing uniques won't
output their normal menacing speech but rather the default speech
defined for fleeing humanoids in general.
First we search for the complete prefix string in combination with the
monster name. Then we try omitting some very specific prefixes that
might not be so important, first skipping on "hostile", then also
ignoring "related", then religion status, and finally "silenced", where
applicable.
If all of that didn't yield any results, next we'll take the complete
prefix list again, then, reading from left to right, combinations are
tested, beginning at three prefixes and ending at none. At this stage
the list of prefixes is always prepended with "default". This ensures
that, for example, fleeing uniques won't output their normal menacing
speech but rather the default speech defined for fleeing humanoids in
general.
In practice this means that database keys starting with "default" are
the fallback solution if the exact look-up has failed. As such, the
messages should be generic enough to allow for all the possibly skipped
prefixes, or else those cases should be caught earlier, e.g. if you
have "default friendly humanoid", you should also define "default
friendly fleeing humanoid" and "default friendly confused humanoid"
(and possibly both combined) even if only with "__NONE" (stay silent),
as the general friendly messages may look odd for a monster such
afflicted.
humanoid, winged humanoid, tailed humanoid, winged tailed humanoid,
centaur, naga, quadruped, tailless quadruped, winged quadruped,
bat, snake, fish, insect, winged insect, arachnid, centipede, snail,
plant, fungus, orb, and blob.
humanoid, winged humanoid (angels), tailed humanoid (draconians),
winged tailed humanoid (gargoyles), centaur, naga, quadruped,
tailless quadruped (frogs), winged quadruped (hippogriff), bat,
snake (also eels and worms), fish, insect, winged insect, arachnid,
centipede, snail, plant, fungus, orb (eyes), and blob (jellies).
database, so one after another, we first check for "default friendly
fleeing beogh orc wizard", "default friendly fleeing orc wizard",
"default friendly beogh orc wizard", "default fleeing beogh orc
wizard", "default friendly orc wizard", "default fleeing orc wizard",
"default beogh orc wizard" and "default orc wizard", none of which is
successful.
Both species and genus of the orc wizard is "orc", so we retry the
above using "orc" instead of "orc wizard". The same is repeated for
"friendly fleeing beogh 'o'", and we still haven't found anything.
database, so first we try to remove the less important prefixes,
something that in this case only applies to "beogh". Unfortunately,
"friendly fleeing orc wizard" also has no corresponding entry in the
database, so that, one after another, we now check for "default
friendly fleeing beogh orc wizard", "default friendly fleeing orc
wizard", "default friendly beogh orc wizard", "default fleeing beogh
orc wizard", "default friendly orc wizard", "default fleeing orc
wizard", "default beogh orc wizard" and "default orc wizard", none of
which is successful.
The genus of orc wizards is "orc", so we retry the above using "orc"
instead of "orc wizard". The same is repeated for "friendly fleeing
beogh 'o'", and we still haven't found anything.
doesn't exist. Still, we haven't yet tried the prefix combinations:
"default friendly fleeing beogh humanoid" is still unsuccessful, but
with "default friendly fleeing humanoid" we finally strike gold:
doesn't exist. Annoyingly enough, neither does "friendly fleeing
humanoid".
Still, we haven't yet tried the prefix combinations: "default
friendly fleeing beogh humanoid" is still unsuccessful, but with
"default friendly fleeing humanoid" we finally strike gold:
shout.txt, monspeak.txt or one of the other files, in which case they
are replaced with a random value from the entry; or they may have
hardcoded expansions defined by the game.
shout.txt for the shouting database, or monspeak.txt or one of the
other files for the speech database, in which case they are replaced
with a random value from the entry; or they may have hardcoded
expansions defined by the game.
Note, though, that these only will take effect if a VISUAL message just
happens to be chosen. As stated above, the database search doesn't
really care whether a monster is supposed to be silent, so it may pick
any noisy monster speech, but the message output will care and refuse
to print such nonsense, so that in this case the monster will actually
stay silent after all.
All in all, chances of silent "speech" are lower (as is intended) but
only VISUAL messages even have a chance to be printed under these circumstances.
Note, though, that these only will take effect if the "silenced" prefix
hasn't been defined for this monster, or at least not in combination
with the other applying prefixes. In the case of silenced monsters,
first the database is searched for the monster key along with all its
prefixes including "silenced", and only if no message has been found,
the search will repeat ignoring the "silenced" prefix and only then
these special VISUAL cases can apply.
This will actually double the amount of rounds the database search
goes through, so you might expect chances for speaking to be higher.
In fact, though, the opposite is the case: once a matching message has
been found, the database search stops, and if this randomly chosen
speech message doesn't happen to be VISUAL it will simply not be
printed, so the monster stays silent after all. All in all, chances
are lower (as is intended) but only VISUAL messages even have a chance
to be printed under these circumstances.
As explained earlier, "silenced" is one of the prefixes that are
regarded as "less important" and can be ignored in the exact string
search. So that both specially defined silenced messages for a
particular monster and its normal VISUAL messages can sometimes take
effect, chances for actually skipping on silenced in the direct string
matching are 50:50.
Example 3:
The player has just cast Silence when a Killer Klown wanders into
view. (Uh oh!) This "silenced Killer Klown" is now attempting to say
something. The exact look-up is unsuccessful, but now there's a 50%
chance of skipping on the "silenced" prefix. If this route is chosen
we may get results such as
%%%%
Killer Klown
@The_monster@ giggles crazily.
@The_monster@ laughs merrily.
...
none of which, if chosen, would actually be printed, but luckily the
"Killer Klown" entry also contains VISUAL statements like the
following:
...
VISUAL:@The_monster@ beckons to you.
VISUAL:@The_monster@ does a flip.
...
If one of these is chosen, we get a non-verbal "speech" statement of
this silenced monster.
However, what happens if the other 50% take effect and we will *not*
ignore the "silenced" prefix? In this case, we'll simply continue
like in the earlier examples above, get no results for either of
"default silenced Killer Klown" or "default Killer Klown", and try
the genus next: human, which cannot be found in the database,
silenced or no. Neither will we find anything for the monster glyph
'@'. Now all that remains is to check the monster shape, which is
"humanoid" again. "silenced humanoid" won't get us any results, nor
will simply "humanoid", but "default silenced humanoid" has some
statements defined.
%%%%
default silenced humanoid
w:30
VISUAL:@The_monster@ says something but you don't hear anything.
w:30
VISUAL:@The_monster@ gestures.
...
All of the statements in these predefined "silenced" entries have to
be of the type VISUAL; otherwise they'll never get printed.
Note that the definition of which monsters are capable of speech is
entirely hardcoded. We think we made this apply to all sensible
monsters, i.e. all intelligent humanoid monsters, but of course it is
possible we've overlooked something, so if you find that your
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].
If you successfully got Crawl compiled, you can easily enable more
detailed debug information. All you need to do is add
#define DEBUG_MONSPEAK
somewhere in AppHdr.h, for example at the beginning of the section
entitled "Debugging Defines", and then compile the game anew, first
using "make clean", then "make wizard".
If you play with DEBUG_MONSPEAK compiled in, whenever the game is
searching the monspeak database you'll get extensive information on all
keys and prefixes tried. Once you're done testing don't forget to
remove (or comment out) the DEBUG_MONSPEAK setting as trying to
actually play that way would sure be annoying.
If you feel that your additions add something to the game and would
like to make them available to the general public, you can post them
(in the form of a diff file, or in plain text) as a feature request on
sourceforge.net [1] or in the newsgroup [2].
If you feel that your additions really add something to the game and
would like to make them available to the general public, you can post
them (in the form of a diff file, or in plain text) as a feature
request on sourceforge.net [1] or in the newsgroup [2].