diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h
index 004436b2..d44eaa33 100644
--- a/Minecraft.Client/Common/App_Defines.h
+++ b/Minecraft.Client/Common/App_Defines.h
@@ -109,6 +109,7 @@ enum EGameHostOptionWorldSize
#define GAMESETTING_EXCLUSIVEFULLSCREEN 0x02000000
#define GAMESETTING_CLASSICCRAFTING 0x04000000
#define GAMESETTING_CAVESOUNDS 0x08000000
+#define GAMESETTING_MINECARTSOUNDS 0x10000000
// defines for languages
diff --git a/Minecraft.Client/Common/App_enums.h b/Minecraft.Client/Common/App_enums.h
index 1564f088..d272bb51 100644
--- a/Minecraft.Client/Common/App_enums.h
+++ b/Minecraft.Client/Common/App_enums.h
@@ -185,6 +185,7 @@ enum eGameSetting
//TU25
eGameSetting_ClassicCrafting,
eGameSetting_CaveSounds,
+ eGameSetting_MinecartSounds,
};
diff --git a/Minecraft.Client/Common/Audio/SoundEngine.cpp b/Minecraft.Client/Common/Audio/SoundEngine.cpp
index 2828713d..7662c4f8 100644
--- a/Minecraft.Client/Common/Audio/SoundEngine.cpp
+++ b/Minecraft.Client/Common/Audio/SoundEngine.cpp
@@ -605,34 +605,8 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, floa
m_activeSounds.push_back(s);
}
-/////////////////////////////////////////////
-//
-//
-// startElytraSound / stopElytraSound
-// Manages a single persistent looping sound for elytra gliding.
-// Call startElytraSound every tick while gliding (it no-ops if already running,
-// just updates volume). Call stopElytraSound when gliding ends.
-//
-// IMPORTANT: m_elytraLoopingSound is NOT added to m_activeSounds.
-// The tick() cleanup loop deletes sounds where is_playing()==false.
-// A looping sound briefly reports is_playing()==false at the loop point,
-// which would cause tick() to free it and leave m_elytraLoopingSound dangling.
-//
-/////////////////////////////////////////////
-void SoundEngine::startElytraSound(float x, float y, float z, float volume, float pitch)
+MiniAudioSound* SoundEngine::startLoopingSound(const wstring& name, float x, float y, float z, float volume, float pitch, bool bIs3D)
{
- // If already initialized just update volume and pitch - never reinitialize mid-flight.
- if (m_elytraLoopingSound != nullptr)
- {
- float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
- if (finalVolume > SFX_MAX_GAIN) finalVolume = SFX_MAX_GAIN;
- ma_sound_set_volume(&m_elytraLoopingSound->sound, finalVolume);
- ma_sound_set_pitch(&m_elytraLoopingSound->sound, pitch);
- return;
- }
-
- // Resolve file path using the same logic as play().
- wstring name = wchSoundNames[eSoundType_ITEM_ELYTRA_FLYING];
char* soundName = ConvertSoundPathToName(name);
char basePath[256];
sprintf_s(basePath, "Windows64Media/Sound/Minecraft/%s", soundName);
@@ -652,42 +626,100 @@ void SoundEngine::startElytraSound(float x, float y, float z, float volume, floa
break;
}
}
- if (!found) return;
+ if (!found)
+ {
+ return nullptr;
+ }
MiniAudioSound* s = new MiniAudioSound();
memset(&s->info, 0, sizeof(AUDIO_INFO));
- s->info.volume = volume; s->info.pitch = pitch;
- s->info.bIs3D = false;
- s->info.iSound = eSoundType_ITEM_ELYTRA_FLYING + eSFX_MAX;
+ s->info.x = x;
+ s->info.y = y;
+ s->info.z = z;
+ s->info.volume = volume;
+ s->info.pitch = pitch;
+ s->info.bIs3D = bIs3D;
+ s->info.bUseSoundsPitchVal = false;
- // Synchronous load so the sound is immediately ready - no ASYNC gap.
- if (ma_sound_init_from_file(&m_engine, finalPath, 0,
- nullptr, nullptr, &s->sound) != MA_SUCCESS)
+ if (ma_sound_init_from_file(&m_engine, finalPath, 0, nullptr, nullptr, &s->sound) != MA_SUCCESS)
{
delete s;
- return;
+ return nullptr;
}
- ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE);
+ ma_sound_set_spatialization_enabled(&s->sound, bIs3D ? MA_TRUE : MA_FALSE);
ma_sound_set_looping(&s->sound, MA_TRUE);
float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
- if (finalVolume > SFX_MAX_GAIN) finalVolume = SFX_MAX_GAIN;
+ if (finalVolume > SFX_MAX_GAIN)
+ finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, pitch);
+ if (bIs3D)
+ {
+ ma_sound_set_position(&s->sound, x, y, z);
+ }
ma_sound_start(&s->sound);
+ return s;
+}
+void SoundEngine::updateLoopingSound(MiniAudioSound* sound, float x, float y, float z, float volume, float pitch)
+{
+ if (sound == nullptr)
+ {
+ return;
+ }
+
+ float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
+ if (finalVolume > SFX_MAX_GAIN)
+ finalVolume = SFX_MAX_GAIN;
+ ma_sound_set_volume(&sound->sound, finalVolume);
+ ma_sound_set_pitch(&sound->sound, pitch);
+ ma_sound_set_position(&sound->sound, x, y, z);
+}
+
+void SoundEngine::stopLoopingSound(MiniAudioSound* sound)
+{
+ if (sound == nullptr)
+ {
+ return;
+ }
+
+ ma_sound_stop(&sound->sound);
+ ma_sound_uninit(&sound->sound);
+ delete sound;
+}
+
+/////////////////////////////////////////////
+//
+//
+// startElytraSound / stopElytraSound
+// Manages a single persistent looping sound for elytra gliding.
+// Call startElytraSound every tick while gliding (it no-ops if already running,
+// just updates volume). Call stopElytraSound when gliding ends.
+//
+// IMPORTANT: m_elytraLoopingSound is NOT added to m_activeSounds.
+// The tick() cleanup loop deletes sounds where is_playing()==false.
+// A looping sound briefly reports is_playing()==false at the loop point,
+// which would cause tick() to free it and leave m_elytraLoopingSound dangling.
+//
+/////////////////////////////////////////////
+void SoundEngine::startElytraSound(float x, float y, float z, float volume, float pitch)
+{
+ // If already initialized just update volume and pitch - never reinitialize mid-flight.
+ if (m_elytraLoopingSound != nullptr)
+ {
+ updateLoopingSound(m_elytraLoopingSound, x, y, z, volume, pitch);
+ return;
+ }
// NOT added to m_activeSounds - tick() cleanup would delete it at loop boundaries.
- m_elytraLoopingSound = s;
+ m_elytraLoopingSound = startLoopingSound(wchSoundNames[eSoundType_ITEM_ELYTRA_FLYING], x, y, z, volume, pitch, false);
}
void SoundEngine::stopElytraSound()
{
if (m_elytraLoopingSound == nullptr) return;
-
- ma_sound_stop(&m_elytraLoopingSound->sound);
- ma_sound_uninit(&m_elytraLoopingSound->sound);
- delete m_elytraLoopingSound;
+ stopLoopingSound(m_elytraLoopingSound);
m_elytraLoopingSound = nullptr;
}
/////////////////////////////////////////////
diff --git a/Minecraft.Client/Common/Audio/SoundEngine.h b/Minecraft.Client/Common/Audio/SoundEngine.h
index 5a593df9..8b3ec7a2 100644
--- a/Minecraft.Client/Common/Audio/SoundEngine.h
+++ b/Minecraft.Client/Common/Audio/SoundEngine.h
@@ -127,6 +127,9 @@ public:
void GetSoundName(char *szSoundName,int iSound);
#endif
void play(int iSound, float x, float y, float z, float volume, float pitch) override;
+ MiniAudioSound* startLoopingSound(const wstring& name, float x, float y, float z, float volume, float pitch, bool bIs3D = true);
+ void updateLoopingSound(MiniAudioSound* sound, float x, float y, float z, float volume, float pitch);
+ void stopLoopingSound(MiniAudioSound* sound);
void startElytraSound(float x, float y, float z, float volume, float pitch);
void stopElytraSound();
void playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay=true) override;
diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp
index cb400e4f..9627c7a9 100644
--- a/Minecraft.Client/Common/Consoles_App.cpp
+++ b/Minecraft.Client/Common/Consoles_App.cpp
@@ -1043,6 +1043,7 @@ int CMinecraftApp::SetDefaultOptions(C_4JProfile::PROFILESETTINGS *pSettings,con
//TU25
SetGameSettings(iPad, eGameSetting_ClassicCrafting, 0);
SetGameSettings(iPad, eGameSetting_CaveSounds, 1);
+ SetGameSettings(iPad, eGameSetting_MinecartSounds, 1);
// 4J-PB - leave these in, or remove from everywhere they are referenced!
// Although probably best to leave in unless we split the profile settings into platform specific classes - having different meaning per platform for the same bitmask could get confusing
@@ -1504,6 +1505,7 @@ void CMinecraftApp::ApplyGameSettingsChanged(int iPad)
//TU25
ActionGameSettings(iPad, eGameSetting_ClassicCrafting);
ActionGameSettings(iPad, eGameSetting_CaveSounds);
+ ActionGameSettings(iPad, eGameSetting_MinecartSounds);
}
void CMinecraftApp::ActionGameSettings(int iPad,eGameSetting eVal)
@@ -2529,6 +2531,21 @@ void CMinecraftApp::SetGameSettings(int iPad,eGameSetting eVal,unsigned char ucV
GameSettingsA[iPad]->bSettingsChanged = true;
}
break;
+ case eGameSetting_MinecartSounds:
+ if ((GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_MINECARTSOUNDS) != (ucVal & 0x01) << 28)
+ {
+ if (ucVal == 1)
+ {
+ GameSettingsA[iPad]->uiBitmaskValues |= GAMESETTING_MINECARTSOUNDS;
+ }
+ else
+ {
+ GameSettingsA[iPad]->uiBitmaskValues &= ~GAMESETTING_MINECARTSOUNDS;
+ }
+ ActionGameSettings(iPad, eVal);
+ GameSettingsA[iPad]->bSettingsChanged = true;
+ }
+ break;
}
}
@@ -2670,6 +2687,9 @@ unsigned char CMinecraftApp::GetGameSettings(int iPad,eGameSetting eVal)
case eGameSetting_CaveSounds:
return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_CAVESOUNDS) >> 27;
+ case eGameSetting_MinecartSounds:
+ return (GameSettingsA[iPad]->uiBitmaskValues & GAMESETTING_MINECARTSOUNDS) >> 28;
+
case eGameSetting_VSync:
return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_VSYNC)>>24;
diff --git a/Minecraft.Client/Common/Media/MediaWindows64/SettingsAudioMenu1080.swf b/Minecraft.Client/Common/Media/MediaWindows64/SettingsAudioMenu1080.swf
index 8e46ea2a..a0bbdfba 100644
Binary files a/Minecraft.Client/Common/Media/MediaWindows64/SettingsAudioMenu1080.swf and b/Minecraft.Client/Common/Media/MediaWindows64/SettingsAudioMenu1080.swf differ
diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp
index 7bfee03f..dc0e7b19 100644
--- a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp
+++ b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp
@@ -16,6 +16,8 @@ UIScene_SettingsAudioMenu::UIScene_SettingsAudioMenu(int iPad, void *initData, U
m_checkboxCaveSounds.init(L"Cave Sounds",eControl_CaveSounds,(app.GetGameSettings(m_iPad,eGameSetting_CaveSounds)!=0));
+ m_checkboxMinecartSounds.init(L"Minecart Sounds",eControl_MinecartSounds,(app.GetGameSettings(m_iPad,eGameSetting_MinecartSounds)!=0));
+
doHorizontalResizeCheck();
if(app.GetLocalPlayerCount()>1)
@@ -124,5 +126,8 @@ void UIScene_SettingsAudioMenu::handleCheckboxToggled(F64 controlId, bool select
case eControl_CaveSounds:
app.SetGameSettings(m_iPad, eGameSetting_CaveSounds, selected ? 1 : 0);
break;
+ case eControl_MinecartSounds:
+ app.SetGameSettings(m_iPad, eGameSetting_MinecartSounds, selected ? 1 : 0);
+ break;
}
}
diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h
index b0123b5a..fdc2bbbc 100644
--- a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h
+++ b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h
@@ -9,15 +9,18 @@ private:
{
eControl_Music,
eControl_Sound,
- eControl_CaveSounds
+ eControl_CaveSounds,
+ eControl_MinecartSounds
};
UIControl_Slider m_sliderMusic, m_sliderSound; // Sliders
UIControl_CheckBox m_checkboxCaveSounds; // Checkboxes
+ UIControl_CheckBox m_checkboxMinecartSounds;
UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene)
UI_MAP_ELEMENT( m_sliderMusic, "Music")
UI_MAP_ELEMENT( m_sliderSound, "Sound")
UI_MAP_ELEMENT( m_checkboxCaveSounds, "CaveSounds")
+ UI_MAP_ELEMENT( m_checkboxMinecartSounds, "MinecartSounds")
UI_END_MAP_ELEMENTS_AND_NAMES()
public:
diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/inside.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/inside.ogg
new file mode 100644
index 00000000..92487c8c
Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/inside.ogg differ
diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/rolling.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/rolling.ogg
new file mode 100644
index 00000000..534618f3
Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/minecart/rolling.ogg differ
diff --git a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml
index 89c9928a..77fad9fd 100644
--- a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml
+++ b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml
@@ -6582,6 +6582,10 @@ Would you like to install the mash-up pack or texture pack now?
Cave Sounds
+
+ Minecart Sounds
+
+
What would you like to do with this save game?
diff --git a/Minecraft.World/Minecart.cpp b/Minecraft.World/Minecart.cpp
index efefb7ea..1a06e5cd 100644
--- a/Minecraft.World/Minecart.cpp
+++ b/Minecraft.World/Minecart.cpp
@@ -13,6 +13,7 @@
#include "../Minecraft.Client/ServerLevel.h"
#include "com.mojang.nbt.h"
#include "Minecart.h"
+#include "MinecartSoundInstance.h"
#include "SharedConstants.h"
@@ -44,7 +45,8 @@ void Minecart::_init()
blocksBuilding = true;
setSize(0.98f, 0.7f);
heightOffset = bbHeight / 2.0f;
- soundUpdater = nullptr;
+ m_rollingSound = nullptr;
+ m_ridingSound = nullptr;
name = L"";
//
@@ -61,26 +63,43 @@ Minecart::Minecart(Level *level) : Entity( level )
Minecart::~Minecart()
{
- delete soundUpdater;
+ delete m_rollingSound;
+ delete m_ridingSound;
}
shared_ptr Minecart::createMinecart(Level *level, double x, double y, double z, int type)
{
+ shared_ptr minecart;
+
switch (type)
{
case TYPE_CHEST:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
case TYPE_FURNACE:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
case TYPE_TNT:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
case TYPE_SPAWNER:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
case TYPE_HOPPER:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
default:
- return std::make_shared(level, x, y, z);
+ minecart = std::make_shared(level, x, y, z);
+ break;
}
+
+ if (level != nullptr && level->isClientSide)
+ {
+ minecart->m_rollingSound = new MinecartSoundInstance(minecart);
+ minecart->m_ridingSound = new RidingMinecartSoundInstance(minecart);
+ }
+
+ return minecart;
}
bool Minecart::makeStepSound()
@@ -207,11 +226,36 @@ bool Minecart::isPickable()
void Minecart::remove()
{
Entity::remove();
+ // clean up sounds after invalidation
+ if (m_rollingSound)
+ {
+ delete m_rollingSound;
+ m_rollingSound = nullptr;
+ }
+ if (m_ridingSound)
+ {
+ delete m_ridingSound;
+ m_ridingSound = nullptr;
+ }
//if (soundUpdater != nullptr) soundUpdater->tick();
}
void Minecart::tick()
{
+ // minecart tick handler
+ if (level->isClientSide)
+ {
+ if (m_rollingSound)
+ {
+ m_rollingSound->tick();
+ }
+
+ if (m_ridingSound)
+ {
+ m_ridingSound->tick();
+ }
+ }
+
//if (soundUpdater != nullptr) soundUpdater->tick();
// 4J - make minecarts (server-side) tick twice, to put things back to how they were when we were accidently ticking them twice
for( int i = 0; i < 2; i++ )
diff --git a/Minecraft.World/Minecart.h b/Minecraft.World/Minecart.h
index bd1a69e6..e4a9121c 100644
--- a/Minecraft.World/Minecart.h
+++ b/Minecraft.World/Minecart.h
@@ -2,7 +2,8 @@
#include "Entity.h"
class DamageSource;
-class Tickable;
+class MinecartSoundInstance;
+class RidingMinecartSoundInstance;
class Minecart : public Entity
{
@@ -30,7 +31,8 @@ private:
static const int DATA_ID_CUSTOM_DISPLAY = 22;
bool flipped;
- Tickable *soundUpdater;
+ MinecartSoundInstance *m_rollingSound;
+ RidingMinecartSoundInstance *m_ridingSound;
wstring name;
protected:
diff --git a/Minecraft.World/MinecartSoundInstance.cpp b/Minecraft.World/MinecartSoundInstance.cpp
new file mode 100644
index 00000000..482ff8eb
--- /dev/null
+++ b/Minecraft.World/MinecartSoundInstance.cpp
@@ -0,0 +1,175 @@
+#include "stdafx.h"
+#include "MinecartSoundInstance.h"
+#include "Minecart.h"
+#include "net.minecraft.world.level.h"
+#include "../Minecraft.Client/Minecraft.h"
+#include "../Minecraft.Client/Common/Audio/SoundEngine.h"
+
+// MinecartSoundInstance
+
+MinecartSoundInstance::MinecartSoundInstance(shared_ptr minecart)
+ : m_minecart(minecart), m_bIsCurrentlyPlaying(false), m_sound(nullptr), m_volume(0.0f), m_pitch(1.0f)
+{
+}
+
+MinecartSoundInstance::~MinecartSoundInstance()
+{
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ }
+ m_sound = nullptr;
+}
+
+void MinecartSoundInstance::tick()
+{
+ if (!m_minecart || m_minecart->removed)
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_bIsCurrentlyPlaying = false;
+ return;
+ }
+
+ // minecart sound functionality check
+ if (!app.GetGameSettings(0, eGameSetting_MinecartSounds))
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_bIsCurrentlyPlaying = false;
+ return;
+ }
+
+ // volume + pitch calculations
+ // relative to minecart velocity
+ double xd = m_minecart->xd;
+ double zd = m_minecart->zd;
+ double velocity = sqrt(xd * xd + zd * zd);
+
+ if (velocity >= 0.01)
+ {
+ float clampedVel = (float)(velocity > 1.0 ? 1.0 : (velocity < 0.0 ? 0.0 : velocity));
+ m_volume = clampedVel * 0.75f;
+ m_pitch = 1.0f;
+
+ if (!m_bIsCurrentlyPlaying)
+ {
+ m_bIsCurrentlyPlaying = true;
+ if (Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ m_sound = Minecraft::GetInstance()->soundEngine->startLoopingSound(L"mob.minecart.rolling", (float)m_minecart->x, (float)m_minecart->y, (float)m_minecart->z, m_volume, m_pitch, true);
+ }
+ }
+ else if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->updateLoopingSound(m_sound, (float)m_minecart->x, (float)m_minecart->y, (float)m_minecart->z, m_volume, m_pitch);
+ }
+ }
+ else
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_volume = 0.0f;
+ m_pitch = 0.0f;
+ m_bIsCurrentlyPlaying = false;
+ }
+}
+
+// RidingMinecartSoundInstance
+
+RidingMinecartSoundInstance::RidingMinecartSoundInstance(shared_ptr minecart)
+ : m_minecart(minecart), m_bIsCurrentlyPlaying(false), m_sound(nullptr), m_volume(0.0f), m_pitch(0.0f)
+{
+}
+
+RidingMinecartSoundInstance::~RidingMinecartSoundInstance()
+{
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ }
+ m_sound = nullptr;
+}
+
+void RidingMinecartSoundInstance::tick()
+{
+ if (!m_minecart || m_minecart->removed)
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_bIsCurrentlyPlaying = false;
+ return;
+ }
+
+ // minecart sound functionality check
+ if (!app.GetGameSettings(0, eGameSetting_MinecartSounds))
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_bIsCurrentlyPlaying = false;
+ return;
+ }
+
+ // minecart passenger check
+ if (m_minecart->rider.lock() == nullptr)
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_bIsCurrentlyPlaying = false;
+ return;
+ }
+
+ // volume + pitch calculations
+ // relative to minecart velocity
+ double xd = m_minecart->xd;
+ double zd = m_minecart->zd;
+ double velocity = sqrt(xd * xd + zd * zd);
+
+ if (velocity >= 0.01)
+ {
+ float clampedVel = (float)(velocity > 1.0 ? 1.0 : (velocity < 0.0 ? 0.0 : velocity));
+ m_volume = clampedVel * 0.75f;
+ m_pitch = 1.0f;
+
+ if (!m_bIsCurrentlyPlaying)
+ {
+ m_bIsCurrentlyPlaying = true;
+ if (Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ m_sound = Minecraft::GetInstance()->soundEngine->startLoopingSound(L"mob.minecart.inside", (float)m_minecart->x, (float)m_minecart->y, (float)m_minecart->z, m_volume, m_pitch, false);
+ }
+ }
+ else if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->updateLoopingSound(m_sound, (float)m_minecart->x, (float)m_minecart->y, (float)m_minecart->z, m_volume, m_pitch);
+ }
+ }
+ else
+ {
+ if (m_sound && Minecraft::GetInstance() && Minecraft::GetInstance()->soundEngine)
+ {
+ Minecraft::GetInstance()->soundEngine->stopLoopingSound(m_sound);
+ m_sound = nullptr;
+ }
+ m_volume = 0.0f;
+ m_bIsCurrentlyPlaying = false;
+ }
+}
diff --git a/Minecraft.World/MinecartSoundInstance.h b/Minecraft.World/MinecartSoundInstance.h
new file mode 100644
index 00000000..e7d2ba78
--- /dev/null
+++ b/Minecraft.World/MinecartSoundInstance.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include
+
+class Minecart;
+class SoundEngine;
+struct MiniAudioSound;
+
+// minecart rolling sound
+class MinecartSoundInstance
+{
+protected:
+ shared_ptr m_minecart;
+ bool m_bIsCurrentlyPlaying;
+ MiniAudioSound* m_sound;
+ float m_volume;
+ float m_pitch;
+
+public:
+ MinecartSoundInstance(shared_ptr minecart);
+ virtual ~MinecartSoundInstance();
+
+ virtual void tick();
+ bool isCurrentlyPlaying() const { return m_bIsCurrentlyPlaying; }
+};
+
+// minecart passenger sound
+class RidingMinecartSoundInstance
+{
+protected:
+ shared_ptr m_minecart;
+ bool m_bIsCurrentlyPlaying;
+ MiniAudioSound* m_sound;
+ float m_volume;
+ float m_pitch;
+
+public:
+ RidingMinecartSoundInstance(shared_ptr minecart);
+ virtual ~RidingMinecartSoundInstance();
+
+ virtual void tick();
+ bool isCurrentlyPlaying() const { return m_bIsCurrentlyPlaying; }
+};
diff --git a/Minecraft.World/cmake/sources/Common.cmake b/Minecraft.World/cmake/sources/Common.cmake
index 8c201c0b..3d522d3d 100644
--- a/Minecraft.World/cmake/sources/Common.cmake
+++ b/Minecraft.World/cmake/sources/Common.cmake
@@ -838,6 +838,8 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_WORLD_ENTITY_ITEM
"${CMAKE_CURRENT_SOURCE_DIR}/ItemEntity.h"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecart.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecart.h"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MinecartSoundInstance.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MinecartSoundInstance.h"
"${CMAKE_CURRENT_SOURCE_DIR}/MinecartChest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MinecartChest.h"
"${CMAKE_CURRENT_SOURCE_DIR}/MinecartContainer.cpp"