3 * Summary: Stealth, noise, shouting.
24 #include "mon-place.h"
25 #include "mon-pathfind.h"
26 #include "mon-stuff.h"
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
,
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))
56 // Friendly or neutral monsters don't shout.
57 if (!force
&& (mons
->friendly() || mons
->neutral()))
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()))
74 mon_acting
mact(mons
);
76 std::string default_msg_key
= "";
81 // No default message.
84 default_msg_key
= "__SHOUT";
87 default_msg_key
= "__BARK";
90 default_msg_key
= "__TWO_SHOUTS";
93 default_msg_key
= "__ROAR";
96 default_msg_key
= "__SCREAM";
99 default_msg_key
= "__BELLOW";
102 default_msg_key
= "__TRUMPET";
105 default_msg_key
= "__SCREECH";
108 default_msg_key
= "__BUZZ";
111 default_msg_key
= "__MOAN";
114 default_msg_key
= "__GURGLE";
117 default_msg_key
= "__WHINE";
120 default_msg_key
= "__CROAK";
123 default_msg_key
= "__GROWL";
126 default_msg_key
= "__HISS";
129 default_msg_key
= "__DEMON_TAUNT";
132 default_msg_key
= "__CAW";
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
166 if (you
.can_see(mons
))
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.
188 if (msg
== "__DEFAULT" || msg
== "__NEXT")
194 "Lua shout function for monster '%s' didn't load: %s",
195 mons
->full_name(DESC_PLAIN
).c_str(),
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
211 std::string glyph_key
= "'";
213 // Database keys are case-insensitve.
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!"
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!"
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!"
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.
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";
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
);
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()))
328 // Monsters put to sleep by ensorcelled hibernation will sleep
329 // at least one turn.
330 if (mons
->has_ench(ENCH_SLEEPY
))
333 // Berserkers aren't really concerned about stealth.
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
)
350 if (!you
.visible_to(mons
))
353 unnatural_stealthy
= true;
358 if (mons
->holiness() == MH_NATURAL
)
360 // Monster is "hibernating"... reduce chance of waking.
361 if (mons
->has_ench(ENCH_SLEEP_WARY
))
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.
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
))
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
);
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.
407 copy
.propagate_noise();
411 // noisy() has a messaging service for giving messages to the player
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();
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
);
429 // If the origin is silenced there is no noise, unless we're
431 if (silenced(where
) && !fake_noise
)
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))
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(
445 (scaled_loudness
+ 1) * 1000,
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
);
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
484 if (you
.hunger_state
>= HS_SATIATED
)
487 if (dist
< 16) // 4*4
490 if (you
.hunger_state
<= HS_NEAR_STARVING
&& dist
> get_los_radius_sq())
491 return " in the distance";
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
))
534 // Let sleeping hounds lie.
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))
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
);
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.
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
))
575 ench
.duration
= std::max(ench
.duration
, dur
);
576 mi
->update_ench(ench
);
580 mi
->add_ench(mon_enchant(ENCH_BATTLE_FRENZY
, 1,
582 simple_monster_message(*mi
, " is consumed with "
590 //////////////////////////////////////////////////////////////////////////////
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
);
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;
606 case DNGN_SWAMP_TREE
:
607 return BASE_NOISE_ATTENUATION_MILLIS
* 3;
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
,
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
;
643 int noise_cell::turn_angle(const coord_def
&next_delta
) const
645 if (neighbour_delta
.origin())
649 if (next_delta
.x
== -neighbour_delta
.x
650 && next_delta
.y
== -neighbour_delta
.y
)
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());
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
,
685 void noise_grid::propagate_noise()
690 dprf("noise_grid: %d noises to apply", noises
.size());
691 std::vector
<coord_def
> noise_perimeter
[2];
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
]);
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
));
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
)
727 const coord_def
next_position(p
.x
+ xi
,
729 if (in_bounds(next_position
)
730 && !silenced(next_position
))
732 if (propagate_noise_to_neighbour(
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",
757 dump_noise_grid("noise-grid.html");
762 bool noise_grid::propagate_noise_to_neighbour(int base_attenuation
,
764 const noise_cell
&cell
,
765 const coord_def
¤t_pos
,
766 const coord_def
&next_pos
)
768 noise_cell
&neighbour(cells(next_pos
));
769 if (!neighbour
.can_apply_noise(cell
.noise_intensity_millis
-
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)
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
,
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
);
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
))
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
)
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
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
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
883 const coord_def noise_centroid
=
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
=
892 noise_centroid
+ coord_def(random_range(-fuzz
, fuzz
, 2),
893 random_range(-fuzz
, fuzz
, 2))
896 const coord_def final_perceived_point
=
897 !in_bounds(perceived_point
)?
898 _point_clamped_in_bounds(perceived_point
)
901 #ifdef DEBUG_NOISE_PROPAGATION
902 dprf("[NOISE] Noise perceived by %s at (%d,%d) centroid (%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
);
912 return (final_perceived_point
);
915 #ifdef DEBUG_NOISE_PROPAGATION
918 #include "viewchar.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;
929 red
= chroma
, green
= x
;
931 red
= x
, green
= chroma
;
933 green
= chroma
, blue
= x
;
935 green
= x
, blue
= chroma
;
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;
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());
961 static void _write_noise_grid_css(FILE *outf
)
964 "<style type='text/css'>\n"
965 "body { font-family: monospace; padding: 0; margin: 0; "
966 "line-height: 100%% }\n"
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);
977 "<span class='i%d'>&#%d;</span>",
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
);
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
)
1000 if (you
.pos() == coord_def(x
, y
))
1001 write_cell(outf
, p
, '@');
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");
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
);
1040 const bool player
= act
->is_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
);
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
);
1067 behaviour_event(mons
, ME_DISTURB
, MHITNOT
, apparent_source
);