And… rewrote large portions of monster_speech.txt. I think I may now finally have understood how this works, so in the not so distant I will have have to clean up some of the hopelessly complicated functions I introduced when I only thought I'd grasped the details. As usual, explaining it to someone else (if only in writing the documentation) really helped.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3591 c06c8d41-db1a-0410-9941-cceddc491573
64RZSOR4B4T5QO3FAERBTH2QJIQFSCIFHI4WYK3MDAEAZVAHRLVQC
S7KC3IFKCUNBO3XO4TV3KEQEIDZWSRJSKXY65DPONKMG54VHU3UQC
YM3U5YZEFIYLRJVDWQCSP75JL56WC36XLQSL5ZBT4IWSXYJRBCBAC
J7KCPTGASQNRAKVKQ5FCXZ75E75PK7I2BB6MFSI3COWU6HRKTWSAC
UQFNR6PCRYPSUWMFF7KHITOVE4DZJ4CEAHFSXO7NG6GYZRF5JKUAC
J6APXOT4QOGQFONWB7G546VTVF6QG42HVOROMHF7YBDJPR4K26OAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
AIIVH43Z5X3GTPFY4FXQRZPG6Y7QPH2KJ47VM2Q43PCGGD5MTMOAC
G5WLU3B4MR3ZDJLGR6OHNIMLZXGQS2EWBJ5YYY5J2GWM6DTD44BAC
BMHUBADDGIOZRVN4P3O5QKIDUYD4RFWBS7MP5X6LZWAYHUBRVD2QC
G7CTMQ3VNTAB73ZI3LNZHKTAJ5LEQEGG772MVFQQ5XXLCMJVORTQC
FYD4A5TIETIV2ZLFYWGHXANU6WQFKMVREHM7OZY2TAXSBMMCDLJAC
%%%%
################################################
# Religious messaging
################################################
good_god
Zin
The Shining One
Elyvilon
%%%%
holy_being
w:2
SPELL:@The_monster@ intones a prayer.
SPELL:A sense of peace washes over you.
w:1
@The_monster@ says, "Thou must remain pure in the battle against evil."
w:1
@The_monster@ says, "May @player_god@ always shine on thee, @player_name@!"
w:1
@The_monster@ says, "@player_god@ be with thee, mortal!"
w:1
@The_monster@ says, "Praised be @good_god@ in the heavens above!"
%%%%
neutral good god 'cap-A'
__NONE
w:2
@holy_being@
friendly good god 'cap-A'
@default friendly humanoid@
w:4
@holy_being@
%%%%
#############################################################
# evil monsters may also have some choicy things to say...
#############################################################
good god '&'
w:20
@_demon_taunt_@
w:1
@The_monster@ screams, "@player_god@ won't protect you, @player_name@!"
w:1
@The_monster@ screams, "@player_god@ is but a nuisance to me, mortal!"
%%%%
speech has been outsourced by Matthew Cline into shout.txt and
speak.txt. This makes changing existing messages, or adding new ones
really easy. This file will hopefully help you in this endeavour.
speech has been outsourced by Matthew Cline. This makes changing
existing messages, or adding new ones really easy. This file will
hopefully help you in this endeavour.
If you take a look through the two files, you'll see that all entries
have basically the same structure: a key, followed by one or more
values. Here is an example.
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.
Here is an example.
Key lookup is always case-insensitive. The game looks up several
different keys when finding monster speech.
Key lookup is always case-insensitive. The game will make many
different attempts when trying to find monster speech, all of which
are explained in detail below. You'll find some examples at the end of
this section.
1. The actual monster name.
Examples: "crystal golem", "default confused moth of wrath"
2. Then the monster glyph, with prefix "cap-" for capital letters.
Examples: "default 'cap-J'", "default confused 'k'"
3. A group description (such as 'insect' or 'humanoid') defined by the
monster's body shape (winged, tailed etc). The latter is entirely
hardcoded, though.
Examples: "default winged insect"
First, a monster may have one or more of a list of attributes that
might influence its speech. We will henceforth refer to these
attributes as prefixes.
default friendly/hostile fleeing silenced confused monster
default <attitude> fleeing silenced confused <player god> <monster>
where <attitude> can be any of friendly, neutral or hostile. Note that
the game generally treats neutral monsters like hostiles as they still
pose a danger to players.
First the database is searched for the whole prefix string, then,
reading from left to right, combinations are tested, beginning at
three prefixes and ending at none, at which time the prefix "default"
is used instead.
The <player god> prefix is constructed according to the religious
belief of the character. If the monster in question is a member of the
orc species and the character worships Beogh, the prefix "beogh" gets
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".
This allows fine-grained handling of monsters depending on your
character's religion status, e.g. orcs will use special speech for
Beogh worshippers, and neutral holy beings (Angel, and Daeva) may
shout messages of encouragement to followers of the good gods, while
demons will attempt to slander the good gods.
Now that the entire set of prefixes has be constructed, we only need
to add the monster name and start the database search.
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.
Only keys that match a search string perfectly (ignoring case) will
be used. Once all prefixes have been exhausted and still no match has
been found, the database lookup will try for a more general monster
description. There are several possible ways this is attempted, in the
following order:
Only keys that match a searching string perfectly (ignoring case) will
be used.
1. The actual monster name.
Examples: "crystal golem", "default confused moth of wrath"
2. The monster species and genus.
Examples: If "friendly ogre-mage" wasn't found, try "friendly ogre"
instead. Same for "dragon" if "swamp drake" was
unsuccessful.
3. Then the monster glyph, with prefix "cap-" for capital letters.
Examples: "default 'cap-J'", "default confused 'k'"
4. A group description (such as 'insect' or 'humanoid') defined by the
monster's body shape (winged, tailed etc). The definition of the
latter is entirely hardcoded, though.
Examples: "default winged insect", "default confused humanoid"
is prefixed to the search string.
is prefixed to the search string, depending on the monster type, e.g.
a "stupid humanoid" may still be smarter than a "smart arachnid".
Here's a list of allowed monster shapes that should hopefully be
self-explanatory:
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.
If no matching keys are found after all 3 rounds, the monster stays silent.
If no matching keys are found after all of these rounds, the monster
definitely stays silent.
Example 1:
The monster we want to make "speak" is a "confused killer bee".
However, such an entry cannot be found in the database, so the game
tries first for "default confused killer bee", then "default killer
bee", neither of which yields any results.
Species and genus are also plain "killer bee", so that doesn't help
us any. For the next round we try again with "confused 'k'", which,
by itself, also can't be found in the database, but once the prefix
comparison tries it together with "default", we have a match:
%%%%
default confused 'k'
SOUND:@The_monster@ buzzes around in tight circles.
%%%%
Example 2:
This time, we're interested in "friendly fleeing beogh orc wizard".
This obviously made up example also has no direct equivalent in the
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.
This is starting to get ridiculous, so it's time for desperate
measures:
With the help of some rather complicated functions the game works out
that orcs can be described as humanoids of average intelligence.
Thus, in a final attempt of making this orc wizard speak, we search
the database for "friendly fleeing beogh humanoid", something that,
not surprisingly (since Beogh and humanoid don't go well together)
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:
%%%%
default friendly fleeing humanoid
w:30
VISUAL:@The_monster@ tries to hide somewhere.
@The_monster@ @shouts@, "WAIT FOR ME, @player_name@! Could you help me?"
...
We'll leave it at that, even though the database code still has work
to do, namely add up the weights of all the entries (and there are
several more), and randomly choose one of them.
Weapon speech
-------------
shout/speak.txt, 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, 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.
Note that if variable replacement is recursive, so be careful to avoid
infinite loops.
Note that variable replacement is recursive, so be careful to avoid
infinite loops. Though the game will abort after a number of
replacement attempts, it will still look ugly in the monster speech.
@monster@ : Plain monster name, e.g. "rat" or "Sigmund"
@a_monster@ : Indefinite article plus monster name,
or only the name if it is unique ("Sigmund").
@the_monster@ : Definite article plus monster name ("the rat"),
or a possessive if it is friendly ("your rat"),
or only the name if it is unique ("Sigmund").
@something@ : Like @monster@, with monster name replaced by "something"
if the monster is invisible and the player cannot see invis.
@a_something@ : similar
@monster@ : Plain monster name, e.g. "rat" or "Sigmund"
@a_monster@ : Indefinite article plus monster name,
or only the name if it is unique ("Sigmund").
@the_monster@ : Definite article plus monster name ("the rat"),
or a possessive if it is friendly ("your rat"),
or only the name if it is unique ("Sigmund").
@something@ : Like @monster@, with monster name replaced by
"something" if the monster is invisible and the
player cannot see invisible.
@a_something@ : similar
@player_name@ : Player name.
@player_god@ : Player's god name, or "you" if non-religious.
@Player_god@ : Player's god name, or "You" if non-religious.
@god_is@ : replaced with "<god name> is" or "you are", if non-religious.
@God_is@ : replaced with "<god name> is" or "You are", if non-religious.
@surface@ : Whatever the monster is standing on.
@feature@ : The monster's square's feature description.
@pronoun@ : it, she, he, as appropriate
@possessive@ : its, her, his, as appropriate
@imp_taunt@ : imp type insults (see insult.txt)
@demon_taunt@ : demon type insults (see insult.txt)
@says@ : synonym of 'say' that fits monster's (hardcoded)
speech pattern and noise level.
@player_name@ : Player name.
@player_god@ : Player's god name, or "you" if non-religious.
@Player_god@ : Player's god name, or "You" if non-religious.
@god_is@ : replaced with "<god name> is" or "you are", if
non-religious.
@God_is@ : replaced with "<god name> is" or "You are", if
non-religious.
@surface@ : Whatever the monster is standing on.
@feature@ : The monster's square's feature description.
@pronoun@ : it, she, he, as appropriate
@possessive@ : its, her, his, as appropriate
@imp_taunt@ : imp type insults (see insult.txt)
@demon_taunt@ : demon type insults (see insult.txt)
@says@ : synonym of "say" that fits monsters' (hardcoded)
speech pattern and noise level.
Pre-defined variables in the database include _high_priest_,
_mercenary_guard_, _wizard_, _hostile_adventurer_, _friendly_imp_,
_hostile_imp_, and _tormentor_. There are also a few synonyms defined
at the beginning of speak.txt such as for @ATTACK@, @pointless@,
@shouts@, @wails@, and others.
Examples of pre-defined variables in the database include
_high_priest_, _mercenary_guard_, _wizard_, _hostile_adventurer_,
_friendly_imp_, _hostile_imp_, and _tormentor_, but more get added all
the time. There are also a few synonyms defined at the beginning of
speak.txt such as for @ATTACK@, @pointless@, @shouts@, @wails@, and
others.
Note, though, that these rarely will take effect as usually the
"silenced humanoid" types will take precedence. In the case of
silenced monsters, first the database is searched for the monster key
along with several prefixes including "silenced", and only if no
message can be found through all iterations of monster name, glyph and
group description, the search will repeat ignoring the "silenced"
prefix and only then these special VISUAL cases can apply.
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.
Messages may also be one of these special commands. These aren't
variables, so they aren't surrounded by @@. They are not expanded, but
rather they produce special game behavior.
Message entries may also be one of several special commands. These
aren't variables, so they aren't surrounded by @@. Accordingly, they
are not expanded, but rather they produce special game behavior.
(make monster friendly/unfriendly) and 's' (make monster shout). These
last two are of particular interest to monster speech designers.
(make monster friendly/neutral/hostile) and 's' (make monster shout).
These last two are of particular interest to monster speech designers.
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].