HammeriteAudioReceiver3D
Audio. Inherits Node3D
Something in the world that can hear, as a node. Attach one to a guard.
It says which kinds of sound it cares about and how faint a sound it will still notice, and emits sound_heard when one arrives above that threshold. Everything between - working out whether the sound could reach here at all, by what route, and how much of it survived the trip - is the audio graph's job, and the listener never sees it. Nothing happens for types this listener has not asked for. That is the whole cost model: a footstep in a level where nothing listens for footsteps does not get propagated, or even measured.
@onready var _ears: HammeriteAudioReceiver3D = $Hearing
func _ready() -> void:
_ears.heard_types = [&"footstep", &"glass_break"]
_ears.sound_heard.connect(_on_sound_heard)
func _on_sound_heard(event: HammeriteAudioSoundEvent) -> void:
# Go and look where it SEEMED to come from, not where it actually was.
investigate(event.apparent_position)
Properties
| Type | Name | Default |
|---|---|---|
StringName[] | heard_types | [] |
float | threshold_db | 20.0 |
bool | enabled | true |
bool | ignore_own_sounds | true |
Node | own_sounds_node | null |
Methods
| Returns | Method |
|---|---|
bool | hears_type(type: StringName) |
bool | add_heard_type(type: StringName) |
bool | remove_heard_type(type: StringName) |
void | press_ear_to_portal(portal_name: String, gain_db: float) |
void | take_ear_from_portal() |
String | ear_portal() |
float | ear_gain_db() |
bool | is_own_sound(emitter: Node) |
Signals
sound_heard
signal sound_heard(event: HammeriteAudioSoundEvent)
Emitted when a sound of a subscribed type arrives at or above threshold_db.
Property descriptions
heard_types
var heard_types: StringName[] = []
Which sound types this listener notices. Empty means none - a listener that has not said what it is listening for is not listening, rather than listening to everything, so an unconfigured guard is deaf instead of omniscient. Assign a whole array, or go through add_heard_type() and remove_heard_type(). Changing this one in place (heard_types.append(...)) never reaches the manager: a sound is routed to the listeners filed under its type, and that index is built when the types are assigned or the node enters the tree - each time it does - so an append is a type nothing was told about.
threshold_db
var threshold_db: float = 20.0
The faintest sound this listener notices, in dB, on the same scale as HammeriteAudioSoundType.loudness_db. Sounds arriving quieter are not reported.
enabled
var enabled: bool = true
Turn hearing off without removing the node - unconscious, dead, or simply not interested yet. A disabled listener costs nothing: it is not in the routing tables at all.
ignore_own_sounds
var ignore_own_sounds: bool = true
Ignore sounds made by this node or anything under it, so a guard does not hear its own footsteps. Defaults to this listener's parent, which is normally the character it belongs to.
own_sounds_node
var own_sounds_node: Node = null
Whose noises count as this listener's own, when the parent is not the answer. A listener hung straight on a character needs nothing here. One nested inside a hearing or senses component does: its parent is that component, and the character's own footsteps are emitted from a SIBLING of it, so they arrive as somebody else's and the character startles itself every time it walks. Point this at the character and the whole of it is its own again.
Method descriptions
hears_type()
func hears_type(type: StringName) -> bool
Does this listener want to hear type?
add_heard_type()
func add_heard_type(type: StringName) -> bool
Start listening for type as well, telling the manager about it. Whether it was new. This rather than heard_types.append(), which edits the array without the setter running and so leaves the manager's index without the new type in it (#239). It registers the whole list afresh either way, so calling it after an append puts that right too.
remove_heard_type()
func remove_heard_type(type: StringName) -> bool
Stop listening for type. Whether it was being listened for.
press_ear_to_portal()
func press_ear_to_portal(portal_name: String, gain_db: float) -> void
Put this listener's ear to the opening portal_name - listening at a door. What arrives through it is heard gain_db louder, and what comes any other way is not. The gameplay half of HammeriteAudioManager.press_ear_to_portal(), which does the same for what is played: a player leaning on a door wants both, and a guard listening at one wants this.
take_ear_from_portal()
func take_ear_from_portal() -> void
Undo press_ear_to_portal(): nothing is heard louder through any opening.
ear_portal()
func ear_portal() -> String
The opening this listener has its ear to, and empty when it has it to nothing.
ear_gain_db()
func ear_gain_db() -> float
How much louder, in dB, what comes through ear_portal() is heard.
is_own_sound()
func is_own_sound(emitter: Node) -> bool
Should a sound made by emitter be ignored as this listener's own? Compares by ancestry rather than by identity: the emitter is a node under the character, and so is this listener, so "mine" means "we share an owner".