Implement forced BGM opcodes
This commit is contained in:
@@ -1584,11 +1584,33 @@ public sealed class GodotAdvHost : IHost
|
||||
// ---- audio ops (OGG plays natively in Godot) ----
|
||||
// BGM is addressed by direct name (BGM{id:D3}.OGG); voice uses the universal packed catalog.
|
||||
public void PlayBgm(long id)
|
||||
=> DispatchBgm(id, 1, false);
|
||||
|
||||
public void RestartBgm(long id, int startMode)
|
||||
=> DispatchBgm(id, startMode, true);
|
||||
|
||||
private void DispatchBgm(long id, int startMode, bool forceRestart)
|
||||
{
|
||||
var asset = _res.ResolveBgm(id);
|
||||
var audio = asset != null ? LoadAudio(asset) : null;
|
||||
_timeline?.Event("bgm", new() { ["id"] = id, ["file"] = audio?.Name });
|
||||
if (audio != null) _main.CallDeferred("PlayBgm", audio.Bytes, audio.Name);
|
||||
_timeline?.Event("bgm", new()
|
||||
{
|
||||
["id"] = id,
|
||||
["file"] = audio?.Name,
|
||||
["start_mode"] = startMode,
|
||||
["force_restart"] = forceRestart,
|
||||
});
|
||||
if (audio == null) return;
|
||||
if (forceRestart)
|
||||
_main.CallDeferred("RestartBgm", audio.Bytes, audio.Name, startMode);
|
||||
else
|
||||
_main.CallDeferred("PlayBgm", audio.Bytes, audio.Name);
|
||||
}
|
||||
|
||||
public void StopBgm()
|
||||
{
|
||||
_timeline?.Event("bgm-stop", new());
|
||||
_main.CallDeferred("StopBgm");
|
||||
}
|
||||
|
||||
public void PlayVoice(long id) => PlayVoice(id, 0);
|
||||
|
||||
@@ -1829,12 +1829,19 @@ public partial class Main : Godot.Control
|
||||
px[i + 3] = 0;
|
||||
}
|
||||
|
||||
// Decode VFS-owned bytes in Godot. BGM loops; voice plays once, cutting off any prior line.
|
||||
// Decode VFS-owned bytes in Godot. Ordinary BGM loops; forced starts retain AGE's
|
||||
// loop/one-shot mode. Voice plays once, cutting off any prior line.
|
||||
public void PlayBgm(byte[] oggBytes, string assetName)
|
||||
=> StartBgm(oggBytes, assetName, 1);
|
||||
|
||||
public void RestartBgm(byte[] oggBytes, string assetName, int startMode)
|
||||
=> StartBgm(oggBytes, assetName, startMode);
|
||||
|
||||
private void StartBgm(byte[] oggBytes, string assetName, int startMode)
|
||||
{
|
||||
var stream = AudioStreamOggVorbis.LoadFromBuffer(oggBytes);
|
||||
if (stream == null) { GD.Print($"OGG load failed {assetName}"); return; }
|
||||
stream.Loop = true;
|
||||
stream.Loop = startMode != 0;
|
||||
// FadeBgm is marshalled from the VM thread and starts on the next Godot frame, while the VM's
|
||||
// blocking deadline begins immediately. Scene startup can consequently request the replacement
|
||||
// track just before the old fade tween reaches zero. Do not let that orphaned tween mute the new
|
||||
@@ -1845,6 +1852,15 @@ public partial class Main : Godot.Control
|
||||
_bgm.Play();
|
||||
}
|
||||
|
||||
public void StopBgm()
|
||||
{
|
||||
RestoreVoiceBgmDuck();
|
||||
CancelBgmFade();
|
||||
_bgm.Stop();
|
||||
_bgm.Stream = null;
|
||||
_bgm.VolumeDb = 0;
|
||||
}
|
||||
|
||||
private void PersistAudioMixerSettings(AudioMixerSettingsSnapshot snapshot)
|
||||
{
|
||||
try
|
||||
@@ -2268,8 +2284,15 @@ public partial class Main : Godot.Control
|
||||
bool bgmReplacementCancelsFade = bgmFadeStarted
|
||||
&& _bgmFadeTween == null
|
||||
&& System.Math.Abs(_bgm.VolumeDb) < 0.001f;
|
||||
_bgm.Stop();
|
||||
_bgm.Stream = null;
|
||||
RestartBgm(bgm.Bytes, bgm.Name, 0);
|
||||
bool bgmOneShotModeOk = (_bgm.Stream as AudioStreamOggVorbis)?.Loop == false;
|
||||
RestartBgm(bgm.Bytes, bgm.Name, 1);
|
||||
bool bgmLoopModeOk = (_bgm.Stream as AudioStreamOggVorbis)?.Loop == true;
|
||||
StopBgm();
|
||||
bool bgmStopReleaseOk = _bgm.Stream == null
|
||||
&& !_bgm.Playing
|
||||
&& _bgmFadeTween == null
|
||||
&& System.Math.Abs(_bgm.VolumeDb) < 0.001f;
|
||||
AdvTextEffectTheme mode1 = ResolveAdvTextEffectTheme(AdvTextStyle.Default with
|
||||
{
|
||||
RenderMode = 1,
|
||||
@@ -2337,18 +2360,22 @@ public partial class Main : Godot.Control
|
||||
new Sys4LogicalCanvas(_screenWidth, _screenHeight));
|
||||
textEffectSmoke.QueueFree();
|
||||
ok &= launcherOk && sleepMinimumOk && inputTranslationOk && cp932WavMetadataOk
|
||||
&& bgmReplacementCancelsFade && textEffectModesOk && fontCalibrationOk
|
||||
&& bgmReplacementCancelsFade && bgmOneShotModeOk && bgmLoopModeOk
|
||||
&& bgmStopReleaseOk && textEffectModesOk && fontCalibrationOk
|
||||
&& logicalCanvasOk;
|
||||
if (ok) GD.Print($"SELFTEST OK: threaded host matches headless ({actual.Count} lines, full handling); " +
|
||||
$"debug launcher catalog/UI smoke ({debugEntries.Count} packed scripts); " +
|
||||
$"sleep-min=1ms; native-key-translation=ok; cp932-wav-info=ok; " +
|
||||
$"bgm-fade-replacement=ok; text-effect-modes=ok; font-calibration=ok; " +
|
||||
$"bgm-fade-replacement=ok; bgm-start-modes-stop=ok; " +
|
||||
$"text-effect-modes=ok; font-calibration=ok; " +
|
||||
$"logical-canvas={_screenWidth}x{_screenHeight}; " +
|
||||
$"window-request={_windowOptions.Width}x{_windowOptions.Height}");
|
||||
else GD.Print($"SELFTEST FAIL: threaded={actual.Count} vs headless={expected.Count}; " +
|
||||
$"debug-launcher={launcherOk}; sleep-min={sleepMinimumOk}; " +
|
||||
$"native-key-translation={inputTranslationOk}; cp932-wav-info={cp932WavMetadataOk}; " +
|
||||
$"bgm-fade-replacement={bgmReplacementCancelsFade}; " +
|
||||
$"bgm-one-shot={bgmOneShotModeOk}; bgm-loop={bgmLoopModeOk}; " +
|
||||
$"bgm-stop-release={bgmStopReleaseOk}; " +
|
||||
$"text-effect-modes={textEffectModesOk}; font-calibration={fontCalibrationOk}; " +
|
||||
$"logical-canvas={logicalCanvasOk}({_screenWidth}x{_screenHeight}); " +
|
||||
$"window-request={_windowOptions.Width}x{_windowOptions.Height}");
|
||||
|
||||
Reference in New Issue
Block a user