fix(audio): listening-gate round 1 — tunnel interior sound + ambience in houses (#355 gate)
Two user findings from the Campaign A listening session. 1. The portal tunnel's in-flight sound was silent while its enter/exit cues played. The tunnel's authored SoundTweakedHook drained into the world 3-D path at its synthetic owner's origin (0,0,0) — after A2 that dies twice: the listener is usually beyond the -50 dB no-allocate radius, and the world pool is suspended for the whole transit hold. The cues the user COULD hear were on the interface bus, which has neither problem, and retail's tunnel is gmSmartBoxUI — UI-owned — so that bus is also the faithful route. UiPresentationHookSink now wraps the shared router for the tunnel: sound-bearing hooks go from-centre through the interface bus (AudioHookSink.OnUiHook); every other hook kind still reaches the particle/lighting/translucency sinks unchanged. 2. Ambience cut dead inside houses; retail keeps the outdoor soundscape in sky-lit interiors. This is TS-66, now retired: the ambient listener source resolves the per-cell CEnvCell.seen_outside bit through the physics cache (the same #107 field AdjustPosition reads) and converts the envcell-local origin through the cell's WorldTransform into landblock coordinates before the 3x3 walk centres on it — an outdoor Position's origin is already landblock-local, an envcell's is cell-local, and skipping that conversion would centre the walk wrongly by up to a landblock. A not-yet-resident cell record resolves to silence for that rebuild rather than a wrong walk. Sealed dungeons stay silent, which is retail-correct. The user also reports interiors carrying their own local sound in retail (hearth-type emitters). Statics already register their sound tables and route animation hooks, so the expectation is that the seen_outside fix plus existing emitters covers it; re-listen decides, and anything still missing becomes a precise follow-up. Full Release suite: 11,740 passed / 4 skipped / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
78b981cca0
commit
e5ade796ac
6 changed files with 159 additions and 14 deletions
|
|
@ -290,28 +290,54 @@ public interface IAmbientListenerSource
|
|||
public sealed class LocalPlayerAmbientListenerSource : IAmbientListenerSource
|
||||
{
|
||||
private readonly AcDream.Runtime.Gameplay.RuntimeLocalPlayerMovementState _player;
|
||||
private readonly Func<uint, Vector3, Vector3?> _indoorLandblockLocal;
|
||||
|
||||
public LocalPlayerAmbientListenerSource(
|
||||
AcDream.Runtime.Gameplay.RuntimeLocalPlayerMovementState player) =>
|
||||
AcDream.Runtime.Gameplay.RuntimeLocalPlayerMovementState player,
|
||||
Func<uint, Vector3, Vector3?>? indoorLandblockLocal = null)
|
||||
{
|
||||
_player = player ?? throw new ArgumentNullException(nameof(player));
|
||||
_indoorLandblockLocal = indoorLandblockLocal ?? ((_, _) => null);
|
||||
}
|
||||
|
||||
public bool TryGetListener(out AmbientListenerPose pose)
|
||||
{
|
||||
if (_player.Controller is { } controller)
|
||||
{
|
||||
AcDream.Core.Physics.Position cell = controller.CellPosition;
|
||||
uint objCellId = controller.CellId;
|
||||
Vector3 landblockLocal = cell.Frame.Origin;
|
||||
bool seenOutside = false;
|
||||
|
||||
// Retail's gate is `isOutdoorCell(pos) || curr_cell->seen_outside`
|
||||
// (TS-66, retired with this wiring): a sky-lit interior — a
|
||||
// cottage, an open shopfront — keeps the OUTDOOR ambient set,
|
||||
// while a sealed dungeon stays silent. The flag is the same
|
||||
// per-cell `CEnvCell.seen_outside` bit the physics cache already
|
||||
// carries for AdjustPosition (#107).
|
||||
//
|
||||
// Frames: an OUTDOOR Position's origin is already landblock-local,
|
||||
// but an ENVCELL's origin is CELL-local — it must go through the
|
||||
// cell's own transform (the dat authors cell Positions in
|
||||
// landblock coordinates) before the 3×3 walk can centre on it.
|
||||
// The resolver returns null when it cannot answer (cell record
|
||||
// not yet resident), which keeps the interior silent for that
|
||||
// rebuild rather than centring the walk on a wrong point.
|
||||
if ((objCellId & 0xFFFFu) >= 0x0100u)
|
||||
{
|
||||
if (_indoorLandblockLocal(objCellId, cell.Frame.Origin)
|
||||
is { } converted)
|
||||
{
|
||||
landblockLocal = converted;
|
||||
seenOutside = true;
|
||||
}
|
||||
}
|
||||
|
||||
pose = new AmbientListenerPose(
|
||||
controller.CellId,
|
||||
objCellId,
|
||||
controller.Position,
|
||||
cell.Frame.Origin,
|
||||
// Retail's gate is `isOutdoorCell(pos) || curr_cell->seen_outside`,
|
||||
// so a sky-lit interior keeps the outdoor set. acdream's
|
||||
// seen_outside lives on the cell's collision record rather than
|
||||
// its Position, and resolving it here needs a physics-cache
|
||||
// lookup this source does not own — deferred as TS-66. Until
|
||||
// then every interior is silent, which is right for a dungeon
|
||||
// and wrong for a cottage.
|
||||
SeenOutside: false);
|
||||
landblockLocal,
|
||||
seenOutside);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue