Apply the new ground_level method.
[crawl.git] / crawl-ref / source / shout.cc
blobfc85d9f54b8eacb9dc28f618814e1f1f93fd29a2
1 /*
2 * File: shout.cc
3 * Summary: Stealth, noise, shouting.
4 */
6 #include "AppHdr.h"
8 #include "shout.h"
10 #include "branch.h"
11 #include "cluautil.h"
12 #include "coord.h"
13 #include "database.h"
14 #include "dlua.h"
15 #include "env.h"
16 #include "exercise.h"
17 #include "ghost.h"
18 #include "jobs.h"
19 #include "libutil.h"
20 #include "message.h"
21 #include "misc.h"
22 #include "mon-behv.h"
23 #include "mon-iter.h"
24 #include "mon-place.h"
25 #include "mon-pathfind.h"
26 #include "mon-stuff.h"
27 #include "mon-util.h"
28 #include "monster.h"
29 #include "noise.h"
30 #include "player.h"
31 #include "random.h"
32 #include "skills.h"
33 #include "state.h"
34 #include "stuff.h"
35 #include "terrain.h"
36 #include "areas.h"
37 #include "hints.h"
38 #include "view.h"
40 #include <sstream>
42 extern int stealth; // defined in main.cc
44 static noise_grid _noise_grid;
45 static void _actor_apply_noise(actor *act,
46 const coord_def &apparent_source,
47 int noise_intensity_millis,
48 const noise_t &noise,
49 int noise_travel_distance);
51 void handle_monster_shouts(monster* mons, bool force)
53 if (!force && x_chance_in_y(you.skill(SK_STEALTH), 30))
54 return;
56 // Friendly or neutral monsters don't shout.
57 if (!force && (mons->friendly() || mons->neutral()))
58 return;
60 // Get it once, since monster might be S_RANDOM, in which case
61 // mons_shouts() will return a different value every time.
62 // Demon lords will insult you as a greeting, but later we'll
63 // choose a random verb and loudness for them.
64 shout_type s_type = mons_shouts(mons->type, false);
66 // Silent monsters can give noiseless "visual shouts" if the
67 // player can see them, in which case silence isn't checked for.
68 if (s_type == S_SILENT && !mons->visible_to(&you)
69 || s_type != S_SILENT && !player_can_hear(mons->pos()))
71 return;
74 mon_acting mact(mons);
76 std::string default_msg_key = "";
78 switch (s_type)
80 case S_SILENT:
81 // No default message.
82 break;
83 case S_SHOUT:
84 default_msg_key = "__SHOUT";
85 break;
86 case S_BARK:
87 default_msg_key = "__BARK";
88 break;
89 case S_SHOUT2:
90 default_msg_key = "__TWO_SHOUTS";
91 break;
92 case S_ROAR:
93 default_msg_key = "__ROAR";
94 break;
95 case S_SCREAM:
96 default_msg_key = "__SCREAM";
97 break;
98 case S_BELLOW:
99 default_msg_key = "__BELLOW";
100 break;
101 case S_TRUMPET:
102 default_msg_key = "__TRUMPET";
103 break;
104 case S_SCREECH:
105 default_msg_key = "__SCREECH";
106 break;
107 case S_BUZZ:
108 default_msg_key = "__BUZZ";
109 break;
110 case S_MOAN:
111 default_msg_key = "__MOAN";
112 break;
113 case S_GURGLE:
114 default_msg_key = "__GURGLE";
115 break;
116 case S_WHINE:
117 default_msg_key = "__WHINE";
118 break;
119 case S_CROAK:
120 default_msg_key = "__CROAK";
121 break;
122 case S_GROWL:
123 default_msg_key = "__GROWL";
124 break;
125 case S_HISS:
126 default_msg_key = "__HISS";
127 break;
128 case S_DEMON_TAUNT:
129 default_msg_key = "__DEMON_TAUNT";
130 break;
131 case S_CAW:
132 default_msg_key = "__CAW";
133 break;
134 default:
135 default_msg_key = "__BUGGY"; // S_LOUD, S_VERY_SOFT, etc. (loudness)
138 // Now that we have the message key, get a random verb and noise level
139 // for pandemonium lords.
140 if (s_type == S_DEMON_TAUNT)
141 s_type = mons_shouts(mons->type, true);
143 std::string msg, suffix;
144 std::string key = mons_type_name(mons->type, DESC_PLAIN);
146 // Pandemonium demons have random names, so use "pandemonium lord"
147 if (mons->type == MONS_PANDEMONIUM_DEMON)
148 key = "pandemonium lord";
149 // Search for player ghost shout by the ghost's job.
150 else if (mons->type == MONS_PLAYER_GHOST)
152 const ghost_demon &ghost = *(mons->ghost);
153 std::string ghost_job = get_job_name(ghost.job);
155 key = ghost_job + " player ghost";
157 default_msg_key = "player ghost";
160 // Tries to find an entry for "name seen" or "name unseen",
161 // and if no such entry exists then looks simply for "name".
162 // We don't use "you.can_see(mons)" here since that would return
163 // false for submerged monsters, but submerged monsters will be forced
164 // to surface before they shout, thus removing that source of
165 // non-visibility.
166 if (you.can_see(mons))
167 suffix = " seen";
168 else
169 suffix = " unseen";
171 if (mons->props.exists("shout_func"))
173 lua_stack_cleaner clean(dlua);
175 dlua_chunk &chunk = mons->props["shout_func"];
177 if (!chunk.load(dlua))
179 push_monster(dlua, mons);
180 clua_pushcxxstring(dlua, suffix);
181 dlua.callfn(NULL, 2, 1);
182 dlua.fnreturns(">s", &msg);
184 // __NONE means to be silent, and __NEXT or __DEFAULT means to try
185 // the next method of getting a shout message.
186 if (msg == "__NONE")
187 return;
188 if (msg == "__DEFAULT" || msg == "__NEXT")
189 msg.clear();
191 else
193 mprf(MSGCH_ERROR,
194 "Lua shout function for monster '%s' didn't load: %s",
195 mons->full_name(DESC_PLAIN).c_str(),
196 dlua.error.c_str());
200 if (msg.empty())
201 msg = getShoutString(key, suffix);
203 if (msg == "__DEFAULT" || msg == "__NEXT")
204 msg = getShoutString(default_msg_key, suffix);
205 else if (msg.empty())
207 char mchar = mons_base_char(mons->type);
209 // See if there's a shout for all monsters using the
210 // same glyph/symbol
211 std::string glyph_key = "'";
213 // Database keys are case-insensitve.
214 if (isaupper(mchar))
215 glyph_key += "cap-";
217 glyph_key += mchar;
218 glyph_key += "'";
219 msg = getShoutString(glyph_key, suffix);
221 if (msg.empty() || msg == "__DEFAULT")
222 msg = getShoutString(default_msg_key, suffix);
225 if (default_msg_key == "__BUGGY")
227 msg::streams(MSGCH_SOUND) << "You hear something buggy!"
228 << std::endl;
230 else if (s_type == S_SILENT && (msg.empty() || msg == "__NONE"))
232 ; // No "visual shout" defined for silent monster, do nothing.
234 else if (msg.empty()) // Still nothing found?
236 msg::streams(MSGCH_DIAGNOSTICS)
237 << "No shout entry for default shout type '"
238 << default_msg_key << "'" << std::endl;
240 msg::streams(MSGCH_SOUND) << "You hear something buggy!"
241 << std::endl;
243 else if (msg == "__NONE")
245 msg::streams(MSGCH_DIAGNOSTICS)
246 << "__NONE returned as shout for non-silent monster '"
247 << default_msg_key << "'" << std::endl;
248 msg::streams(MSGCH_SOUND) << "You hear something buggy!"
249 << std::endl;
251 else
253 msg_channel_type channel = MSGCH_TALK;
255 std::string param = "";
256 std::string::size_type pos = msg.find(":");
258 if (pos != std::string::npos)
260 param = msg.substr(0, pos);
261 msg = msg.substr(pos + 1);
264 if (s_type == S_SILENT || param == "VISUAL")
265 channel = MSGCH_TALK_VISUAL;
266 else if (param == "SOUND")
267 channel = MSGCH_SOUND;
269 // Monster must come up from being submerged if it wants to shout.
270 if (mons->submerged())
272 if (!mons->del_ench(ENCH_SUBMERGED))
274 // Couldn't unsubmerge.
275 return;
278 if (you.can_see(mons))
280 if (mons->type == MONS_AIR_ELEMENTAL)
281 mons->seen_context = "thin air";
282 else if (mons->type == MONS_TRAPDOOR_SPIDER)
283 mons->seen_context = "leaps out";
284 else if (!monster_habitable_grid(mons, DNGN_FLOOR))
285 mons->seen_context = "bursts forth shouting";
286 else
287 mons->seen_context = "surfaces";
289 // Give interrupt message before shout message.
290 handle_seen_interrupt(mons);
294 if (channel != MSGCH_TALK_VISUAL || you.can_see(mons))
296 msg = do_mon_str_replacements(msg, mons, s_type);
297 msg::streams(channel) << msg << std::endl;
299 // Otherwise it can move away with no feedback.
300 if (you.can_see(mons))
302 if (!(mons->flags & MF_WAS_IN_VIEW))
303 handle_seen_interrupt(mons);
304 seen_monster(mons);
309 const int noise_level = get_shout_noise_level(s_type);
310 const bool heard = noisy(noise_level, mons->pos(), mons->mindex());
312 if (crawl_state.game_is_hints() && (heard || you.can_see(mons)))
313 learned_something_new(HINT_MONSTER_SHOUT, mons->pos());
316 void force_monster_shout(monster* mons)
318 handle_monster_shouts(mons, true);
321 bool check_awaken(monster* mons)
323 // Usually redundant because we iterate over player LOS,
324 // but e.g. for you.xray_vision.
325 if (!mons->see_cell(you.pos()))
326 return (false);
328 // Monsters put to sleep by ensorcelled hibernation will sleep
329 // at least one turn.
330 if (mons->has_ench(ENCH_SLEEPY))
331 return (false);
333 // Berserkers aren't really concerned about stealth.
334 if (you.berserk())
335 return (true);
337 // I assume that creatures who can sense invisible are very perceptive.
338 int mons_perc = 10 + (mons_intel(mons) * 4) + mons->hit_dice
339 + mons_sense_invis(mons) * 5;
341 bool unnatural_stealthy = false; // "stealthy" only because of invisibility?
343 // Critters that are wandering but still have MHITYOU as their foe are
344 // still actively on guard for the player, even if they can't see you.
345 // Give them a large bonus -- handle_behaviour() will nuke 'foe' after
346 // a while, removing this bonus.
347 if (mons_is_wandering(mons) && mons->foe == MHITYOU)
348 mons_perc += 15;
350 if (!you.visible_to(mons))
352 mons_perc -= 75;
353 unnatural_stealthy = true;
356 if (mons->asleep())
358 if (mons->holiness() == MH_NATURAL)
360 // Monster is "hibernating"... reduce chance of waking.
361 if (mons->has_ench(ENCH_SLEEP_WARY))
362 mons_perc -= 10;
364 else // unnatural creature
366 // Unnatural monsters don't actually "sleep", they just
367 // haven't noticed an intruder yet... we'll assume that
368 // they're diligently on guard.
369 mons_perc += 10;
373 // If you've been tagged with Corona or are Glowing, the glow
374 // makes you extremely unstealthy.
375 if (you.backlit() && you.visible_to(mons))
376 mons_perc += 50;
378 if (mons_perc < 0)
379 mons_perc = 0;
381 if (x_chance_in_y(mons_perc + 1, stealth))
382 return (true); // Oops, the monster wakes up!
384 // You didn't wake the monster!
385 if (you.can_see(mons) // to avoid leaking information
386 && !mons->wont_attack()
387 && !mons->neutral() // include pacified monsters
388 && !mons_class_flag(mons->type, M_NO_EXP_GAIN))
390 practise(unnatural_stealthy ? EX_SNEAK_INVIS : EX_SNEAK);
393 return (false);
396 void apply_noises()
398 // [ds] This copying isn't awesome, but we cannot otherwise handle
399 // the case where one set of noises wakes up monsters who then let
400 // out yips of their own, modifying _noise_grid while it is in the
401 // middle of propagate_noise().
402 if (_noise_grid.dirty())
404 noise_grid copy = _noise_grid;
405 // Reset the main grid.
406 _noise_grid.reset();
407 copy.propagate_noise();
411 // noisy() has a messaging service for giving messages to the player
412 // as appropriate.
413 bool noisy(int original_loudness, const coord_def& where,
414 const char *msg, int who,
415 bool mermaid, bool message_if_unseen, bool fake_noise)
417 // high ambient noise makes sounds harder to hear
418 const int ambient = current_level_ambient_noise();
419 const int loudness =
420 ambient < 0? original_loudness + random2avg(abs(ambient), 3)
421 : original_loudness - random2avg(abs(ambient), 3);
423 dprf("Noise %d (orig: %d; ambient: %d) at pos(%d,%d)",
424 loudness, original_loudness, ambient, where.x, where.y);
426 if (loudness <= 0)
427 return (false);
429 // If the origin is silenced there is no noise, unless we're
430 // faking it.
431 if (silenced(where) && !fake_noise)
432 return (false);
434 // [ds] Reduce noise propagation for Sprint.
435 const int scaled_loudness =
436 crawl_state.game_is_sprint()? std::max(1, div_rand_round(loudness, 2))
437 : loudness;
439 // Add +1 to scaled_loudness so that all squares adjacent to a
440 // sound of loudness 1 will hear the sound.
441 const std::string noise_msg(msg? msg : "");
442 _noise_grid.register_noise(
443 noise_t(where,
444 noise_msg,
445 (scaled_loudness + 1) * 1000,
446 who,
447 0 | (mermaid? NF_MERMAID : 0)
448 | (message_if_unseen? NF_MESSAGE_IF_UNSEEN : 0)));
450 // Some users of noisy() want an immediate answer to whether the
451 // player heard the noise. The deferred noise system also means
452 // that soft noises can be drowned out by loud noises. For both
453 // these reasons, use the simple old noise system to check if the
454 // player heard the noise:
455 const int dist = loudness * loudness + 1;
456 const int player_distance = distance(you.pos(), where);
458 // Message the player.
459 if (player_distance <= dist && player_can_hear(where))
461 if (msg && !fake_noise)
462 mpr(msg, MSGCH_SOUND);
463 return (true);
465 return (false);
468 bool noisy(int loudness, const coord_def& where, int who,
469 bool mermaid, bool message_if_unseen)
471 return noisy(loudness, where, NULL, who, mermaid, message_if_unseen);
474 // This fakes noise even through silence.
475 bool fake_noisy(int loudness, const coord_def& where)
477 return noisy(loudness, where, NULL, -1, false, false, true);
480 static const char* _player_vampire_smells_blood(int dist)
482 // non-thirsty vampires get no clear indication of how close the
483 // smell is
484 if (you.hunger_state >= HS_SATIATED)
485 return "";
487 if (dist < 16) // 4*4
488 return " near-by";
490 if (you.hunger_state <= HS_NEAR_STARVING && dist > get_los_radius_sq())
491 return " in the distance";
493 return "";
496 void blood_smell(int strength, const coord_def& where)
498 const int range = strength * strength;
499 dprf("blood stain at (%d, %d), range of smell = %d",
500 where.x, where.y, range);
502 // Of the player species, only Vampires can smell blood.
503 if (you.species == SP_VAMPIRE)
505 // Whether they actually do so, depends on their hunger state.
506 int vamp_strength = strength - 2 * (you.hunger_state - 1);
507 if (vamp_strength > 0)
509 int vamp_range = vamp_strength * vamp_strength;
511 const int player_distance = distance(you.pos(), where);
513 if (player_distance <= vamp_range)
515 dprf("Player smells blood, pos: (%d, %d), dist = %d)",
516 you.pos().x, you.pos().y, player_distance);
517 you.check_awaken(range - player_distance);
518 // Don't message if you can see the square.
519 if (!you.see_cell(where))
521 mprf("You smell fresh blood%s.",
522 _player_vampire_smells_blood(player_distance));
528 circle_def c(where, range, C_CIRCLE);
529 for (monster_iterator mi(&c); mi; ++mi)
531 if (!mons_class_flag(mi->type, M_BLOOD_SCENT))
532 continue;
534 // Let sleeping hounds lie.
535 if (mi->asleep()
536 && mons_species(mi->type) != MONS_VAMPIRE
537 && mi->type != MONS_SHARK)
539 // 33% chance of sleeping on
540 // 33% of being disturbed (start BEH_WANDER)
541 // 33% of being alerted (start BEH_SEEK)
542 if (!one_chance_in(3))
544 if (coinflip())
546 dprf("disturbing %s (%d, %d)",
547 mi->name(DESC_PLAIN).c_str(),
548 mi->pos().x, mi->pos().y);
549 behaviour_event(*mi, ME_DISTURB, MHITNOT, where);
551 continue;
554 dprf("alerting %s (%d, %d)",
555 mi->name(DESC_PLAIN).c_str(),
556 mi->pos().x, mi->pos().y);
557 behaviour_event(*mi, ME_ALERT, MHITNOT, where);
559 if (mi->type == MONS_SHARK)
561 // Sharks go into a battle frenzy if they smell blood.
562 monster_pathfind mp;
563 if (mp.init_pathfind(*mi, where))
565 mon_enchant ench = mi->get_ench(ENCH_BATTLE_FRENZY);
566 const int dist = 15 - (mi->pos() - where).rdist();
567 const int dur = random_range(dist, dist*2)
568 * speed_to_duration(mi->speed);
570 if (ench.ench != ENCH_NONE)
572 int level = ench.degree;
573 if (level < 4 && one_chance_in(2*level))
574 ench.degree++;
575 ench.duration = std::max(ench.duration, dur);
576 mi->update_ench(ench);
578 else
580 mi->add_ench(mon_enchant(ENCH_BATTLE_FRENZY, 1,
581 KC_OTHER, dur));
582 simple_monster_message(*mi, " is consumed with "
583 "blood-lust!");
590 //////////////////////////////////////////////////////////////////////////////
591 // noise machinery
593 // Currently noise attenuation depends solely on the feature in question.
594 // Walls are assumed to completely kill noise.
595 int noise_attenuation_millis(const coord_def &pos)
597 const dungeon_feature_type feat = grd(pos);
598 switch (feat)
600 // Closed doors are excellent at cutting off sound.
601 case DNGN_CLOSED_DOOR:
602 case DNGN_DETECTED_SECRET_DOOR:
603 case DNGN_SECRET_DOOR:
604 return BASE_NOISE_ATTENUATION_MILLIS * 8;
605 case DNGN_TREE:
606 case DNGN_SWAMP_TREE:
607 return BASE_NOISE_ATTENUATION_MILLIS * 3;
608 default:
609 if (feat_is_statue_or_idol(feat))
610 return BASE_NOISE_ATTENUATION_MILLIS * 2;
611 if (feat_is_wall(feat))
612 return NOISE_ATTENUATION_COMPLETE;
613 return BASE_NOISE_ATTENUATION_MILLIS;
617 noise_cell::noise_cell()
618 : neighbour_delta(0, 0), noise_id(-1), noise_intensity_millis(0)
622 bool noise_cell::can_apply_noise(int _noise_intensity_millis) const
624 return (noise_intensity_millis < _noise_intensity_millis);
627 bool noise_cell::apply_noise(int _noise_intensity_millis,
628 int _noise_id,
629 int _noise_travel_distance,
630 const coord_def &_neighbour_delta)
632 if (can_apply_noise(_noise_intensity_millis))
634 noise_id = _noise_id;
635 noise_intensity_millis = _noise_intensity_millis;
636 noise_travel_distance = _noise_travel_distance;
637 neighbour_delta = _neighbour_delta;
638 return (true);
640 return (false);
643 int noise_cell::turn_angle(const coord_def &next_delta) const
645 if (neighbour_delta.origin())
646 return (0);
648 // Going in reverse?
649 if (next_delta.x == -neighbour_delta.x
650 && next_delta.y == -neighbour_delta.y)
651 return (4);
653 const int xdiff = std::abs(neighbour_delta.x - next_delta.x);
654 const int ydiff = std::abs(neighbour_delta.y - next_delta.y);
655 return xdiff + ydiff;
658 noise_grid::noise_grid()
659 : cells(), noises(), affected_actor_count(0)
663 void noise_grid::reset()
665 cells.init(noise_cell());
666 noises.clear();
667 affected_actor_count = 0;
670 void noise_grid::register_noise(const noise_t &noise)
672 noise_cell &target_cell(cells(noise.noise_source));
673 if (target_cell.can_apply_noise(noise.noise_intensity_millis))
675 const int noise_index = noises.size();
676 noises.push_back(noise);
677 noises[noise_index].noise_id = noise_index;
678 cells(noise.noise_source).apply_noise(noise.noise_intensity_millis,
679 noise_index,
681 coord_def(0, 0));
685 void noise_grid::propagate_noise()
687 if (noises.empty())
688 return;
690 dprf("noise_grid: %d noises to apply", noises.size());
691 std::vector<coord_def> noise_perimeter[2];
692 int circ_index = 0;
694 for (int i = 0, size = noises.size(); i < size; ++i)
695 noise_perimeter[circ_index].push_back(noises[i].noise_source);
697 int travel_distance = 0;
698 while (!noise_perimeter[circ_index].empty())
700 const std::vector<coord_def> &perimeter(noise_perimeter[circ_index]);
701 std::vector<coord_def> &next_perimeter(noise_perimeter[!circ_index]);
702 ++travel_distance;
703 for (int i = 0, size = perimeter.size(); i < size; ++i)
705 const coord_def p(perimeter[i]);
706 const noise_cell &cell(cells(p));
708 if (!cell.silent())
710 apply_noise_effects(p,
711 cell.noise_intensity_millis,
712 noises[cell.noise_id],
713 travel_distance - 1);
715 const int attenuation = noise_attenuation_millis(p);
716 // If the base noise attenuation kills the noise, go no farther:
717 if (noise_is_audible(cell.noise_intensity_millis - attenuation))
719 // [ds] Not using adjacent iterator which has
720 // unnecessary overhead for the tight loop here.
721 for (int xi = -1; xi <= 1; ++xi)
723 for (int yi = -1; yi <= 1; ++yi)
725 if (xi || yi)
727 const coord_def next_position(p.x + xi,
728 p.y + yi);
729 if (in_bounds(next_position)
730 && !silenced(next_position))
732 if (propagate_noise_to_neighbour(
733 attenuation,
734 travel_distance,
735 cell, p,
736 next_position))
738 next_perimeter.push_back(next_position);
748 noise_perimeter[circ_index].clear();
749 circ_index = !circ_index;
752 #ifdef DEBUG_NOISE_PROPAGATION
753 if (affected_actor_count)
755 mprf(MSGCH_WARN, "Writing noise grid with %d noise sources",
756 noises.size());
757 dump_noise_grid("noise-grid.html");
759 #endif
762 bool noise_grid::propagate_noise_to_neighbour(int base_attenuation,
763 int travel_distance,
764 const noise_cell &cell,
765 const coord_def &current_pos,
766 const coord_def &next_pos)
768 noise_cell &neighbour(cells(next_pos));
769 if (!neighbour.can_apply_noise(cell.noise_intensity_millis -
770 base_attenuation))
771 return (false);
773 // Diagonals cost more.
774 if ((next_pos - current_pos).abs() == 2)
775 base_attenuation = base_attenuation * 141 / 100;
777 const int noise_turn_angle = cell.turn_angle(next_pos - current_pos);
778 const int turn_attenuation =
779 noise_turn_angle? (base_attenuation * (100 + noise_turn_angle * 25)
780 / 100)
781 : base_attenuation;
782 const int attenuated_noise_intensity =
783 cell.noise_intensity_millis - turn_attenuation;
784 if (noise_is_audible(attenuated_noise_intensity))
786 const int neighbour_old_distance = neighbour.noise_travel_distance;
787 if (neighbour.apply_noise(attenuated_noise_intensity,
788 cell.noise_id,
789 travel_distance,
790 next_pos - current_pos))
791 // Return true only if we hadn't already registered this
792 // cell as a neighbour (presumably with a lower volume).
793 return (neighbour_old_distance != travel_distance);
795 return (false);
799 void noise_grid::apply_noise_effects(const coord_def &pos,
800 int noise_intensity_millis,
801 const noise_t &noise,
802 int noise_travel_distance)
804 if (you.pos() == pos)
806 _actor_apply_noise(&you, noise.noise_source,
807 noise_intensity_millis, noise,
808 noise_travel_distance);
809 ++affected_actor_count;
812 if (monster *mons = monster_at(pos))
814 if (mons->alive()
815 && !mons->has_ench(ENCH_SLEEPY)
816 && mons->mindex() != noise.noise_producer_id)
818 const coord_def perceived_position =
819 noise_perceived_position(mons, pos, noise);
820 _actor_apply_noise(mons, perceived_position,
821 noise_intensity_millis, noise,
822 noise_travel_distance);
823 ++affected_actor_count;
828 static coord_def _point_clamped_in_bounds(const coord_def &p)
830 return coord_def(
831 std::min(X_BOUND_2 - 1, std::max(X_BOUND_1 + 1, p.x)),
832 std::min(Y_BOUND_2 - 1, std::max(Y_BOUND_1 + 1, p.y)));
835 // Given an actor at affected_pos and a given noise, calculates where
836 // the actor might think the noise originated.
838 // [ds] Let's keep this brutally simple, since the player will
839 // probably not notice even if we get very clever:
841 // - If the cells can see each other, return the actual source position.
843 // - If the cells cannot see each other, calculate a noise source as follows:
845 // Calculate a noise centroid between the noise source and the observer,
846 // weighted to the noise source if the noise has traveled in a straight line,
847 // weighted toward the observer the more the noise has deviated from a
848 // straight line.
850 // Fuzz the centroid by the extra distance the noise has traveled over
851 // the straight line distance. This is where the observer will think the
852 // noise originated.
854 // Thus, if the noise has traveled in a straight line, the observer
855 // will know the exact origin, 100% of the time, even if the
856 // observer is all the way across the level.
857 coord_def noise_grid::noise_perceived_position(actor *act,
858 const coord_def &affected_pos,
859 const noise_t &noise) const
861 const int noise_travel_distance = cells(affected_pos).noise_travel_distance;
862 if (!noise_travel_distance)
863 return (noise.noise_source);
865 const int cell_grid_distance =
866 grid_distance(affected_pos, noise.noise_source);
868 if (cell_grid_distance <= LOS_RADIUS)
870 if (act->see_cell(noise.noise_source))
871 return (noise.noise_source);
874 const int extra_distance_covered =
875 noise_travel_distance - cell_grid_distance;
877 const int source_weight = 200;
878 const int target_weight =
879 extra_distance_covered?
880 75 * extra_distance_covered / cell_grid_distance
881 : 0;
883 const coord_def noise_centroid =
884 target_weight?
885 (noise.noise_source * source_weight + affected_pos * target_weight)
886 / (source_weight + target_weight)
887 : noise.noise_source;
889 const int fuzz = extra_distance_covered;
890 const coord_def perceived_point =
891 fuzz?
892 noise_centroid + coord_def(random_range(-fuzz, fuzz, 2),
893 random_range(-fuzz, fuzz, 2))
894 : noise_centroid;
896 const coord_def final_perceived_point =
897 !in_bounds(perceived_point)?
898 _point_clamped_in_bounds(perceived_point)
899 : perceived_point;
901 #ifdef DEBUG_NOISE_PROPAGATION
902 dprf("[NOISE] Noise perceived by %s at (%d,%d) centroid (%d,%d) "
903 "source (%d,%d) "
904 "heard at (%d,%d), distance: %d (traveled %d)",
905 act->name(DESC_PLAIN, true).c_str(),
906 final_perceived_point.x, final_perceived_point.y,
907 noise_centroid.x, noise_centroid.y,
908 noise.noise_source.x, noise.noise_source.y,
909 affected_pos.x, affected_pos.y,
910 cell_grid_distance, noise_travel_distance);
911 #endif
912 return (final_perceived_point);
915 #ifdef DEBUG_NOISE_PROPAGATION
917 #include "options.h"
918 #include "viewchar.h"
919 #include "math.h"
921 // Return HTML RGB triple given a hue and assuming chroma of 0.86 (220)
922 static std::string _hue_rgb(int hue)
924 const int chroma = 220;
925 const double hue2 = hue / 60.0;
926 const int x = chroma * (1.0 - fabs(hue2 - floor(hue2 / 2) * 2 - 1));
927 int red = 0, green = 0, blue = 0;
928 if (hue2 < 1)
929 red = chroma, green = x;
930 else if (hue2 < 2)
931 red = x, green = chroma;
932 else if (hue2 < 3)
933 green = chroma, blue = x;
934 else if (hue2 < 4)
935 green = x, blue = chroma;
936 else
937 red = x, blue = chroma;
938 // Other hues are not generated, so skip them.
939 return make_stringf("%02x%02x%02x", red, green, blue);
942 static std::string _noise_intensity_styles()
944 // Hi-intensity sound will be red (HSV 0), low intensity blue (HSV 240).
945 const int hi_hue = 0;
946 const int lo_hue = 240;
947 const int huespan = lo_hue - hi_hue;
949 const int max_intensity = 25;
950 std::string styles;
951 for (int intensity = 1; intensity <= max_intensity; ++intensity)
953 const int hue = lo_hue - intensity * huespan / max_intensity;
954 styles += make_stringf(".i%d { background: #%s }\n",
955 intensity, _hue_rgb(hue).c_str());
958 return styles;
961 static void _write_noise_grid_css(FILE *outf)
963 fprintf(outf,
964 "<style type='text/css'>\n"
965 "body { font-family: monospace; padding: 0; margin: 0; "
966 "line-height: 100%% }\n"
967 "%s\n"
968 "</style>",
969 _noise_intensity_styles().c_str());
972 void noise_grid::write_cell(FILE *outf, coord_def p, int ch) const
974 const int intensity = std::min(25, cells(p).noise_intensity_millis / 1000);
975 if (intensity)
976 fprintf(outf,
977 "<span class='i%d'>&#%d;</span>",
978 intensity, ch);
979 else
980 fprintf(outf, "<span>&#%d;</span>", ch);
983 void noise_grid::write_noise_grid(FILE *outf) const
985 // Duplicate the screenshot() trick.
986 FixedVector<unsigned, NUM_DCHAR_TYPES> char_table_bk;
987 char_table_bk = Options.char_table;
989 init_char_table(CSET_ASCII);
990 init_show_table();
992 fprintf(outf, "<div>\n");
993 // Write the whole map out without checking for mappedness. Handy
994 // for debugging level-generation issues.
995 for (int y = 0; y < GYM; ++y)
997 for (int x = 0; x < GXM; ++x)
999 coord_def p(x, y);
1000 if (you.pos() == coord_def(x, y))
1001 write_cell(outf, p, '@');
1002 else
1003 write_cell(outf, p, get_feature_def(grd[x][y]).symbol);
1005 fprintf(outf, "<br>\n");
1007 fprintf(outf, "</div>\n");
1010 void noise_grid::dump_noise_grid(const std::string &filename) const
1012 FILE *outf = fopen(filename.c_str(), "w");
1013 fprintf(outf, "<!DOCTYPE html><html><head>");
1014 _write_noise_grid_css(outf);
1015 fprintf(outf, "</head>\n<body>\n");
1016 write_noise_grid(outf);
1017 fprintf(outf, "</body></html>\n");
1018 fclose(outf);
1020 #endif
1022 static void _actor_apply_noise(actor *act,
1023 const coord_def &apparent_source,
1024 int noise_intensity_millis,
1025 const noise_t &noise,
1026 int noise_travel_distance)
1028 #ifdef DEBUG_NOISE_PROPAGATION
1029 dprf("[NOISE] Actor %s (%d,%d) perceives noise (%d) "
1030 "from (%d,%d), real source (%d,%d), distance: %d, noise traveled: %d",
1031 act->name(DESC_PLAIN, true).c_str(),
1032 act->pos().x, act->pos().y,
1033 noise_intensity_millis,
1034 apparent_source.x, apparent_source.y,
1035 noise.noise_source.x, noise.noise_source.y,
1036 grid_distance(act->pos(), noise.noise_source),
1037 noise_travel_distance);
1038 #endif
1040 const bool player = act->is_player();
1041 if (player)
1043 const int loudness = div_rand_round(noise_intensity_millis, 1000);
1044 act->check_awaken(loudness);
1045 if (!(noise.noise_flags & NF_MERMAID))
1047 you.beholders_check_noise(loudness);
1048 you.fearmongers_check_noise(loudness);
1051 else
1053 monster *mons = act->as_monster();
1054 // If the noise came from the character, any nearby monster
1055 // will be jumping on top of them.
1056 if (grid_distance(apparent_source, you.pos()) <= 3)
1057 behaviour_event(mons, ME_ALERT, MHITYOU, apparent_source);
1058 else if ((noise.noise_flags & NF_MERMAID)
1059 && mons_secondary_habitat(mons) == HT_WATER
1060 && !mons->friendly())
1062 // Mermaids/sirens call (hostile) aquatic monsters.
1063 behaviour_event(mons, ME_ALERT, MHITNOT, apparent_source);
1065 else
1067 behaviour_event(mons, ME_DISTURB, MHITNOT, apparent_source);