diff options
author | Robert Vollmert <rvollmert@gmx.net> | 2009-11-10 12:42:34 +0100 |
---|---|---|
committer | Robert Vollmert <rvollmert@gmx.net> | 2009-11-10 12:44:19 +0100 |
commit | 3977b123c4b05ea20323805fa901a213ecaeac3e (patch) | |
tree | a18491061f515ba43c35aaff1e51366cc5f7af3b /crawl-ref/source/view.cc | |
parent | ba3b7dbaad47d6e41d3763ec7dd6215bd74cfb5f (diff) | |
download | crawl-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.cc | 519 |
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 |