summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/view.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-11-10 12:42:34 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-11-10 12:44:19 +0100
commit3977b123c4b05ea20323805fa901a213ecaeac3e (patch)
treea18491061f515ba43c35aaff1e51366cc5f7af3b /crawl-ref/source/view.cc
parentba3b7dbaad47d6e41d3763ec7dd6215bd74cfb5f (diff)
downloadcrawl-ref-3977b123c4b05ea20323805fa901a213ecaeac3e.tar.gz
crawl-ref-3977b123c4b05ea20323805fa901a213ecaeac3e.zip
Split shouting and stealth code from view.cc.
Diffstat (limited to 'crawl-ref/source/view.cc')
-rw-r--r--crawl-ref/source/view.cc519
1 files changed, 1 insertions, 518 deletions
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index aa08afb7bd..05e43236bc 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -7,6 +7,7 @@
#include "AppHdr.h"
#include "view.h"
+#include "shout.h"
#include <stdint.h>
#include <string.h>
@@ -85,8 +86,6 @@
crawl_view_geometry crawl_view;
-extern int stealth; // defined in acr.cc
-
bool inside_level_bounds(int x, int y)
{
return (x > 0 && x < GXM && y > 0 && y < GYM);
@@ -142,245 +141,6 @@ void flush_comes_into_view()
handle_seen_interrupt(mon);
}
-void handle_monster_shouts(monsters* monster, bool force)
-{
- if (!force && x_chance_in_y(you.skills[SK_STEALTH], 30))
- return;
-
- // Friendly or neutral monsters don't shout.
- if (!force && (monster->friendly() || monster->neutral()))
- return;
-
- // Get it once, since monster might be S_RANDOM, in which case
- // mons_shouts() will return a different value every time.
- // Demon lords will insult you as a greeting, but later we'll
- // choose a random verb and loudness for them.
- shout_type s_type = mons_shouts(monster->type, false);
-
- // Silent monsters can give noiseless "visual shouts" if the
- // player can see them, in which case silence isn't checked for.
- if (s_type == S_SILENT && !monster->visible_to(&you)
- || s_type != S_SILENT && !player_can_hear(monster->pos()))
- {
- return;
- }
-
- mon_acting mact(monster);
-
- std::string default_msg_key = "";
-
- switch (s_type)
- {
- case S_SILENT:
- // No default message.
- break;
- case S_SHOUT:
- default_msg_key = "__SHOUT";
- break;
- case S_BARK:
- default_msg_key = "__BARK";
- break;
- case S_SHOUT2:
- default_msg_key = "__TWO_SHOUTS";
- break;
- case S_ROAR:
- default_msg_key = "__ROAR";
- break;
- case S_SCREAM:
- default_msg_key = "__SCREAM";
- break;
- case S_BELLOW:
- default_msg_key = "__BELLOW";
- break;
- case S_SCREECH:
- default_msg_key = "__SCREECH";
- break;
- case S_BUZZ:
- default_msg_key = "__BUZZ";
- break;
- case S_MOAN:
- default_msg_key = "__MOAN";
- break;
- case S_GURGLE:
- default_msg_key = "__GURGLE";
- break;
- case S_WHINE:
- default_msg_key = "__WHINE";
- break;
- case S_CROAK:
- default_msg_key = "__CROAK";
- break;
- case S_GROWL:
- default_msg_key = "__GROWL";
- break;
- case S_HISS:
- default_msg_key = "__HISS";
- break;
- case S_DEMON_TAUNT:
- default_msg_key = "__DEMON_TAUNT";
- break;
- default:
- default_msg_key = "__BUGGY"; // S_LOUD, S_VERY_SOFT, etc. (loudness)
- }
-
- // Now that we have the message key, get a random verb and noise level
- // for pandemonium lords.
- if (s_type == S_DEMON_TAUNT)
- s_type = mons_shouts(monster->type, true);
-
- std::string msg, suffix;
- std::string key = mons_type_name(monster->type, DESC_PLAIN);
-
- // Pandemonium demons have random names, so use "pandemonium lord"
- if (monster->type == MONS_PANDEMONIUM_DEMON)
- key = "pandemonium lord";
- // Search for player ghost shout by the ghost's class.
- else if (monster->type == MONS_PLAYER_GHOST)
- {
- const ghost_demon &ghost = *(monster->ghost);
- std::string ghost_class = get_class_name(ghost.job);
-
- key = ghost_class + " player ghost";
-
- default_msg_key = "player ghost";
- }
-
- // Tries to find an entry for "name seen" or "name unseen",
- // and if no such entry exists then looks simply for "name".
- // We don't use "you.can_see(monster)" here since that would return
- // false for submerged monsters, but submerged monsters will be forced
- // to surface before they shout, thus removing that source of
- // non-visibility.
- if (mons_near(monster) && (!monster->invisible() || you.can_see_invisible()))
- suffix = " seen";
- else
- suffix = " unseen";
-
- msg = getShoutString(key, suffix);
-
- if (msg == "__DEFAULT" || msg == "__NEXT")
- msg = getShoutString(default_msg_key, suffix);
- else if (msg.empty())
- {
- // NOTE: Use the hardcoded glyph rather than that returned
- // by mons_char(), since the result of mons_char() can be
- // changed by user settings.
- char mchar = get_monster_data(monster->type)->showchar;
-
- // See if there's a shout for all monsters using the
- // same glyph/symbol
- std::string glyph_key = "'";
-
- // Database keys are case-insensitve.
- if (isupper(mchar))
- glyph_key += "cap-";
-
- glyph_key += mchar;
- glyph_key += "'";
- msg = getShoutString(glyph_key, suffix);
-
- if (msg.empty() || msg == "__DEFAULT")
- msg = getShoutString(default_msg_key, suffix);
- }
-
- if (default_msg_key == "__BUGGY")
- {
- msg::streams(MSGCH_SOUND) << "You hear something buggy!"
- << std::endl;
- }
- else if (s_type == S_SILENT && (msg.empty() || msg == "__NONE"))
- {
- ; // No "visual shout" defined for silent monster, do nothing.
- }
- else if (msg.empty()) // Still nothing found?
- {
- msg::streams(MSGCH_DIAGNOSTICS)
- << "No shout entry for default shout type '"
- << default_msg_key << "'" << std::endl;
-
- msg::streams(MSGCH_SOUND) << "You hear something buggy!"
- << std::endl;
- }
- else if (msg == "__NONE")
- {
- msg::streams(MSGCH_DIAGNOSTICS)
- << "__NONE returned as shout for non-silent monster '"
- << default_msg_key << "'" << std::endl;
- msg::streams(MSGCH_SOUND) << "You hear something buggy!"
- << std::endl;
- }
- else
- {
- msg_channel_type channel = MSGCH_TALK;
-
- std::string param = "";
- std::string::size_type pos = msg.find(":");
-
- if (pos != std::string::npos)
- {
- param = msg.substr(0, pos);
- msg = msg.substr(pos + 1);
- }
-
- if (s_type == S_SILENT || param == "VISUAL")
- channel = MSGCH_TALK_VISUAL;
- else if (param == "SOUND")
- channel = MSGCH_SOUND;
-
- // Monster must come up from being submerged if it wants to shout.
- if (monster->submerged())
- {
- if (!monster->del_ench(ENCH_SUBMERGED))
- {
- // Couldn't unsubmerge.
- return;
- }
-
- if (you.can_see(monster))
- {
- if (monster->type == MONS_AIR_ELEMENTAL)
- monster->seen_context = "thin air";
- else if (monster->type == MONS_TRAPDOOR_SPIDER)
- monster->seen_context = "leaps out";
- else if (!monster_habitable_grid(monster, DNGN_FLOOR))
- monster->seen_context = "bursts forth shouting";
- else
- monster->seen_context = "surfaces";
-
- // Give interrupt message before shout message.
- handle_seen_interrupt(monster);
- }
- }
-
- if (channel != MSGCH_TALK_VISUAL || you.can_see(monster))
- {
- msg = do_mon_str_replacements(msg, monster, s_type);
- msg::streams(channel) << msg << std::endl;
-
- // Otherwise it can move away with no feedback.
- if (you.can_see(monster))
- {
- if (!(monster->flags & MF_WAS_IN_VIEW))
- handle_seen_interrupt(monster);
- seen_monster(monster);
- }
- }
- }
-
- const int noise_level = get_shout_noise_level(s_type);
- const bool heard = noisy(noise_level, monster->pos(), monster->mindex());
-
- if (Options.tutorial_left && (heard || you.can_see(monster)))
- learned_something_new(TUT_MONSTER_SHOUT, monster->pos());
-}
-
-#ifdef WIZARD
-void force_monster_shout(monsters* monster)
-{
- handle_monster_shouts(monster, true);
-}
-#endif
-
void monster_grid_updates()
{
for (int s = 0; s < MAX_MONSTERS; ++s)
@@ -501,283 +261,6 @@ void update_monsters_in_view()
}
}
-bool check_awaken(monsters* monster)
-{
- // Monsters put to sleep by ensorcelled hibernation will sleep
- // at least one turn.
- if (monster->has_ench(ENCH_SLEEPY))
- return (false);
-
- // Berserkers aren't really concerned about stealth.
- if (you.berserk())
- return (true);
-
- // I assume that creatures who can sense invisible are very perceptive.
- int mons_perc = 10 + (mons_intel(monster) * 4) + monster->hit_dice
- + mons_sense_invis(monster) * 5;
-
- bool unnatural_stealthy = false; // "stealthy" only because of invisibility?
-
- // Critters that are wandering but still have MHITYOU as their foe are
- // still actively on guard for the player, even if they can't see you.
- // Give them a large bonus -- handle_behaviour() will nuke 'foe' after
- // a while, removing this bonus.
- if (mons_is_wandering(monster) && monster->foe == MHITYOU)
- mons_perc += 15;
-
- if (!you.visible_to(monster))
- {
- mons_perc -= 75;
- unnatural_stealthy = true;
- }
-
- if (monster->asleep())
- {
- if (monster->holiness() == MH_NATURAL)
- {
- // Monster is "hibernating"... reduce chance of waking.
- if (monster->has_ench(ENCH_SLEEP_WARY))
- mons_perc -= 10;
- }
- else // unnatural creature
- {
- // Unnatural monsters don't actually "sleep", they just
- // haven't noticed an intruder yet... we'll assume that
- // they're diligently on guard.
- mons_perc += 10;
- }
- }
-
- // If you've been tagged with Corona or are Glowing, the glow
- // makes you extremely unstealthy.
- if (you.backlit() && you.visible_to(monster))
- mons_perc += 50;
-
- if (mons_perc < 0)
- mons_perc = 0;
-
- if (x_chance_in_y(mons_perc + 1, stealth))
- return (true); // Oops, the monster wakes up!
-
- // You didn't wake the monster!
- if (player_light_armour(true)
- && you.can_see(monster) // to avoid leaking information
- && you.burden_state == BS_UNENCUMBERED
- && !you.attribute[ATTR_SHADOWS]
- && !monster->wont_attack()
- && !mons_class_flag(monster->type, M_NO_EXP_GAIN)
- // If invisible, training happens much more rarely.
- && (!unnatural_stealthy && one_chance_in(25) || one_chance_in(100)))
- {
- exercise(SK_STEALTH, 1);
- }
-
- return (false);
-}
-
-// Noisy now has a messenging service for giving messages to the
-// player is appropriate.
-//
-// Returns true if the PC heard the noise.
-bool noisy(int loudness, const coord_def& where, const char *msg, int who,
- bool mermaid)
-{
- bool ret = false;
-
- if (loudness <= 0)
- return (false);
-
- // If the origin is silenced there is no noise.
- if (silenced(where))
- return (false);
-
- const int dist = loudness * loudness;
- const int player_distance = distance( you.pos(), where );
-
- // Message the player.
- if (player_distance <= dist && player_can_hear( where ))
- {
- if (msg)
- mpr( msg, MSGCH_SOUND );
-
- you.check_awaken(dist - player_distance);
-
- if (!mermaid)
- you.beholders_check_noise(loudness);
-
- ret = true;
- }
-
- for (int p = 0; p < MAX_MONSTERS; p++)
- {
- monsters* monster = &menv[p];
-
- if (!monster->alive())
- continue;
-
- // Monsters arent' affected by their own noise. We don't check
- // where == monster->pos() since it might be caused by the
- // Projected Noise spell.
- if (p == who)
- continue;
-
- if (distance(monster->pos(), where) <= dist
- && !silenced(monster->pos()))
- {
- // If the noise came from the character, any nearby monster
- // will be jumping on top of them.
- if (where == you.pos())
- behaviour_event( monster, ME_ALERT, MHITYOU, you.pos() );
- else if (mermaid && mons_primary_habitat(monster) == HT_WATER
- && !monster->friendly())
- {
- // Mermaids/sirens call (hostile) aquatic monsters.
- behaviour_event( monster, ME_ALERT, MHITNOT, where );
- }
- else
- behaviour_event( monster, ME_DISTURB, MHITNOT, where );
- }
- }
-
- return (ret);
-}
-
-bool noisy(int loudness, const coord_def& where, int who,
- bool mermaid)
-{
- return noisy(loudness, where, NULL, who, mermaid);
-}
-
-static const char* _player_vampire_smells_blood(int dist)
-{
- // non-thirsty vampires get no clear indication of how close the
- // smell is
- if (you.hunger_state >= HS_SATIATED)
- return "";
-
- if (dist < 16) // 4*4
- return " near-by";
-
- if (you.hunger_state <= HS_NEAR_STARVING && dist > get_los_radius_sq())
- return " in the distance";
-
- return "";
-}
-
-void blood_smell( int strength, const coord_def& where )
-{
- monsters *monster = NULL;
-
- const int range = strength * strength;
-#ifdef DEBUG_DIAGNOSTICS
- mprf(MSGCH_DIAGNOSTICS,
- "blood stain at (%d, %d), range of smell = %d",
- where.x, where.y, range);
-#endif
-
- // Of the player species, only Vampires can smell blood.
- if (you.species == SP_VAMPIRE)
- {
- // Whether they actually do so, depends on their hunger state.
- int vamp_strength = strength - 2 * (you.hunger_state - 1);
- if (vamp_strength > 0)
- {
- int vamp_range = vamp_strength * vamp_strength;
-
- const int player_distance = distance( you.pos(), where );
-
- if (player_distance <= vamp_range)
- {
-#ifdef DEBUG_DIAGNOSTICS
- mprf(MSGCH_DIAGNOSTICS,
- "Player smells blood, pos: (%d, %d), dist = %d)",
- you.pos().x, you.pos().y, player_distance);
-#endif
- you.check_awaken(range - player_distance);
- // Don't message if you can see the square.
- if (!you.see_cell(where))
- {
- mprf("You smell fresh blood%s.",
- _player_vampire_smells_blood(player_distance));
- }
- }
- }
- }
-
- for (int p = 0; p < MAX_MONSTERS; p++)
- {
- monster = &menv[p];
-
- if (monster->type < 0)
- continue;
-
- if (!mons_class_flag(monster->type, M_BLOOD_SCENT))
- continue;
-
- if (distance(monster->pos(), where) <= range)
- {
- // Let sleeping hounds lie.
- if (monster->asleep()
- && mons_species(monster->type) != MONS_VAMPIRE
- && monster->type != MONS_SHARK)
- {
- // 33% chance of sleeping on
- // 33% of being disturbed (start BEH_WANDER)
- // 33% of being alerted (start BEH_SEEK)
- if (!one_chance_in(3))
- {
- if (coinflip())
- {
-#ifdef DEBUG_DIAGNOSTICS
- mprf(MSGCH_DIAGNOSTICS, "disturbing %s (%d, %d)",
- monster->name(DESC_PLAIN).c_str(),
- monster->pos().x, monster->pos().y);
-#endif
- behaviour_event(monster, ME_DISTURB, MHITNOT, where);
- }
- continue;
- }
- }
-#ifdef DEBUG_DIAGNOSTICS
- mprf(MSGCH_DIAGNOSTICS, "alerting %s (%d, %d)",
- monster->name(DESC_PLAIN).c_str(),
- monster->pos().x, monster->pos().y);
-#endif
- behaviour_event( monster, ME_ALERT, MHITNOT, where );
-
- if (monster->type == MONS_SHARK)
- {
- // Sharks go into a battle frenzy if they smell blood.
- monster_pathfind mp;
- if (mp.init_pathfind(monster, where))
- {
- mon_enchant ench = monster->get_ench(ENCH_BATTLE_FRENZY);
- const int dist = 15 - (monster->pos() - where).rdist();
- const int dur = random_range(dist, dist*2)
- * speed_to_duration(monster->speed);
-
- if (ench.ench != ENCH_NONE)
- {
- int level = ench.degree;
- if (level < 4 && one_chance_in(2*level))
- ench.degree++;
- ench.duration = std::max(ench.duration, dur);
- monster->update_ench(ench);
- }
- else
- {
- monster->add_ench(mon_enchant(ENCH_BATTLE_FRENZY, 1,
- KC_OTHER, dur));
- simple_monster_message(monster, " is consumed with "
- "blood-lust!");
- }
- }
- }
- }
- }
-}
-
-
// We logically associate a difficulty parameter with each tile on each level,
// to make deterministic magic mapping work. This function returns the
// difficulty parameters for each tile on the current level, whose difficulty