Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- Fixed a couple of uninitialized values (Atom step state and the simulation frame counter) that could cause inconsistent behavior between runs.

- Fixed `AHuman:EquipShieldInBGArm()` being able to take the wrong item out of the inventory, which left the device it equipped both held and in the inventory. The device then got dropped twice when its holder died, and the game could crash when quitting.

</details>

<details><summary><b>Removed</b></summary>
Expand Down
5 changes: 4 additions & 1 deletion Source/Entities/AHuman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,10 @@ bool AHuman::EquipShieldInBGArm(bool depositToFront) {
if (HeldDevice* heldDevice = m_pBGArm->GetHeldDevice()) {
heldDevice->Deactivate();
if (depositToFront) {
AddToInventoryFront(m_pBGArm->RemoveAttachable(heldDevice));
// What goes in at the front moves the device we are after one place further in.
if (AddToInventoryFront(m_pBGArm->RemoveAttachable(heldDevice))) {
device_offset++;
}
} else {
AddToInventoryBack(m_pBGArm->RemoveAttachable(heldDevice));
}
Expand Down
4 changes: 4 additions & 0 deletions Source/Entities/SoundContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ namespace RTE {
/// Destroys and resets (through Clear()) the SoundContainer object. It doesn't delete the Sound files, since they're owned by ContentFile static maps.
/// @param notInherited Whether to only destroy the members defined in this derived class, or to destroy all inherited members also.
void Destroy(bool notInherited = false) override {
// The channels that are still playing its sounds can't be left tied to this once it is gone.
if (!m_PlayingChannels.empty()) {
g_AudioMan.DetachSoundContainerPlayingChannels(this);
}
if (!notInherited) {
Entity::Destroy();
}
Expand Down
1 change: 1 addition & 0 deletions Source/Lua/LuaAdapterDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ namespace RTE {

#pragma region AHuman Lua Adapters
struct LuaAdaptersAHuman {
static bool EquipShieldInBGArm(AHuman* luaSelfObject);
static void ReloadFirearms(AHuman* luaSelfObject);
};
#pragma endregion
Expand Down
4 changes: 4 additions & 0 deletions Source/Lua/LuaAdapters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ void LuaAdaptersScene::CalculatePathAsync(Scene* luaSelfObject, const luabind::o
luaSelfObject->CalculatePathAsync(start, end, jumpHeight, digStrength, team, callLuaCallback);
}

bool LuaAdaptersAHuman::EquipShieldInBGArm(AHuman* luaSelfObject) {
return luaSelfObject->EquipShieldInBGArm();
}

void LuaAdaptersAHuman::ReloadFirearms(AHuman* luaSelfObject) {
luaSelfObject->ReloadFirearms(false);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Lua/LuaBindingsEntities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, AHuman) {
.def("EquipThrowable", &AHuman::EquipThrowable)
.def("EquipDiggingTool", &AHuman::EquipDiggingTool)
.def("EquipShield", &AHuman::EquipShield)
.def("EquipShieldInBGArm", (bool(AHuman::*)()) & AHuman::EquipShieldInBGArm)
.def("EquipShieldInBGArm", &LuaAdaptersAHuman::EquipShieldInBGArm)
.def("EquipDeviceInGroup", &AHuman::EquipDeviceInGroup)
.def("EquipNamedDevice", (bool(AHuman::*)(const std::string&, bool)) & AHuman::EquipNamedDevice)
.def("EquipNamedDevice", (bool(AHuman::*)(const std::string&, const std::string&, bool)) & AHuman::EquipNamedDevice)
Expand Down
34 changes: 31 additions & 3 deletions Source/Managers/AudioMan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,28 @@ bool AudioMan::StopSoundContainerPlayingChannels(SoundContainer* soundContainer,
return result == FMOD_OK;
}

void AudioMan::DetachSoundContainerPlayingChannels(SoundContainer* soundContainer) {
if (!m_AudioEnabled || !soundContainer) {
return;
}

FMOD::Channel* soundChannel;
int loopCount;

// Stopping a channel takes it out of the SoundContainer's own collection of them, so it's a copy that is gone through here.
const std::unordered_set<int> channels = *soundContainer->GetPlayingChannels();
for (int channel: channels) {
if (m_AudioSystem->getChannel(channel, &soundChannel) != FMOD_OK) {
continue;
}
if (soundChannel->getLoopCount(&loopCount) == FMOD_OK && loopCount < 0) {
soundChannel->stop();
} else {
soundChannel->setUserData(nullptr);
}
}
}

void AudioMan::FadeOutSoundContainerPlayingChannels(SoundContainer* soundContainer, int fadeOutTime) {
if (!m_AudioEnabled || !soundContainer || !soundContainer->IsBeingPlayed()) {
return;
Expand Down Expand Up @@ -673,7 +695,8 @@ void AudioMan::Update3DEffectsForSFXChannels() {
float doubleMinimumDistanceForPanning = m_MinimumDistanceForPanning * 2.0F;
void* userData;
result = result == FMOD_OK ? soundChannel->getUserData(&userData) : result;
if (result == FMOD_OK) {
// A sound that is playing out after its SoundContainer got destroyed stays the way it was left.
if (result == FMOD_OK && userData) {
const SoundContainer* soundContainer = static_cast<SoundContainer*>(userData);
if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning) || soundContainer->GetCustomPanValue() != 0.0f) {
result = soundChannel->set3DLevel(0);
Expand Down Expand Up @@ -702,6 +725,10 @@ FMOD_RESULT AudioMan::UpdatePositionalEffectsForSoundChannel(FMOD::Channel* soun
}

const SoundContainer* channelSoundContainer = static_cast<SoundContainer*>(userData);
// A sound that is playing out after its SoundContainer got destroyed stays the way it was left.
if (!channelSoundContainer) {
return FMOD_OK;
}

bool sceneWraps = g_SceneMan.SceneWrapsX();

Expand Down Expand Up @@ -798,11 +825,12 @@ FMOD_RESULT F_CALLBACK AudioMan::SoundChannelEndedCallback(FMOD_CHANNELCONTROL*
FMOD_RESULT result = channel->getIndex(&channelIndex);

// Remove this playing sound index from the SoundContainer if it has any playing sounds, i.e. it hasn't been reset before this callback happened.
// There is no SoundContainer to remove it from if that got destroyed while the sound was left to play out.
void* userData;
result = (result == FMOD_OK) ? channel->getUserData(&userData) : result;
if (result == FMOD_OK) {
SoundContainer* channelSoundContainer = static_cast<SoundContainer*>(userData);
if (channelSoundContainer->IsBeingPlayed()) {
if (channelSoundContainer && channelSoundContainer->IsBeingPlayed()) {
channelSoundContainer->RemovePlayingChannel(channelIndex);
}
result = (result == FMOD_OK) ? channel->setUserData(nullptr) : result;
Expand All @@ -812,7 +840,7 @@ FMOD_RESULT F_CALLBACK AudioMan::SoundChannelEndedCallback(FMOD_CHANNELCONTROL*
}

if (result != FMOD_OK) {
g_ConsoleMan.PrintString("ERROR: An error occurred when Ending a sound in SoundContainer " + channelSoundContainer->GetPresetName() + ": " + std::string(FMOD_ErrorString(result)));
g_ConsoleMan.PrintString("ERROR: An error occurred when Ending a sound in SoundContainer " + (channelSoundContainer ? channelSoundContainer->GetPresetName() : "None") + ": " + std::string(FMOD_ErrorString(result)));
return result;
}
} else {
Expand Down
5 changes: 5 additions & 0 deletions Source/Managers/AudioMan.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@ namespace RTE {
/// @return
bool StopSoundContainerPlayingChannels(SoundContainer* soundContainer, int player);

/// Has the channels playing a SoundContainer's sounds no longer be tied to it, for when it is about to be destroyed.
/// The sounds get to play out, except for the ones that loop endlessly. Those get stopped, since there would be nothing left to stop them with.
/// @param soundContainer A pointer to a SoundContainer object. Ownership is NOT transferred!
void DetachSoundContainerPlayingChannels(SoundContainer* soundContainer);

/// Fades out playback a SoundContainer.
/// @param soundContainer A pointer to a SoundContainer object. Ownership is NOT transferred!
/// @param fadeOutTime The amount of time, in ms, to fade out over.
Expand Down
Loading