summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/docs/monster_speech.txt291
-rw-r--r--crawl-ref/init.txt7
-rw-r--r--crawl-ref/source/dat/database/insult.txt229
-rw-r--r--crawl-ref/source/dat/database/monspeak.txt23
-rw-r--r--crawl-ref/source/database.cc3
-rw-r--r--crawl-ref/source/delay.cc10
-rw-r--r--crawl-ref/source/misc.cc12
-rw-r--r--crawl-ref/source/mon-util.cc64
-rw-r--r--crawl-ref/source/monspeak.cc208
-rw-r--r--crawl-ref/source/player.cc64
-rw-r--r--crawl-ref/source/player.h2
11 files changed, 612 insertions, 301 deletions
diff --git a/crawl-ref/docs/monster_speech.txt b/crawl-ref/docs/monster_speech.txt
index 1eaba210e1..35ed996a2a 100644
--- a/crawl-ref/docs/monster_speech.txt
+++ b/crawl-ref/docs/monster_speech.txt
@@ -6,20 +6,62 @@ 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.
+
shout.txt handles message output for monsters noticing you.
monspeak.txt handles messages for monsters communicating with you.
-insult.txt handles insults thrown at you by imps and demons.
-
wpnnoise.txt handles randart weapons with the noises property.
+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.
+
+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].
+
A simple 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.
+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.
Here is an example.
%%%%
@@ -63,7 +105,7 @@ minor demons.
The rest of the entry consists of messages, separated by blank
lines. This is one of them. Each may be prefixed with an optional
-weight ('w:1'). A message will be chosen with a probability of its
+weight ("w:1"). A message will be chosen with a probability of its
weight out of the sum of weights for its entry. Weight defaults to 10
if not specified. In this example, this particular message will be
selected 1 time out of 12.
@@ -104,36 +146,58 @@ attributes as prefixes.
speech from "hostile" or "neutral". All prefixes are optional and
tested in the following order:
- default <attitude> fleeing silenced confused <player god> <monster>
+ default <attitude> fleeing silenced confused [related] <player god>
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.
+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.
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".
+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".
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
+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
+Once the entire set of prefixes has been determined, 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.
+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.
Only keys that match a search string perfectly (ignoring case) will
be used. Once all prefixes have been exhausted and still no match has
@@ -143,7 +207,7 @@ following order:
1. The actual monster name.
Examples: "crystal golem", "default confused moth of wrath"
-2. The monster species and genus.
+2. The monster genus.
Examples: If "friendly ogre-mage" wasn't found, try "friendly ogre"
instead. Same for "dragon" if "swamp drake" was
unsuccessful.
@@ -161,22 +225,27 @@ 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.
+ 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).
+
If no matching keys are found after all of these rounds, the monster
definitely stays silent.
+Examples
+--------
+
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
+ The monster genus is 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:
@@ -191,15 +260,18 @@ 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.
+ 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.
This is starting to get ridiculous, so it's time for desperate
measures:
@@ -208,9 +280,11 @@ Example 2:
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:
+ 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:
%%%%
default friendly fleeing humanoid
@@ -261,9 +335,10 @@ Variables
Values can contain variable references, which look like text
surrounded by @@. These variables may be defined by entries in
-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 that variable replacement is recursive, so be careful to avoid
infinite loops. Though the game will abort after a number of
@@ -283,6 +358,11 @@ The following variables are hardcoded:
@a_something@ : similar
@the_something@ : similar
@player_name@ : Player name.
+@player_species@: Player base species, with Draconian rather than the
+ actual subtype.
+@player_genus@ : Player genus, i.e. "Elf" rather than the exact type,
+ or "Ogre" instead of "Ogre-Mage".
+@player_genus_plural@ : pluralised genus form.
@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
@@ -314,7 +394,7 @@ 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
+monspeak.txt such as for @ATTACK@, @pointless@, @shouts@, @wails@, and
others.
Weapon noises also use a number of synonyms which are defined at the
@@ -363,21 +443,78 @@ and enforce output even when silenced.
VISUAL SPELL : MSGCH_MONSTER_SPELL
VISUAL WARN : MSGCH_WARN
-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.
+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.
+
+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.
+
For shouts the default is also MSGCH_TALK which is automatically
changed to MSGCH_TALK_VISUAL for monsters that can't speak (animals,
@@ -394,16 +531,15 @@ Special commands and variables
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.
+are not expanded, but rather they produce special game behaviour.
__NONE : no message
- __NEXT : try the next combination of attributes, or a more
- general monster description
+ __NEXT : try a more general monster description
__MORE : enforce a "--more--" prompt
__YOU_RESIST : print "You resist."
__NOTHING_HAPPENS : print "Nothing appears to happen."
-Some special keys are defined in speak.txt and shout.txt, such as
+Some special keys are defined in monspeak.txt and shout.txt, such as
__RESIST_OR_NOTHING and __SHOUT. These are normal variable expansions,
and may be used as such. They are given special-looking names because
Crawl looks up the noises a given monster can produce and looks for
@@ -446,22 +582,29 @@ adding a high weight value before it, e.g. w:5000, or equally
temporarily push it into another channel (e.g. MSGCH_WARN) to make it
more noticeable.
-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.
Publishing your changes
=======================
-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].
+
.. [1] http://crawl-ref.sourceforge.net
http://sourceforge.net/projects/crawl-ref
diff --git a/crawl-ref/init.txt b/crawl-ref/init.txt
index b546ab2624..2a7f25376c 100644
--- a/crawl-ref/init.txt
+++ b/crawl-ref/init.txt
@@ -4,9 +4,10 @@
# changes to the interface: command keys, monster colours, and the
# butchering interface. (New players should go ignore these lines.)
#
-# : crawl.read_options('docs/034_command_keys.txt')
# : crawl.read_options('docs/034_monster_glyphs.txt')
-# always_confirm_butcher = true
+# additional_macro_file = docs/034_command_keys.txt
+# use_old_selection_order = true
+# always_confirm_butcher = true
@@ -47,7 +48,7 @@
#
# name = Delilah
# remember_name = false
-# use_old_selection_order = false
+# use_old_selection_order = true
# weapon = (random | short sword | hand axe | spear | mace | trident)
# chaos_knight = (Xom | Makhleb | random)
# death_knight = (necromancy | Yredelemnul | random)
diff --git a/crawl-ref/source/dat/database/insult.txt b/crawl-ref/source/dat/database/insult.txt
index 11be0548eb..c68281146e 100644
--- a/crawl-ref/source/dat/database/insult.txt
+++ b/crawl-ref/source/dat/database/insult.txt
@@ -45,123 +45,6 @@ Thy @body_or_spiritual_part@ shall be my @meal@!
I @will_or_shall@ @feast_or_devour@ thy @body_or_spiritual_part@!
-# 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
%%%%
# capitalized "flee" verbs
run_away
@@ -304,6 +187,118 @@ whilst thou art whole
whilst life thou hast
%%%%
+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
+
+%%%%
+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
+%%%%
+#######################################################
+# generic insults consist of three random parts
+#######################################################
+generic_insult
+
+@insult_adjective1@ @insult_adjective2@ @insult_noun@
+%%%%
###############################################################
# adjective1, usually short and/or consisting of one word only
###############################################################
diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt
index cd50fdb7f5..1b26f010d9 100644
--- a/crawl-ref/source/dat/database/monspeak.txt
+++ b/crawl-ref/source/dat/database/monspeak.txt
@@ -530,6 +530,25 @@ VISUAL:@The_monster@ tries to recover @possessive@ lost courage.
VISUAL:@The_monster@ looks really @rattled@.
## END fleeing humanoid ##
%%%%
+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."
+%%%%
+default friendly related humanoid
+
+w:2
+@friendly_family@
+
+@default friendly humanoid@
+%%%%
default friendly humanoid
@The_monster@ yells, "Run! I'll cover you!"
@@ -708,8 +727,6 @@ __NONE
w:1
@_friendly_imp_@
-%%%%
-
####################################################################
# Player ghost phrases. Ghosts with different classes can be given
# different messages.
@@ -815,7 +832,7 @@ VISUAL:@The_monster@ grins evilly.
@The_monster@ screams, "Give me everything and get away!"
-@The_monster@ screams, "I want your rings! And amulets! And... EVERYTHIGNG!"
+@The_monster@ screams, "I want your rings! And amulets! And... EVERYTHING!"
@The_monster@ screams, "I hate dirty adventurers like you!"
diff --git a/crawl-ref/source/database.cc b/crawl-ref/source/database.cc
index 853d5a9a4a..fc33b797a8 100644
--- a/crawl-ref/source/database.cc
+++ b/crawl-ref/source/database.cc
@@ -742,6 +742,9 @@ std::string getSpeakString(const std::string &monst)
int num_replacements = 0;
+#ifdef DEBUG_MONSPEAK
+ mprf(MSGCH_DIAGNOSTICS, "monster speech lookup for %s", monst.c_str());
+#endif
return getRandomizedStr(speakDB, monst, "", num_replacements);
}
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc
index cf282c8458..5448984ea0 100644
--- a/crawl-ref/source/delay.cc
+++ b/crawl-ref/source/delay.cc
@@ -859,13 +859,13 @@ static void finish_delay(const delay_queue_item &delay)
(you.has_usable_claws() || you.mutation[MUT_FANGS] == 3) ?
"ripping" : "chopping");
- 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))
simple_god_message(" expects more respect for your departed "
"relatives.");
-
+ else if (you.religion == GOD_ZIN && mons_intel(item.plus) >= I_NORMAL)
+ simple_god_message(" expects more respect for this departed "
+ "soul.");
+
if (you.species == SP_VAMPIRE &&
mons_corpse_effect(item.plus) != CE_HCL &&
(!god_likes_butchery(you.religion) ||
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 4cb63046bc..ad5f84db63 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -650,12 +650,12 @@ void up_stairs(dungeon_feature_type force_stair,
crawl_state.level_annotation_shown = false;
- if (level_annotation_has("WARN", next_level_id)
+ if (level_annotation_has("!", next_level_id)
&& next_level_id != level_id::current()
&& next_level_id.level_type == LEVEL_DUNGEON && !force_stair)
{
mpr("Warning: level annotation for next level is:", MSGCH_PROMPT);
- mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT);
+ mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PLAIN, YELLOW);
if (!yesno("Enter next level anyway?", true, 0, true, false))
{
@@ -711,7 +711,7 @@ void up_stairs(dungeon_feature_type force_stair,
old_level_info.update();
// Make sure we return to our main dungeon level... labyrinth entrances
- // in the abyss or pandemonium a bit trouble (well the labyrinth does
+ // in the abyss or pandemonium are a bit trouble (well the labyrinth does
// provide a way out of those places, its really not that bad I suppose)
if (level_type_exits_up(you.level_type))
you.level_type = LEVEL_DUNGEON;
@@ -885,6 +885,7 @@ void up_stairs(dungeon_feature_type force_stair,
}
}
}
+ request_autopickup();
} // end up_stairs()
void down_stairs( int old_level, dungeon_feature_type force_stair,
@@ -1031,12 +1032,12 @@ void down_stairs( int old_level, dungeon_feature_type force_stair,
crawl_state.level_annotation_shown = false;
- if (level_annotation_has("WARN", next_level_id)
+ if (level_annotation_has("!", next_level_id)
&& next_level_id != level_id::current()
&& next_level_id.level_type == LEVEL_DUNGEON && !force_stair)
{
mpr("Warning: level annotation for next level is:", MSGCH_PROMPT);
- mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT);
+ mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PLAIN, YELLOW);
if (!yesno("Enter next level anyways?", true, 0, true, false))
{
@@ -1393,6 +1394,7 @@ void down_stairs( int old_level, dungeon_feature_type force_stair,
new_level_info.update_stair(you.x_pos, you.y_pos, lp, true);
}
}
+ request_autopickup();
} // end down_stairs()
void trackers_init_new_level(bool transit)
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 8e5e5450c7..e0a2cd14ee 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -5418,6 +5418,21 @@ static std::string get_species_insult(const std::string type)
return (insult);
}
+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);
+}
+
// Replaces the "@foo@" strings in monster shout and monster speak
// definitions.
std::string do_mon_str_replacements(const std::string &in_msg,
@@ -5466,6 +5481,12 @@ std::string do_mon_str_replacements(const std::string &in_msg,
}
msg = replace_all(msg, "@player_name@", you.your_name);
+ msg = replace_all(msg, "@player_species@",
+ species_name(you.species, 1).c_str());
+ msg = replace_all(msg, "@player_genus@",
+ species_name(you.species, 1, true).c_str());
+ msg = replace_all(msg, "@player_genus_plural@",
+ pluralise_player_genus().c_str());
if (player_monster_visible(monster))
{
@@ -5518,9 +5539,12 @@ std::string do_mon_str_replacements(const std::string &in_msg,
msg = replace_all(msg, "@Player_god@", replace_god_name(false, true));
// replace with species specific insults
- 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"));
+ }
static const char * sound_list[] =
{
@@ -5618,8 +5642,8 @@ mon_body_shape get_mon_shape(const int type)
case 'c': // centaurs
return(MON_SHAPE_CENTAUR);
case 'd': // draconions and drakes
- if (mons_genus(type) == MONS_DRACONIAN ||
- mons_class_flag(type, M_HUMANOID))
+ if (mons_genus(type) == MONS_DRACONIAN
+ || mons_class_flag(type, M_HUMANOID))
{
if (mons_class_flag(type, M_FLIES))
return(MON_SHAPE_HUMANOID_WINGED_TAILED);
@@ -5640,38 +5664,39 @@ mon_body_shape get_mon_shape(const int type)
else
return(MON_SHAPE_HUMANOID);
case 'h': // hounds
- case 'j': // jackals
return(MON_SHAPE_QUADRUPED);
+ case 'j': // snails
+ return(MON_SHAPE_SNAIL);
case 'k': // killer bees
return(MON_SHAPE_INSECT_WINGED);
case 'l': // lizards
return(MON_SHAPE_QUADRUPED);
- 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);
case 'n': // necrophages and ghouls
return(MON_SHAPE_HUMANOID);
case 'o': // orcs
return(MON_SHAPE_HUMANOID);
case 'p': // ghosts
- if (type != MONS_INSUBSTANTIAL_WISP &&
- type != MONS_PLAYER_GHOST)
+ if (type != MONS_INSUBSTANTIAL_WISP
+ && type != MONS_PLAYER_GHOST) // handled elsewhere
+ {
return(MON_SHAPE_HUMANOID);
+ }
+ break;
case 'q': // quasists
return(MON_SHAPE_HUMANOID_TAILED);
case 'r': // rodents
return(MON_SHAPE_QUADRUPED);
- case 's': // arachnids and centidpeds
+ case 's': // arachnids and centipedes
if (type == MONS_GIANT_CENTIPEDE)
return(MON_SHAPE_CENTIPEDE);
else
return(MON_SHAPE_ARACHNID);
case 'u': // ugly things are humanoid???
return(MON_SHAPE_HUMANOID);
+ case 't': // minotaurs
+ return(MON_SHAPE_HUMANOID);
case 'v': // vortices and elementals
return(MON_SHAPE_MISC);
case 'w': // worms
@@ -5703,8 +5728,11 @@ mon_body_shape get_mon_shape(const int type)
return(MON_SHAPE_QUADRUPED_TAILLESS);
case 'G': // floating eyeballs and orbs
return(MON_SHAPE_ORB);
- case 'H': // hippogriffs and griffns
- return(MON_SHAPE_QUADRUPED_WINGED);
+ case 'H': // manticores, hippogriffs and griffins
+ if (type == MONS_MANTICORE)
+ return(MON_SHAPE_QUADRUPED);
+ else
+ return(MON_SHAPE_QUADRUPED_WINGED);
case 'I': // ice beasts
return(MON_SHAPE_QUADRUPED);
case 'J': // jellies and jellyfish
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index ed373e7944..8528276860 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -42,34 +42,84 @@
#include "stuff.h"
#include "view.h"
-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,
const std::string key,
- const monsters *monster,
+ bool ignore_hostile = false,
+ bool ignore_related = false,
+ bool ignore_religion = false,
bool ignore_silenced = false)
{
- std::string prefix = "";
+ bool hostile = false;
+ bool related = false;
+ bool religion = false;
bool silenced = false;
+
+ std::string prefix = "";
+ std::string msg = "";
const int size = prefixes.size();
for (int i = 0; i < size; i++)
{
- 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);
}
+ return msg;
+}
- std::string msg = "";
+static std::string get_speak_string(const std::vector<std::string> prefixes,
+ const std::string key,
+ const monsters *monster)
+{
+ std::string msg = try_exact_string(prefixes, key);
- // try string of all prefixes
- msg = getSpeakString(prefix + key);
- if (msg != "")
+ if (!msg.empty())
return msg;
// Combinations of prefixes by threes
+ const int size = prefixes.size();
+ std::string prefix = "";
if (size >= 3)
{
for (int i = 0; i < (size - 2); i++)
@@ -81,7 +131,8 @@ static std::string get_speak_string(const std::vector<std::string> prefixes,
prefix += prefixes[k] + " ";
msg = getSpeakString("default " + prefix + key);
- if (msg != "")
+
+ if (!msg.empty())
return msg;
}
}
@@ -96,7 +147,8 @@ static std::string get_speak_string(const std::vector<std::string> prefixes,
prefix += prefixes[j] + " ";
msg = getSpeakString("default " + prefix + key);
- if (msg != "")
+
+ if (!msg.empty())
return msg;
}
}
@@ -109,7 +161,8 @@ static std::string get_speak_string(const std::vector<std::string> prefixes,
prefix = prefixes[i] + " ";
msg = getSpeakString("default " + prefix + key);
- if (msg != "")
+
+ if (!msg.empty())
return msg;
}
}
@@ -117,10 +170,6 @@ static std::string get_speak_string(const std::vector<std::string> prefixes,
// No prefixes
msg = getSpeakString("default " + key);
- // try the same ignoring silence
- if (msg == "" && silenced)
- return get_speak_string(prefixes, key, monster, true);
-
return msg;
}
@@ -139,12 +188,11 @@ static std::string player_ghost_speak_str(const monsters *monster,
prefix += " ";
}
+ // first try together with class name
std::string msg = getSpeakString(prefix + ghost_class + " player ghost");
- if (msg == "__NONE")
- return "";
-
- if (msg == "" || msg == "__NEXT")
+ // else try without class name
+ if (msg.empty() || msg == "__NEXT")
msg = getSpeakString(prefix + "player ghost");
return msg;
@@ -173,19 +221,24 @@ bool mons_speaks(const monsters *monster)
return false;
}
- // charmed monsters aren't too expressive
- if (monster->has_ench(ENCH_CHARM))
- return false;
-
// berserk monsters just want your hide.
if (monster->has_ench(ENCH_BERSERK))
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 (monster->attitude == ATT_FRIENDLY)
- prefixes.push_back("friendly");
- else if (monster->attitude == ATT_NEUTRAL)
+ if (mons_neutral(monster))
+ {
+ if (coinflip()) // neutrals speak half as often
+ return false;
prefixes.push_back("neutral");
+ }
+ else if (mons_friendly(monster))
+ prefixes.push_back("friendly");
else
prefixes.push_back("hostile");
@@ -201,17 +254,38 @@ bool mons_speaks(const monsters *monster)
if (monster->has_ench(ENCH_CONFUSION))
prefixes.push_back("confused");
-
+
+ // only look at the current player form
+ if (is_player_same_species(monster->type, true))
+ prefixes.push_back("related");
+
// Add Beogh to list of prefixes for orcs (hostile and friendly) if you
// worship Beogh. (This assumes you being a Hill Orc, so might have odd
- // 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))
+ {
prefixes.push_back("beogh");
+ }
else if (is_good_god(you.religion))
prefixes.push_back("good god");
else if (is_evil_god(you.religion))
prefixes.push_back("evil god");
+#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
+
std::string msg;
// First, try its exact name
@@ -225,23 +299,13 @@ bool mons_speaks(const monsters *monster)
else
msg = get_speak_string(prefixes, monster->name(DESC_PLAIN), monster);
- // The exact name brought no results, try species.
- if (msg.empty() || msg == "__NEXT")
+ // The exact name brought no results, try monster genus.
+ if ((msg.empty() || msg == "__NEXT")
+ && mons_genus(monster->type) != monster->type)
{
- 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),
monster);
- }
- // 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);
- }
}
// __NONE means to be silent, and __NEXT means to try the next,
@@ -249,16 +313,21 @@ bool mons_speaks(const monsters *monster)
// string.
if (msg == "__NONE")
+ {
+#ifdef DEBUG_MONSPEAK
+ mpr("result: \"__NONE\"!", MSGCH_DIAGNOSTICS);
+#endif
return false;
+ }
// Now that we're not dealing with a specific monster name, include
// whether or not it can move in the prefix
if (mons_is_stationary(monster))
prefixes.insert(prefixes.begin(), "stationary");
- // Names for the monster, its species and its genus all failed,
+ // Names for the exact monster name and its genus have failed,
// so try the monster's glyph/symbol.
- if (msg == "" || msg == "__NEXT")
+ if (msg.empty() || msg == "__NEXT")
{
std::string key = "'";
@@ -271,7 +340,12 @@ bool mons_speaks(const monsters *monster)
msg = get_speak_string(prefixes, key, monster);
}
if (msg == "__NONE")
+ {
+#ifdef DEBUG_MONSPEAK
+ mpr("result: \"__NONE\"!", MSGCH_DIAGNOSTICS);
+#endif
return false;
+ }
// Monster symbol didn't work, try monster shape. Since we're
// dealing with just the monster shape, change the prefix to
@@ -314,14 +388,20 @@ bool mons_speaks(const monsters *monster)
prefixes.insert(prefixes.begin(), "smart");
}
- if (msg == "" || msg == "__NEXT")
+ if (msg.empty() || msg == "__NEXT")
msg = get_speak_string(prefixes, get_mon_shape_str(shape), monster);
+
if (msg == "__NONE")
+ {
+#ifdef DEBUG_MONSPEAK
+ mpr("result: \"__NONE\"!", MSGCH_DIAGNOSTICS);
+#endif
return false;
+ }
// If we failed to get a message with a winged or tailed humanoid,
// or a naga or centaur, try moving closer to plain humanoid
- if ((msg == "" || msg == "__NEXT") && shape > MON_SHAPE_HUMANOID
+ if ((msg.empty() || msg == "__NEXT") && shape > MON_SHAPE_HUMANOID
&& shape <= MON_SHAPE_NAGA)
{
// If a humanoid monster has both wings and a tail, try
@@ -335,15 +415,21 @@ bool mons_speaks(const monsters *monster)
monster);
// Only be silent if both tailed and winged return __NONE
- if (msg == "" || msg == "__NONE" || msg == "__NEXT")
+ if (msg.empty() || msg == "__NONE" || msg == "__NEXT")
{
shape = MON_SHAPE_HUMANOID_WINGED;
std::string msg2;
msg2 = get_speak_string(prefixes,
get_mon_shape_str(shape),
monster);
+
if (msg == "__NONE" && msg2 == "__NONE")
+ {
+#ifdef DEBUG_MONSPEAK
+ mpr("result: \"__NONE\"!", MSGCH_DIAGNOSTICS);
+#endif
return false;
+ }
if (msg2 == "__NONE")
msg2 = "";
@@ -351,7 +437,7 @@ bool mons_speaks(const monsters *monster)
msg = msg2;
}
} // if (shape == MON_SHAPE_HUMANOID_WINGED_TAILED)
- if (msg == "" || msg == "__NONE" || msg == "__NEXT")
+ if (msg.empty() || msg == "__NONE" || msg == "__NEXT")
{
shape = MON_SHAPE_HUMANOID;
msg = get_speak_string(prefixes,
@@ -359,8 +445,14 @@ bool mons_speaks(const monsters *monster)
monster);
}
}
- if (msg == "__NONE" || msg == "")
+ if (msg.empty() || msg == "__NONE")
+ {
+#ifdef DEBUG_MONSPEAK
+ mprf(MSGCH_DIAGNOSTICS, "final result: %s!",
+ (msg.empty() ? "empty" : "\"__NONE\""));
+#endif
return false;
+ }
if (msg == "__NEXT")
{
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 772462174b..9a1f80d31a 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -426,54 +426,83 @@ bool player_genus(genus_type which_genus, species_type species)
return (false);
} // end player_genus()
-bool is_player_same_species(const int mon)
+// If transform is true, compare with current transformation instead
+// of (or in addition to) underlying species.
+// (See mon-data.h for species/genus use.)
+bool is_player_same_species(const int mon, bool transform)
{
+ 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
+ }
+ }
+
switch (you.species)
{
case SP_HUMAN:
- if (mons_species(mon) == MONS_HUMAN)
+ if (mons_genus(mon) == MONS_HUMAN)
return (true);
return (false);
case SP_CENTAUR:
- if (mons_species(mon) == MONS_CENTAUR)
+ if (mons_genus(mon) == MONS_CENTAUR)
return (true);
return (false);
case SP_OGRE:
case SP_OGRE_MAGE:
- if (mons_species(mon) == MONS_OGRE)
+ if (mons_genus(mon) == MONS_OGRE)
return (true);
return (false);
case SP_TROLL:
- if (mons_species(mon) == MONS_TROLL)
+ if (mons_genus(mon) == MONS_TROLL)
return (true);
return (false);
case SP_MUMMY:
- if (mons_species(mon) == MONS_MUMMY)
+ if (mons_genus(mon) == MONS_MUMMY)
return (true);
return (false);
- case SP_GHOUL:
+ case SP_GHOUL: // genus would include necrophage and rotting hulk
if (mons_species(mon) == MONS_GHOUL)
return (true);
return (false);
case SP_VAMPIRE:
- if (mons_species(mon) == MONS_VAMPIRE)
+ if (mons_genus(mon) == MONS_VAMPIRE)
return (true);
return (false);
case SP_MINOTAUR:
- if (mons_species(mon) == MONS_MINOTAUR)
+ if (mons_genus(mon) == MONS_MINOTAUR)
return (true);
return (false);
case SP_NAGA:
- if (mons_species(mon) == MONS_NAGA)
+ if (mons_genus(mon) == MONS_NAGA)
return (true);
return (false);
case SP_HILL_ORC:
- if (mons_species(mon) == MONS_ORC)
+ if (mons_genus(mon) == MONS_ORC)
return (true);
return (false);
case SP_MERFOLK:
- if (mons_species(mon) == MONS_MERFOLK
- || mons_species(mon) == MONS_MERMAID)
+ if (mons_genus(mon) == MONS_MERFOLK
+ || mons_genus(mon) == MONS_MERMAID)
{
return (true);
}
@@ -483,7 +512,7 @@ bool is_player_same_species(const int mon)
case SP_HIGH_ELF:
case SP_DEEP_ELF:
case SP_SLUDGE_ELF:
- if (mons_species(mon) == MONS_ELF)
+ if (mons_genus(mon) == MONS_ELF)
return (true);
return (false);
@@ -496,12 +525,12 @@ bool is_player_same_species(const int mon)
case SP_PURPLE_DRACONIAN:
case SP_MOTTLED_DRACONIAN:
case SP_PALE_DRACONIAN:
- if (mons_species(mon) == MONS_DRACONIAN)
+ if (mons_genus(mon) == MONS_DRACONIAN)
return (true);
return (false);
case SP_KOBOLD:
- if (mons_species(mon) == MONS_KOBOLD)
+ if (mons_genus(mon) == MONS_KOBOLD)
return (true);
return (false);
default: // no monster equivalent
@@ -3934,7 +3963,8 @@ std::string species_name(species_type speci, int level, bool genus, bool adj)
// We've previously declared that these are radically
// different from Ogres... so we're not going to
// refer to them as Ogres. -- bwr
- case SP_OGRE_MAGE: res = "Ogre-Mage"; break;
+ // Not the species, but genus... why not? -- jpeg
+ case SP_OGRE_MAGE: res = (genus? "Ogre" : "Ogre-Mage"); break;
case SP_CENTAUR: res = "Centaur"; break;
case SP_SPRIGGAN: res = "Spriggan"; break;
case SP_MINOTAUR: res = "Minotaur"; break;
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index 378629316c..11840c558d 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -326,7 +326,7 @@ void redraw_skill(const std::string &your_name, const std::string &class_name);
* *********************************************************************** */
bool player_genus( genus_type which_genus,
species_type species = SP_UNKNOWN );
-bool is_player_same_species( const int mon );
+bool is_player_same_species( const int mon, bool = false );
bool you_can_wear( int eq, bool special_armour = false );
bool player_has_feet(void);