diff --git a/gamedata/scripts/callbacks_gameobject.script b/gamedata/scripts/callbacks_gameobject.script index fafd64c23..86d10ab49 100644 --- a/gamedata/scripts/callbacks_gameobject.script +++ b/gamedata/scripts/callbacks_gameobject.script @@ -651,6 +651,43 @@ _G.COnBeforePlayHudSound = function(alias, parent) end end +--[[ +on_before_play_ambient_bed callback, scale or veto the per-weather ambient background bed. +Fired from CGamePersistent::WeathersUpdate (GamePersistent.cpp) at each bed play site. +Return via result.volume_mult: 0 vetoes the play, <1 attenuates, nil leaves vanilla. + +Arguments + section: weather ambient section the bed loads from + file: resolved sound file name, "" when the handle is not ready yet + pos: Fvector world position the bed plays at +--]] +AddScriptCallback("on_before_play_ambient_bed") +_G.COnAmbientBed = function(section, file, pos) + local result = { volume_mult = nil } + SendScriptCallback("on_before_play_ambient_bed", section, file, pos, result) + if result.volume_mult then + return result + end +end + +--[[ +on_before_play_level_music callback, scale or silence the level music track. +Fired from SMusicTrack::Play (level_sounds.cpp). Volume-only: a skip would make the +manager re-select every frame, so 0 silences rather than stops the track. +Return via result.volume_mult: 0 silences, <1 attenuates, nil leaves vanilla. + +Arguments + file: resolved music track file name, "" when the handle is not ready yet +--]] +AddScriptCallback("on_before_play_level_music") +_G.COnLevelMusic = function(file) + local result = { volume_mult = nil } + SendScriptCallback("on_before_play_level_music", file, result) + if result.volume_mult then + return result + end +end + -- NLTP_ASHES & demonized : Actor on death callback AddScriptCallback("actor_on_death") function actor_on_first_update() diff --git a/src/xrGame/GamePersistent.cpp b/src/xrGame/GamePersistent.cpp index 19a8afb75..a96bfbd3f 100644 --- a/src/xrGame/GamePersistent.cpp +++ b/src/xrGame/GamePersistent.cpp @@ -276,6 +276,18 @@ void CGamePersistent::OnGameEnd() xr_delete(g_stalker_velocity_holder); } +// { volume_mult = number } from a sound hook's return; 1.0 (vanilla) when nil, not a table, or not a number +static float ambient_hook_volume_mult(const ::luabind::object& output) +{ + if (output && output.type() == LUA_TTABLE) + { + auto volume_mult_obj = output["volume_mult"]; + if (volume_mult_obj.type() == LUA_TNUMBER) + return ::luabind::object_cast(volume_mult_obj); + } + return 1.0f; +} + void CGamePersistent::WeathersUpdate() { if (g_pGameLevel && !g_dedicated_server) @@ -315,7 +327,22 @@ void CGamePersistent::WeathersUpdate() pos.z = _sin(angle); pos.normalize().mul(ch.get_rnd_sound_dist()).add(Device.vCameraPosition); pos.y += 10.f; - snd.play_at_pos(0, pos); + + // Ambient bed hook, mirrors COnBeforePlayHudSound: Lua may veto (0) or attenuate; absent global = exact vanilla + float bed_volume_mult = 1.0f; + ::luabind::functor<::luabind::object> bed_funct; + if (ai().script_engine().functor("_G.COnAmbientBed", bed_funct)) + { + LPCSTR bed_file = snd._handle() ? snd._handle()->file_name() : ""; + bed_volume_mult = ambient_hook_volume_mult(bed_funct(ch.m_load_section.c_str(), bed_file, pos)); + } + + if (bed_volume_mult > EPS_S) + { + snd.play_at_pos(0, pos); + if (bed_volume_mult < 1.0f) + snd.set_volume(bed_volume_mult); + } #ifdef DEBUG if (!snd._handle() && strstr(Core.Params, "-nosound")) diff --git a/src/xrGame/level_sounds.cpp b/src/xrGame/level_sounds.cpp index 31baef11f..91cb8841c 100644 --- a/src/xrGame/level_sounds.cpp +++ b/src/xrGame/level_sounds.cpp @@ -5,6 +5,20 @@ #include "level.h" #include "level_sounds.h" +#include "ai_space.h" +#include "../xrServerEntities/script_engine.h" + +// { volume_mult = number } from a sound hook's return; 1.0 (vanilla) when nil, not a table, or not a number +static float ambient_hook_volume_mult(const ::luabind::object& output) +{ + if (output && output.type() == LUA_TTABLE) + { + auto volume_mult_obj = output["volume_mult"]; + if (volume_mult_obj.type() == LUA_TNUMBER) + return ::luabind::object_cast(volume_mult_obj); + } + return 1.0f; +} //----------------------------------------------------------------------------- // static level sounds @@ -129,7 +143,16 @@ BOOL SMusicTrack::in(u32 game_time) void SMusicTrack::Play() { m_SourceStereo.play_at_pos(0, Fvector().set(0.0f, 0.0f, 0.0f), sm_Intro); - SetVolume(1.0f); + + // Level-music hook: volume-only (a skip would make the manager re-select every frame); absent global = vanilla + float music_volume_mult = 1.0f; + ::luabind::functor<::luabind::object> music_funct; + if (ai().script_engine().functor("_G.COnLevelMusic", music_funct)) + { + LPCSTR music_file = m_SourceStereo._handle() ? m_SourceStereo._handle()->file_name() : ""; + music_volume_mult = ambient_hook_volume_mult(music_funct(music_file)); + } + SetVolume(music_volume_mult); } BOOL SMusicTrack::IsPlaying()