`CAECollisionAudioEntity` (#1099)
* Fix `CAECollisionAudioEntity::AddCollisionSoundToList` * Fix `GetCollisionSoundStatus` * Add enums to `eAudioSlot` * Add enums to `eAudioBank` * `ReportCollision` * `PlayLoopingCollisionSound` * `ReportObjectDestruction` * `ReportGlassCollisionEvent` * `UpdateLoopingCollisionSound`, `GetCollisionSoundStatus` + Refactors * `CAEAudioHardware::EnsureSoundBankIsLoaded`
This commit is contained in:
parent
57e4df0ed8
commit
504276662b
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
enum GENRL_COLLISIONS : int32 {
|
||||
SND_GENRL_COLLISIONS_GRASS_SKID = 0x0,
|
||||
SND_GENRL_COLLISIONS_GRAVEL_SKID = 0x1,
|
||||
SND_GENRL_COLLISIONS_METAL_SCRAPE = 0x2,
|
||||
SND_GENRL_COLLISIONS_WATER_LOOP = 0x3,
|
||||
SND_GENRL_COLLISIONS_BARREL_CONE = 0x4,
|
||||
SND_GENRL_COLLISIONS_BIKE_CRASH1 = 0x5,
|
||||
SND_GENRL_COLLISIONS_BIKE_CRASH2 = 0x6,
|
||||
SND_GENRL_COLLISIONS_BIKE_CRASH3 = 0x7,
|
||||
SND_GENRL_COLLISIONS_BIKE_CRASH4 = 0x8,
|
||||
SND_GENRL_COLLISIONS_BIKE_CRASH5 = 0x9,
|
||||
SND_GENRL_COLLISIONS_BIN_BAG = 0xA,
|
||||
SND_GENRL_COLLISIONS_BOTTLE_SMASH = 0xB,
|
||||
SND_GENRL_COLLISIONS_CARDBOARD_BOX1 = 0xC,
|
||||
SND_GENRL_COLLISIONS_CARDBOARD_BOX2 = 0xD,
|
||||
SND_GENRL_COLLISIONS_CARDBOARD_BOX3 = 0xE,
|
||||
SND_GENRL_COLLISIONS_COL_GLASS_TINK1 = 0xF,
|
||||
SND_GENRL_COLLISIONS_COL_GLASS_TINK2 = 0x10,
|
||||
SND_GENRL_COLLISIONS_COL_GLASS_TINK3 = 0x11,
|
||||
SND_GENRL_COLLISIONS_COL_GLASS_TINK4 = 0x12,
|
||||
SND_GENRL_COLLISIONS_COLBENCH1 = 0x13,
|
||||
SND_GENRL_COLLISIONS_COLCAR01 = 0x14,
|
||||
SND_GENRL_COLLISIONS_COLCAR03 = 0x15,
|
||||
SND_GENRL_COLLISIONS_COLCAR04 = 0x16,
|
||||
SND_GENRL_COLLISIONS_COLCAR05 = 0x17,
|
||||
SND_GENRL_COLLISIONS_COLCAR06 = 0x18,
|
||||
SND_GENRL_COLLISIONS_COLCAR08 = 0x19,
|
||||
SND_GENRL_COLLISIONS_COLCAR09 = 0x1A,
|
||||
SND_GENRL_COLLISIONS_COLCAR10 = 0x1B,
|
||||
SND_GENRL_COLLISIONS_COLCAR12 = 0x1C,
|
||||
SND_GENRL_COLLISIONS_COLCARPED = 0x1D,
|
||||
SND_GENRL_COLLISIONS_COLCONTAINER = 0x1E,
|
||||
SND_GENRL_COLLISIONS_COLGRASS = 0x1F,
|
||||
SND_GENRL_COLLISIONS_COLLAMPPOST = 0x20,
|
||||
SND_GENRL_COLLISIONS_COLSOLIDWOOD = 0x21,
|
||||
SND_GENRL_COLLISIONS_CONCRETE = 0x22,
|
||||
SND_GENRL_COLLISIONS_CRATE1 = 0x23,
|
||||
SND_GENRL_COLLISIONS_CRATE2 = 0x24,
|
||||
SND_GENRL_COLLISIONS_CRATE3 = 0x25,
|
||||
SND_GENRL_COLLISIONS_DISPENSER = 0x26,
|
||||
SND_GENRL_COLLISIONS_DUMPSTER = 0x27,
|
||||
SND_GENRL_COLLISIONS_FENCE_HEAVY = 0x28,
|
||||
SND_GENRL_COLLISIONS_FENCE_PARTICLES1 = 0x29,
|
||||
SND_GENRL_COLLISIONS_FENCE_PARTICLES2 = 0x2A,
|
||||
SND_GENRL_COLLISIONS_FENCE_PARTICLES3 = 0x2B,
|
||||
SND_GENRL_COLLISIONS_FENCE_PARTICLES4 = 0x2C,
|
||||
SND_GENRL_COLLISIONS_FENCE_THIN = 0x2D,
|
||||
SND_GENRL_COLLISIONS_GARAGE_DOOR = 0x2E,
|
||||
SND_GENRL_COLLISIONS_GORE_SPLAT1 = 0x2F,
|
||||
SND_GENRL_COLLISIONS_GORE_SPLAT2 = 0x30,
|
||||
SND_GENRL_COLLISIONS_GORE_SPLAT3 = 0x31,
|
||||
SND_GENRL_COLLISIONS_GRENADE = 0x32,
|
||||
SND_GENRL_COLLISIONS_HARD_GLASS = 0x33,
|
||||
SND_GENRL_COLLISIONS_HAY_BALE = 0x34,
|
||||
SND_GENRL_COLLISIONS_HEADLIGHT = 0x35,
|
||||
SND_GENRL_COLLISIONS_HYDRANT = 0x36,
|
||||
SND_GENRL_COLLISIONS_METAL_BARREL = 0x37,
|
||||
SND_GENRL_COLLISIONS_PANE_BREAK = 0x38,
|
||||
SND_GENRL_COLLISIONS_RIMHIT = 0x39,
|
||||
SND_GENRL_COLLISIONS_RIMSCRAPE = 0x3A,
|
||||
SND_GENRL_COLLISIONS_ROADCONE = 0x3B,
|
||||
SND_GENRL_COLLISIONS_RUBBER = 0x3C,
|
||||
SND_GENRL_COLLISIONS_SATCHEL_CHARGE = 0x3D,
|
||||
SND_GENRL_COLLISIONS_SCAFFOLD_TIGHT = 0x3E,
|
||||
SND_GENRL_COLLISIONS_SIGN_A = 0x3F,
|
||||
SND_GENRL_COLLISIONS_SIGN_B = 0x40,
|
||||
SND_GENRL_COLLISIONS_SOLID_METAL = 0x41,
|
||||
SND_GENRL_COLLISIONS_SPLASH_END = 0x42,
|
||||
SND_GENRL_COLLISIONS_SPLASH_START = 0x43,
|
||||
SND_GENRL_COLLISIONS_WINDSCREEN = 0x44,
|
||||
SND_GENRL_COLLISIONS_WIRE_FENCE1 = 0x45,
|
||||
SND_GENRL_COLLISIONS_WIRE_FENCE2 = 0x46,
|
||||
SND_GENRL_COLLISIONS_WIRE_FENCE3 = 0x47,
|
||||
};
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
#pragma once
|
||||
|
||||
struct CollisionLookupEntry {
|
||||
int32 MinSoundID, MaxSoundID; //!< Sound ID ranges for this surface
|
||||
int32 MaxStartAt; //!< Max start at (in %)
|
||||
int32 ParamD;
|
||||
};
|
||||
static inline constexpr std::array<CollisionLookupEntry, SURFACE_NUM_TYPES_FOR_COLLISION> gCollisionLookup = {{ // No clue what the +16 is for
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 35, 37, 90, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 40, 40, 60, 40 },
|
||||
{ -1, -1, 0, 30 },
|
||||
{ -1, -1, 0, 30 },
|
||||
{ -1, -1, 0, 30 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ 46, 46, 90, 80 },
|
||||
{ 65, 65, 60, 100 },
|
||||
{ 62, 62, 80, 100 },
|
||||
{ 32, 32, 40, 100 },
|
||||
{ 69, 71, 80, 40 },
|
||||
{ 69, 71, 80, 40 },
|
||||
{ 65, 65, 60, 100 },
|
||||
{ 54, 54, 50, 100 },
|
||||
{ 30, 30, 60, 100 },
|
||||
{ 38, 38, 70, 60 },
|
||||
{ 60, 60, 0, 30 },
|
||||
{ 12, 14, 0, 40 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ 20, 28, 90, 100 },
|
||||
{ 20, 28, 90, 100 },
|
||||
{ 20, 28, 90, 100 },
|
||||
{ -1, -1, 1, 20 },
|
||||
{ 60, 60, 0, 30 },
|
||||
{ 4, 4, 60, 30 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 19, 19, 80, 50 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 33, 33, 0, 50 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ -1, -1, 0, 20 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 33, 33, 0, 70 },
|
||||
{ 4, 4, 60, 30 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 65, 65, 60, 100 },
|
||||
{ 31, 31, 0, 20 },
|
||||
{ 65, 65, 60, 100 },
|
||||
{ 34, 34, 70, 100 },
|
||||
{ 10, 10, 40, 10 },
|
||||
{ 63, 64, 50, 80 },
|
||||
{ 55, 55, 60, 80 },
|
||||
{ 59, 59, 30, 20 },
|
||||
{ 4, 4, 60, 30 },
|
||||
{ 39, 39, 50, 80 },
|
||||
{ 45, 45, 70, 40 },
|
||||
{ 40, 40, 70, 50 },
|
||||
{ 40, 40, 70, 50 },
|
||||
{ 51, 51, 30, 50 },
|
||||
{ 52, 52, 50, 20 },
|
||||
{ 47, 49, 30, 20 },
|
||||
{ 65, 65, 60, 100 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ -1, -1, 0, 0 },
|
||||
{ 5, 9, 90, 60 },
|
||||
{ 11, 11, 0, 30 },
|
||||
{ 61, 61, 0, 40 },
|
||||
{ 50, 50, 0, 30 },
|
||||
{ 0, 2, 0, 70 },
|
||||
{ 0, 2, 0, 30 },
|
||||
{ 4, 6, 0, 40 }
|
||||
}};
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
#include "StdInc.h"
|
||||
|
||||
#include "AECollisionAudioEntity.h"
|
||||
#include <extensions/utility.hpp>
|
||||
|
||||
#include "AECollisionAudioEntity.h"
|
||||
#include "AEAudioHardware.h"
|
||||
#include "AEAudioUtility.h"
|
||||
|
||||
#include "AECollisionAudioEntity.CollisionLookup.h"
|
||||
|
||||
void CAECollisionAudioEntity::InjectHooks() {
|
||||
RH_ScopedVirtualClass(CAECollisionAudioEntity, 0x862E64, 1);
|
||||
RH_ScopedCategory("Audio/Entities");
|
||||
|
|
@ -14,18 +17,18 @@ void CAECollisionAudioEntity::InjectHooks() {
|
|||
RH_ScopedInstall(AddCollisionSoundToList, 0x4DAAC0);
|
||||
RH_ScopedInstall(Reset, 0x4DA320);
|
||||
RH_ScopedInstall(ReportGlassCollisionEvent, 0x4DA070);
|
||||
RH_ScopedInstall(UpdateLoopingCollisionSound, 0x4DA540, { .reversed = false });
|
||||
RH_ScopedInstall(GetCollisionSoundStatus, 0x4DA830, { .reversed = true });
|
||||
RH_ScopedInstall(ReportObjectDestruction, 0x4DAB60, { .reversed = false });
|
||||
RH_ScopedInstall(PlayOneShotCollisionSound, 0x4DB150, { .reversed = false });
|
||||
RH_ScopedInstall(PlayLoopingCollisionSound, 0x4DB450, { .reversed = false });
|
||||
RH_ScopedInstall(PlayBulletHitCollisionSound, 0x4DB7C0, { .reversed = false });
|
||||
RH_ScopedInstall(ReportCollision, 0x4DBA10, { .reversed = false });
|
||||
RH_ScopedInstall(UpdateLoopingCollisionSound, 0x4DA540);
|
||||
RH_ScopedInstall(GetCollisionSoundStatus, 0x4DA830);
|
||||
RH_ScopedInstall(ReportObjectDestruction, 0x4DAB60);
|
||||
RH_ScopedInstall(PlayOneShotCollisionSound, 0x4DB150);
|
||||
RH_ScopedInstall(PlayLoopingCollisionSound, 0x4DB450);
|
||||
RH_ScopedInstall(PlayBulletHitCollisionSound, 0x4DB7C0);
|
||||
RH_ScopedInstall(ReportCollision, 0x4DBA10);
|
||||
RH_ScopedInstall(ReportBulletHit, 0x4DBDF0);
|
||||
RH_ScopedInstall(Service, 0x4DA2C0);
|
||||
|
||||
RH_ScopedOverloadedInstall(ReportWaterSplash, "at-position", 0x4DA190, void(CAECollisionAudioEntity::*)(CVector, float), { .reversed = true });
|
||||
RH_ScopedOverloadedInstall(ReportWaterSplash, "for-physical", 0x4DAE40, void(CAECollisionAudioEntity::*)(CPhysical*, float, bool), { .reversed = false });
|
||||
RH_ScopedInstall(ChooseCollisionSoundID, 0x4DAA50);
|
||||
RH_ScopedOverloadedInstall(ReportWaterSplash, "at-position", 0x4DA190, void(CAECollisionAudioEntity::*)(CVector, float));
|
||||
RH_ScopedOverloadedInstall(ReportWaterSplash, "for-physical", 0x4DAE40, void(CAECollisionAudioEntity::*)(CPhysical*, float, bool));
|
||||
}
|
||||
|
||||
// 0x5B9BD0
|
||||
|
|
@ -41,173 +44,297 @@ void CAECollisionAudioEntity::InitialisePostLoading() {
|
|||
|
||||
// 0x4DA320
|
||||
void CAECollisionAudioEntity::Reset() {
|
||||
for (auto& entry : m_Entries) {
|
||||
if (entry.m_nStatus != COL_AUDIO_ENTRY_STATUS_2)
|
||||
for (auto& entry : m_CollisionSoundList) {
|
||||
if (entry.Status != COLLISION_SOUND_LOOPING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.m_Sound)
|
||||
entry.m_Sound->StopSoundAndForget();
|
||||
if (entry.Sound) {
|
||||
entry.Sound->StopSoundAndForget();
|
||||
}
|
||||
|
||||
entry = {};
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DAAC0
|
||||
void CAECollisionAudioEntity::AddCollisionSoundToList(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, CAESound* sound,
|
||||
eCollisionAudioEntryStatus status)
|
||||
{
|
||||
void CAECollisionAudioEntity::AddCollisionSoundToList(
|
||||
CEntity* entityA,
|
||||
CEntity* entityB,
|
||||
eSurfaceType surfaceA,
|
||||
eSurfaceType surfaceB,
|
||||
CAESound* sound,
|
||||
eCollisionSoundStatus status
|
||||
) {
|
||||
// Find an entry with no sound.
|
||||
const auto newEntry = rng::find_if(m_Entries, [](tCollisionAudioEntry& entry) {
|
||||
return !entry.m_Sound;
|
||||
});
|
||||
|
||||
if (newEntry == m_Entries.end()) {
|
||||
const auto e = rng::find_if_not(m_CollisionSoundList, &tCollisionSound::Sound);
|
||||
if (e != m_CollisionSoundList.end()) {
|
||||
NOTSA_LOG_WARN("Collision sound list is full");
|
||||
return;
|
||||
}
|
||||
|
||||
// ? check
|
||||
newEntry->m_Entity1 = entity1;
|
||||
newEntry->m_Entity2 = entity2;
|
||||
newEntry->m_nSurface1 = surf1;
|
||||
newEntry->m_nSurface2 = surf2;
|
||||
newEntry->m_Sound = sound;
|
||||
newEntry->m_nStatus = status;
|
||||
newEntry->m_nTime = status == COL_AUDIO_ENTRY_STATUS_2 ? CTimer::GetTimeInMS() + 100 : 0;
|
||||
++m_nActiveCollisionSounds;
|
||||
e->EntityA = entityA;
|
||||
e->EntityB = entityB;
|
||||
|
||||
e->SurfaceA = surfaceA;
|
||||
e->SurfaceB = surfaceB;
|
||||
|
||||
e->Sound = sound;
|
||||
e->Status = status;
|
||||
e->LoopStopTimeMs = status == COLLISION_SOUND_LOOPING
|
||||
? CTimer::GetTimeInMS() + 100
|
||||
: 0;
|
||||
|
||||
m_NumActiveCollisionSounds++;
|
||||
}
|
||||
|
||||
// 0x4DA830
|
||||
void CAECollisionAudioEntity::GetCollisionSoundStatus(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, int32& outIndex) {
|
||||
const auto foundEntry = rng::find_if(m_Entries, [entity1, entity2](tCollisionAudioEntry& entry) {
|
||||
return entry.m_Entity1 == entity1 && entry.m_Entity2 == entity2
|
||||
|| entry.m_Entity1 == entity2 && entry.m_Entity2 == entity1;
|
||||
});
|
||||
|
||||
// 300 is possibly unintended.
|
||||
outIndex = foundEntry != m_Entries.end() ? std::distance(m_Entries.begin(), foundEntry) : 300;
|
||||
eCollisionSoundStatus CAECollisionAudioEntity::GetCollisionSoundStatus(CEntity* entityA, CEntity* entityB, eSurfaceType surfaceA, eSurfaceType surfaceB, int32& outIndex) {
|
||||
auto status = COLLISION_SOUND_INACTIVE;
|
||||
for (auto&& [i, v] : rngv::enumerate(m_CollisionSoundList)) {
|
||||
if (v.EntityA == entityA && v.EntityB == entityB || v.EntityA == entityB && v.EntityB == entityA) {
|
||||
if ((status = v.Status) == COLLISION_SOUND_LOOPING) {
|
||||
outIndex = (int32)(i);
|
||||
return v.Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
outIndex = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
// 0x4DB150
|
||||
void CAECollisionAudioEntity::PlayOneShotCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, CVector& posn) {
|
||||
plugin::CallMethod<0x4DB150, CAECollisionAudioEntity*, CEntity*, CEntity*, uint8, uint8, float, CVector&>(this, entity1, entity2, surf1, surf2, a5, posn);
|
||||
void CAECollisionAudioEntity::PlayOneShotCollisionSound(CEntity* entityA, CEntity* entityB, eSurfaceType surfaceA, eSurfaceType surfaceB, float impulseMagnitude, const CVector& posn) {
|
||||
const auto ProcessSound = [&](CEntity* eA, CEntity* eB, eSurfaceType sA, eSurfaceType sB) {
|
||||
if (sB >= SURFACE_NUM_TYPES_FOR_COLLISION) {
|
||||
return false;
|
||||
}
|
||||
if (sB == SURFACE_UNKNOWN_192 && sA != SURFACE_UNKNOWN_192) {
|
||||
return false;
|
||||
}
|
||||
const auto isMissionScriptSurface = notsa::contains({ SURFACE_UNKNOWN_192, SURFACE_UNKNOWN_193, SURFACE_UNKNOWN_194 }, sB); // 0x4DB23E
|
||||
const auto slot = isMissionScriptSurface
|
||||
? SND_BANK_SLOT_MISSION4
|
||||
: SND_BANK_SLOT_COLLISIONS;
|
||||
if (isMissionScriptSurface) {
|
||||
const auto bank = notsa::find_value(notsa::make_mapping<eSurfaceType, eSoundBank>({
|
||||
{ SURFACE_UNKNOWN_192, SND_BANK_SCRIPT_POOL_MINIGAME },
|
||||
{ SURFACE_UNKNOWN_193, SND_BANK_SCRIPT_BASKETBALL },
|
||||
{ SURFACE_UNKNOWN_194, SND_BANK_SCRIPT_GYM },
|
||||
}), sB);
|
||||
if (!AEAudioHardware.IsSoundBankLoaded(bank, slot)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto soundID = ChooseCollisionSoundID(sB); // 0x4DB280
|
||||
if (soundID == -1) {
|
||||
return true;
|
||||
}
|
||||
auto offset = ((float)(gCollisionLookup[sA].ParamD) * impulseMagnitude) / 100.f;
|
||||
if (sB == SURFACE_UNKNOWN_188 && sA == SURFACE_PED) { // 0x4DB2A6
|
||||
offset *= 10.f;
|
||||
}
|
||||
offset *= 500.f;
|
||||
const auto startAt = 100 - std::min(100, (int32)(std::floor(offset))); // 0x4DB2FB
|
||||
if (startAt >= 100) {
|
||||
return true;
|
||||
}
|
||||
const auto PlaySound = [&](float volume, int32 startAt) {
|
||||
volume += GetDefaultVolume(AE_GENERAL_COLLISION) - 3.f;
|
||||
if (volume <= -100.f) { // 0x4DB37E
|
||||
return;
|
||||
}
|
||||
auto* const sound = AESoundManager.PlaySound({
|
||||
.BankSlotID = slot,
|
||||
.SoundID = soundID,
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = volume,
|
||||
.RollOffFactor = 2.f,
|
||||
.Flags = SOUND_ROLLED_OFF | SOUND_START_PERCENTAGE | SOUND_REQUEST_UPDATES,
|
||||
.FrequencyVariance = 0.02f
|
||||
});
|
||||
m_CollisionSoundIDHistory[sB] = soundID;
|
||||
if (sound) { // 0x4DB3E7
|
||||
AddCollisionSoundToList(entityA, entityB, sA, sB, sound, COLLISION_SOUND_ONE_SHOT);
|
||||
}
|
||||
};
|
||||
const auto maxStartAt = gCollisionLookup[sB].MaxStartAt;
|
||||
if (startAt > maxStartAt) { // 0x4DB32D
|
||||
PlaySound(CAEAudioUtility::AudioLog10((float)(100 - startAt) / (float)(100 - maxStartAt)) * 20.f, maxStartAt);
|
||||
} else {
|
||||
PlaySound(0.f, startAt);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!ProcessSound(entityA, entityB, surfaceA, surfaceB)) {
|
||||
return;
|
||||
}
|
||||
if (!ProcessSound(entityB, entityA, surfaceB, surfaceA)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// based on `PlayLoopingCollisionSound` and `UpdateLoopingCollisionSound`
|
||||
std::pair<float, float> CAECollisionAudioEntity::GetLoopingCollisionSoundVolumeAndSpeed(CEntity* entityA, CEntity* entityB, eSurfaceType surfaceA, eSurfaceType surfaceB, bool isForceLooping) {
|
||||
const auto CalculateVolumeAtSpeed = [&](float speed) {
|
||||
return GetDefaultVolume(AE_GENERAL_COLLISION) + CAEAudioUtility::AudioLog10(std::min(speed / 0.75f, 1.f) * 20.f);
|
||||
};
|
||||
const auto GetVolumeAndSpeedForPhysical = [&](CVector velocity, CVector turn) -> std::pair<float, float> {
|
||||
const auto mag = std::sqrt(std::max(turn.SquaredMagnitude(), velocity.SquaredMagnitude()));
|
||||
if (mag == 0.f) {
|
||||
return { -100.f, 0.f };
|
||||
} else {
|
||||
auto speed = std::min(std::sqrt(mag * ((gCollisionLookup[surfaceA].ParamD * gCollisionLookup[surfaceB].ParamD) / 10000.f)) * 3.f, 0.3f);
|
||||
if (isForceLooping) {
|
||||
speed /= 6.f;
|
||||
}
|
||||
return { CalculateVolumeAtSpeed(speed), speed };
|
||||
}
|
||||
};
|
||||
|
||||
if (surfaceA == SURFACE_CAR && surfaceB == SURFACE_CAR) {
|
||||
const auto vehA = entityA->AsVehicle(),
|
||||
vehB = entityB->AsVehicle();
|
||||
return GetVolumeAndSpeedForPhysical(vehA->GetMoveSpeed() - vehB->GetMoveSpeed(), vehA->GetTurnSpeed() - vehB->GetTurnSpeed());
|
||||
}
|
||||
|
||||
if ((surfaceB != SURFACE_PED && surfaceA == SURFACE_CAR) || (surfaceA != SURFACE_PED && surfaceB == SURFACE_CAR)) {
|
||||
assert(entityA->GetIsTypePhysical());
|
||||
|
||||
const auto* const physicalA = entityA->AsPhysical();
|
||||
return GetVolumeAndSpeedForPhysical(physicalA->GetMoveSpeed(), physicalA->GetTurnSpeed());
|
||||
}
|
||||
|
||||
return { -100.f, 0.f };
|
||||
}
|
||||
|
||||
// 0x4DB450
|
||||
void CAECollisionAudioEntity::PlayLoopingCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, CVector& posn, uint8 a7) {
|
||||
plugin::CallMethod<0x4DB450, CAECollisionAudioEntity*, CEntity*, CEntity*, uint8, uint8, float, CVector&, uint8>(this, entity1, entity2, surf1, surf2, a5, posn, a7);
|
||||
void CAECollisionAudioEntity::PlayLoopingCollisionSound(CEntity* entityA, CEntity* entityB, eSurfaceType surfaceA, eSurfaceType surfaceB, float force, const CVector& pos, bool isForceLooping) {
|
||||
const auto [volume, speed] = GetLoopingCollisionSoundVolumeAndSpeed(entityA, entityB, surfaceA, surfaceB, isForceLooping);
|
||||
const auto GetSoundID = [&]() -> eSoundID { // 0x4DB6AF
|
||||
if (g_surfaceInfos.IsAudioGrass(surfaceA) || g_surfaceInfos.IsAudioGrass(surfaceB)) {
|
||||
return 0;
|
||||
} else if (g_surfaceInfos.IsAudioWater(surfaceA) || g_surfaceInfos.IsAudioWater(surfaceB)) {
|
||||
return 3;
|
||||
} else if (g_surfaceInfos.IsAudioMetal(surfaceA) || g_surfaceInfos.IsAudioMetal(surfaceB)) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
if (auto* const sound = AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = GetSoundID(),
|
||||
.AudioEntity = this,
|
||||
.Pos = pos,
|
||||
.Volume = volume,
|
||||
.RollOffFactor = 2.f,
|
||||
.Speed = std::max(speed, 0.75f) * 0.8f,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
})) {
|
||||
AddCollisionSoundToList(entityA, entityB, surfaceA, surfaceB, sound, COLLISION_SOUND_LOOPING);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DA540
|
||||
void CAECollisionAudioEntity::UpdateLoopingCollisionSound() {
|
||||
return plugin::CallMethod<0x4DA540, CAECollisionAudioEntity*>(this);
|
||||
void CAECollisionAudioEntity::UpdateLoopingCollisionSound(
|
||||
CAESound* sound,
|
||||
CEntity* entityA,
|
||||
CEntity* entityB,
|
||||
eSurfaceType surfaceA,
|
||||
eSurfaceType surfaceB,
|
||||
float impulseForce,
|
||||
const CVector& pos,
|
||||
bool isForceLooping
|
||||
) {
|
||||
const auto [volume, speed] = GetLoopingCollisionSoundVolumeAndSpeed(entityA, entityB, surfaceA, surfaceB, isForceLooping);
|
||||
sound->SetSpeed(notsa::step_to(std::max(0.75f, speed), sound->GetSpeed(), 0.1f));
|
||||
sound->SetVolume(notsa::step_to(volume, sound->GetVolume(), 1.0f));
|
||||
sound->SetPosition(pos);
|
||||
}
|
||||
|
||||
// 0x4DB7C0
|
||||
void CAECollisionAudioEntity::PlayBulletHitCollisionSound(eSurfaceType surface, const CVector& posn, float angleWithColPointNorm) {
|
||||
if (surface >= NUM_FUCKING_SURFACES)
|
||||
if (surface >= SURFACE_NUM_TYPES_FOR_COLLISION) {
|
||||
return;
|
||||
|
||||
int32 iRand;
|
||||
float maxDistance = 1.5f;
|
||||
float volume = GetDefaultVolume(AE_BULLET_HIT);
|
||||
if (surface == SURFACE_PED)
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(7, 9);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
}
|
||||
else if (g_surfaceInfos.IsAudioWater(surface))
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(16, 18);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
maxDistance = 2.0f;
|
||||
volume = volume + 6.0f;
|
||||
}
|
||||
else if (g_surfaceInfos.IsAudioWood(surface))
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(19, 21);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
}
|
||||
else if (g_surfaceInfos.IsAudioMetal(surface))
|
||||
{
|
||||
float probability = (90.0f - angleWithColPointNorm) / 180.0f; // see BoneNode_c::EulerToQuat
|
||||
if (CAEAudioUtility::ResolveProbability(probability))
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(10, 12);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
const auto PlayRandomSound = [&](int32 minID, int32 maxID, float volumeOffset = 0.f, float rollOff = 1.5f) { // 0x4DB9BF
|
||||
//! Find a new random sound ID that is not the same as the last one.
|
||||
const auto GetNewRandomSoundID = [&]{
|
||||
while (true) {
|
||||
const auto id = CAEAudioUtility::GetRandomNumberInRange(minID, maxID);
|
||||
if (id != m_LastBulletHitSoundID) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
};
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_BULLET_HITS,
|
||||
.SoundID = m_LastBulletHitSoundID = GetNewRandomSoundID(),
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = GetDefaultVolume(AE_BULLET_HIT) + volumeOffset,
|
||||
.RollOffFactor = rollOff,
|
||||
.FrequencyVariance = 0.02f
|
||||
});
|
||||
};
|
||||
if (surface == SURFACE_PED) {
|
||||
PlayRandomSound(7, 9);
|
||||
} else if (g_surfaceInfos.IsAudioWater(surface)) {
|
||||
PlayRandomSound(16, 18, 6.f, 2.f);
|
||||
} else if (g_surfaceInfos.IsAudioWood(surface)) {
|
||||
PlayRandomSound(19, 21);
|
||||
} else if (g_surfaceInfos.IsAudioMetal(surface)) {
|
||||
if (CAEAudioUtility::ResolveProbability((90.0f - angleWithColPointNorm) / 180.0f)) { // see BoneNode_c::EulerToQuat
|
||||
PlayRandomSound(10, 12);
|
||||
} else {
|
||||
PlayRandomSound(4, 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(4, 6);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
}
|
||||
} else if (g_surfaceInfos.IsAudioGravelConcreteOrTile(surface))
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(13, 15);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
iRand = CAEAudioUtility::GetRandomNumberInRange(1, 3);
|
||||
while (iRand == m_nLastBulletHitSoundID);
|
||||
}
|
||||
|
||||
if (iRand >= 0) {
|
||||
CAESound sound;
|
||||
sound.Initialise(SND_BANK_SLOT_BULLET_HITS, iRand, this, posn, volume, maxDistance, 1.0f, 1.0f, 0, SOUND_DEFAULT, 0.02f, 0);
|
||||
AESoundManager.RequestNewSound(&sound);
|
||||
m_nLastBulletHitSoundID = iRand;
|
||||
} else if (g_surfaceInfos.IsAudioGravelConcreteOrTile(surface)) {
|
||||
PlayRandomSound(13, 15);
|
||||
} else {
|
||||
PlayRandomSound(1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DA070
|
||||
void CAECollisionAudioEntity::ReportGlassCollisionEvent(eAudioEvents glassSoundType, Const CVector& posn, uint32 time) {
|
||||
auto speed = 1.0f;
|
||||
const auto sfxId = [glassSoundType, &speed] {
|
||||
switch (glassSoundType) {
|
||||
case AE_GLASS_HIT:
|
||||
return 51;
|
||||
case AE_GLASS_CRACK:
|
||||
return 68;
|
||||
case AE_GLASS_BREAK_SLOW:
|
||||
speed = 0.75f;
|
||||
return 56;
|
||||
case AE_GLASS_BREAK_FAST:
|
||||
return 56;
|
||||
case AE_GLASS_HIT_GROUND:
|
||||
return CAEAudioUtility::GetRandomNumberInRange(15, 18);
|
||||
case AE_GLASS_HIT_GROUND_SLOW:
|
||||
speed = 0.56f;
|
||||
return CAEAudioUtility::GetRandomNumberInRange(15, 18);
|
||||
default:
|
||||
return -1; // Invalid audio event
|
||||
void CAECollisionAudioEntity::ReportGlassCollisionEvent(eAudioEvents event, const CVector& posn, uint32 time) {
|
||||
const auto PlayCollisionSound = [&](eSoundID soundID, float speed) {
|
||||
if (time) {
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = soundID,
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = GetDefaultVolume(event),
|
||||
.RollOffFactor = 1.5f,
|
||||
.Speed = 0.f,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
.EventID = event,
|
||||
.ClientVariable = (float)(time + CTimer::GetTimeInMS()),
|
||||
});
|
||||
} else {
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = soundID,
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = GetDefaultVolume(event),
|
||||
.RollOffFactor = 1.5f,
|
||||
.Speed = speed,
|
||||
});
|
||||
}
|
||||
}();
|
||||
|
||||
if (sfxId == -1)
|
||||
return;
|
||||
|
||||
m_tempSound.Initialise(
|
||||
SND_BANK_SLOT_COLLISIONS,
|
||||
sfxId,
|
||||
this,
|
||||
posn,
|
||||
GetDefaultVolume(glassSoundType),
|
||||
1.5f,
|
||||
speed
|
||||
);
|
||||
|
||||
if (time) {
|
||||
auto& snd = m_tempSound;
|
||||
snd.m_ClientVariable = (float)(time + CTimer::GetTimeInMS());
|
||||
snd.m_Event = glassSoundType;
|
||||
snd.m_RequestUpdates = true;
|
||||
};
|
||||
switch (event) {
|
||||
case AE_GLASS_HIT: PlayCollisionSound(51, 1.f); break;
|
||||
case AE_GLASS_CRACK: PlayCollisionSound(68, 1.f); break;
|
||||
case AE_GLASS_BREAK_SLOW: PlayCollisionSound(56, 0.75f); break;
|
||||
case AE_GLASS_BREAK_FAST: PlayCollisionSound(56, 1.f); break;
|
||||
case AE_GLASS_HIT_GROUND: PlayCollisionSound(CAEAudioUtility::GetRandomNumberInRange(15, 18), 1.f); break;
|
||||
case AE_GLASS_HIT_GROUND_SLOW: PlayCollisionSound(CAEAudioUtility::GetRandomNumberInRange(15, 18), 0.56f); break;
|
||||
default: NOTSA_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,73 +343,293 @@ void CAECollisionAudioEntity::ReportWaterSplash(CVector posn, float volume) {
|
|||
if (!AEAudioHardware.EnsureSoundBankIsLoaded(SND_BANK_GENRL_COLLISIONS, SND_BANK_SLOT_COLLISIONS, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_tempSound.Initialise(
|
||||
SND_BANK_SLOT_COLLISIONS,
|
||||
67,
|
||||
this,
|
||||
posn,
|
||||
GetDefaultVolume(AE_WATER_SPLASH) + volume,
|
||||
2.5f,
|
||||
1.26f,
|
||||
1.0f,
|
||||
0u,
|
||||
SOUND_REQUEST_UPDATES
|
||||
);
|
||||
m_tempSound.m_Event = AE_FRONTEND_SELECT;
|
||||
AESoundManager.RequestNewSound(&m_tempSound);
|
||||
|
||||
m_tempSound.Initialise(
|
||||
SND_BANK_SLOT_COLLISIONS,
|
||||
66,
|
||||
this,
|
||||
posn,
|
||||
GetDefaultVolume(AE_WATER_SPLASH) + volume,
|
||||
2.5f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0u,
|
||||
SOUND_REQUEST_UPDATES
|
||||
);
|
||||
m_tempSound.m_Event = AE_FRONTEND_BACK;
|
||||
m_tempSound.m_ClientVariable = static_cast<float>(CTimer::GetTimeInMS() + 166);
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = 67,
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = GetDefaultVolume(AE_WATER_SPLASH) + volume,
|
||||
.RollOffFactor = 2.5f,
|
||||
.Speed = 1.26f,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
.EventID = 1
|
||||
});
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = 66,
|
||||
.AudioEntity = this,
|
||||
.Pos = posn,
|
||||
.Volume = GetDefaultVolume(AE_WATER_SPLASH) + volume,
|
||||
.RollOffFactor = 2.5f,
|
||||
.Speed = 0.f,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
.EventID = 2,
|
||||
.ClientVariable = (float)(CTimer::GetTimeInMS() + 166)
|
||||
});
|
||||
}
|
||||
|
||||
// 0x4DAE40
|
||||
void CAECollisionAudioEntity::ReportWaterSplash(CPhysical* physical, float height, bool splashMoreThanOnce) {
|
||||
return plugin::CallMethod<0x4DAE40, CAECollisionAudioEntity*, CPhysical*, float, bool>(this, physical, height, splashMoreThanOnce);
|
||||
void CAECollisionAudioEntity::ReportWaterSplash(CPhysical* physical, float volume, bool isForceSplash) {
|
||||
assert(physical->GetIsTypePhysical());
|
||||
|
||||
if (!isForceSplash) {
|
||||
if (physical->GetMoveSpeed().z > -0.1f && volume == -100.f) {
|
||||
return;
|
||||
}
|
||||
if (rng::any_of(std::to_array({ 1, 2, 3 }), [&](int32 event) {
|
||||
return AESoundManager.AreSoundsOfThisEventPlayingForThisEntityAndPhysical(event, this, physical) != 0;
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!AEAudioHardware.EnsureSoundBankIsLoaded(SND_BANK_GENRL_COLLISIONS, SND_BANK_SLOT_COLLISIONS, true)) { // 0x4DAED2
|
||||
return;
|
||||
}
|
||||
const auto PlaySound = [&](int32 eventID, float speed, float volumeOffset, uint32 interval) {
|
||||
if (volume <= -100.f) { // 0x4DAF90
|
||||
volume = std::max(CAEAudioUtility::AudioLog10(std::min(0.6f, physical->GetMoveSpeed().Magnitude())) * 20.f, -18.f);
|
||||
}
|
||||
volume += GetDefaultVolume(AE_WATER_SPLASH) + volumeOffset;
|
||||
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = 67,
|
||||
.AudioEntity = this,
|
||||
.Pos = physical->GetPosition(),
|
||||
.Volume = volume,
|
||||
.RollOffFactor = 2.5f,
|
||||
.Speed = speed,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
.EventID = 1,
|
||||
});
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = 66,
|
||||
.AudioEntity = this,
|
||||
.Pos = physical->GetPosition(),
|
||||
.Volume = volume,
|
||||
.RollOffFactor = 2.5f,
|
||||
.Speed = speed,
|
||||
.Flags = SOUND_REQUEST_UPDATES | SOUND_LIFESPAN_TIED_TO_PHYSICAL_ENTITY,
|
||||
.RegisterWithEntity = physical,
|
||||
.EventID = eventID,
|
||||
.ClientVariable = (float)(CTimer::GetTimeInMS() + interval),
|
||||
});
|
||||
};
|
||||
if (physical->GetIsTypePed()) { // 0x4DAF2B - Inverted
|
||||
if (isForceSplash && !physical->AsPed()->GetIntelligence()->GetTaskSwim()) {
|
||||
PlaySound(2, 1.26f, -6.f, 166);
|
||||
}
|
||||
} else if (physical->GetIsTypeVehicle()) { // 0x4DAF52
|
||||
PlaySound(3, 0.94f, 0.f, 248);
|
||||
} else { // 0x4DAF70
|
||||
PlaySound(2, 1.26f, -12.f, 166);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DAB60
|
||||
void CAECollisionAudioEntity::ReportObjectDestruction(CEntity* entity) {
|
||||
return plugin::CallMethod<0x4DAB60, CAECollisionAudioEntity*, CEntity*>(this, entity);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity->GetModelId() == ModelIndices::MI_GRASSHOUSE) { // 0x4DAB94 - Moved up here
|
||||
AESoundManager.PlaySound({ // 0x4DABEF
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = (eSoundID)(56),
|
||||
.AudioEntity = this,
|
||||
.Pos = entity->GetPosition(),
|
||||
.Volume = GetDefaultVolume(AE_GLASS_BREAK_SLOW),
|
||||
.RollOffFactor = 1.5f,
|
||||
.Speed = 0.75f,
|
||||
});
|
||||
AESoundManager.PlaySound({ // 0x4DAC65
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = (eSoundID)(CAEAudioUtility::GetRandomNumberInRange(15, 18)),
|
||||
.AudioEntity = this,
|
||||
.Pos = entity->GetPosition(),
|
||||
.Volume = GetDefaultVolume(AE_GLASS_HIT_GROUND_SLOW),
|
||||
.RollOffFactor = 1.5f,
|
||||
.Speed = 0.0f,
|
||||
.Flags = SOUND_REQUEST_UPDATES,
|
||||
.EventID = AE_GLASS_HIT_GROUND_SLOW,
|
||||
.ClientVariable = (float)(CTimer::GetTimeInMS() + 600),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PlaySoundForSurface = [&](eSurfaceType surface) {
|
||||
const auto PlaySound = [&](eSoundID soundID) {
|
||||
AESoundManager.PlaySound({
|
||||
.BankSlotID = SND_BANK_SLOT_COLLISIONS,
|
||||
.SoundID = soundID,
|
||||
.AudioEntity = this,
|
||||
.Pos = entity->GetPosition(),
|
||||
.Volume = GetDefaultVolume(AE_GENERAL_COLLISION),
|
||||
.RollOffFactor = 2.f,
|
||||
.FrequencyVariance = 0.0588f,
|
||||
});
|
||||
};
|
||||
switch (surface) {
|
||||
case SURFACE_WOOD_CRATES:
|
||||
case SURFACE_WOOD_BENCH:
|
||||
case SURFACE_WOOD_PICKET_FENCE:
|
||||
case SURFACE_WOOD_SLATTED_FENCE:
|
||||
case SURFACE_WOOD_RANCH_FENCE: PlaySound(CAEAudioUtility::GetRandomNumberInRange(41, 44)); break; // 0x4DAD14
|
||||
case SURFACE_GLASS: PlaySound(11); break; // 0x4DAD43
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
if (entity->GetModelId() == MODEL_MOLOTOV) { // 0x4DAB7B
|
||||
PlaySoundForSurface(SURFACE_GLASS);
|
||||
} else if (const auto cd = entity->GetColData()) { // 0x4DAB89
|
||||
if (const auto spheres = cd->GetSpheres(); !spheres.empty()) {
|
||||
PlaySoundForSurface(spheres.front().GetSurfaceType());
|
||||
} else if (const auto boxes = cd->GetBoxes(); !boxes.empty()) {
|
||||
PlaySoundForSurface(boxes.front().GetSurfaceType());
|
||||
} else if (const auto tris = cd->GetTris(); !tris.empty()) {
|
||||
PlaySoundForSurface(tris.front().GetSurfaceType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DBA10
|
||||
void CAECollisionAudioEntity::ReportCollision(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, const CVector& pos, const CVector* normal, float collisionImpact1, float collisionImpact2, bool bOnlyPlayOneShotCollisionSound, bool unknown) {
|
||||
return plugin::CallMethod<0x4DBA10, CAECollisionAudioEntity*, CEntity*, CEntity*, uint8, uint8, const CVector&, const CVector*, float, float, bool, bool>(this, entity1, entity2, surf1, surf2, pos, normal, collisionImpact1, collisionImpact2, bOnlyPlayOneShotCollisionSound, unknown);
|
||||
void CAECollisionAudioEntity::ReportCollision(
|
||||
CEntity* entityA,
|
||||
CEntity* entityB,
|
||||
eSurfaceType surfaceA,
|
||||
eSurfaceType surfaceB,
|
||||
const CVector& pos,
|
||||
const CVector* normal,
|
||||
float impulseForce,
|
||||
float relVelSq,
|
||||
bool isForceOneShot,
|
||||
bool isForceLooping
|
||||
) {
|
||||
if (!AEAudioHardware.IsSoundBankLoaded(SND_BANK_GENRL_COLLISIONS, SND_BANK_SLOT_COLLISIONS)) {
|
||||
return;
|
||||
}
|
||||
if (!CanAddNewSound()) {
|
||||
return;
|
||||
}
|
||||
if (relVelSq <= 0.f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto GetSurfaceToUse = [&](
|
||||
CEntity* eA, eSurfaceType& sA,
|
||||
CEntity* eB, eSurfaceType& sB
|
||||
) {
|
||||
if (!eA->GetIsTypeVehicle()) {
|
||||
switch (eA->GetModelId()) {
|
||||
case MODEL_MOLOTOV:
|
||||
return SURFACE_GLASS;
|
||||
case MODEL_SATCHEL:
|
||||
return SURFACE_UNKNOWN_190;
|
||||
case MODEL_GRENADE:
|
||||
case MODEL_BOMB:
|
||||
case MODEL_TEARGAS:
|
||||
return SURFACE_UNKNOWN_191;
|
||||
}
|
||||
const auto eAModelId = eA->GetModelId();
|
||||
if (eAModelId == ModelIndices::MI_BASKETBALL) {
|
||||
return SURFACE_UNKNOWN_193;
|
||||
}
|
||||
if (eAModelId == ModelIndices::MI_PUNCHBAG) {
|
||||
return SURFACE_UNKNOWN_194;
|
||||
}
|
||||
if (eAModelId == ModelIndices::MI_GRASSHOUSE) {
|
||||
return SURFACE_UNBREAKABLE_GLASS;
|
||||
}
|
||||
if (eAModelId == ModelIndices::MI_IMY_GRAY_CRATE) {
|
||||
return SURFACE_WOOD_SOLID;
|
||||
}
|
||||
if (eA->GetIsTypePhysical() && eA->AsPhysical()->physicalFlags.bMakeMassTwiceAsBig) {
|
||||
return SURFACE_UNKNOWN_192;
|
||||
}
|
||||
return sA;
|
||||
}
|
||||
if (eB && eB->GetIsTypeBuilding() && normal && eA->GetUp().Dot(*normal) > 0.6f) {
|
||||
if (eA->AsVehicle()->IsSubBMX()) {
|
||||
return SURFACE_UNKNOWN_188;
|
||||
}
|
||||
if (g_surfaceInfos.GetFrictionEffect(sB) != FRICTION_EFFECT_SPARKS) {
|
||||
return SURFACE_RUBBER;
|
||||
}
|
||||
}
|
||||
if (eA->AsVehicle()->IsSubBMX()) {
|
||||
return SURFACE_UNKNOWN_188;
|
||||
}
|
||||
return SURFACE_CAR;
|
||||
};
|
||||
|
||||
surfaceA = GetSurfaceToUse(entityA, surfaceA, entityB, surfaceB);
|
||||
surfaceB = GetSurfaceToUse(entityB, surfaceB, entityA, surfaceA);
|
||||
|
||||
if (isForceOneShot) { // 0x4DBC68
|
||||
PlayOneShotCollisionSound(entityA, entityB, surfaceA, surfaceB, impulseForce, pos);
|
||||
} else { // 0x4DBC83
|
||||
int32 entryID;
|
||||
switch (const auto soundStatus = CAECollisionAudioEntity::GetCollisionSoundStatus(entityA, entityB, surfaceA, surfaceB, entryID)) {
|
||||
case COLLISION_SOUND_INACTIVE: { // 0x4DBD1C, 0x4DBD5A
|
||||
if (isForceLooping) {
|
||||
return PlayLoopingCollisionSound(entityA, entityB, surfaceA, surfaceB, impulseForce, pos, isForceLooping);
|
||||
}
|
||||
return PlayOneShotCollisionSound(entityA, entityB, surfaceA, surfaceB, impulseForce, pos);
|
||||
}
|
||||
case COLLISION_SOUND_ONE_SHOT: // 0x4DBD1C, 0x4DBDE0
|
||||
return PlayLoopingCollisionSound(entityA, entityB, surfaceA, surfaceB, impulseForce, pos, isForceLooping);
|
||||
case COLLISION_SOUND_LOOPING: { // 0x4DBCA3, 0x4DBD44
|
||||
const auto e = &m_CollisionSoundList[entryID];
|
||||
e->LoopStopTimeMs = CTimer::GetTimeInMS() + 100;
|
||||
if (e->Sound) {
|
||||
UpdateLoopingCollisionSound(e->Sound, e->EntityA, e->EntityB, e->SurfaceA, e->SurfaceB, impulseForce, pos, isForceLooping);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NOTSA_UNREACHABLE("Invalid soundStatus: {}", (int32)(soundStatus));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DBDF0
|
||||
void CAECollisionAudioEntity::ReportBulletHit(CEntity* entity, eSurfaceType surface, const CVector& posn, float angleWithColPointNorm) {
|
||||
if (AEAudioHardware.IsSoundBankLoaded(SND_BANK_GENRL_BULLET_HITS, SND_BANK_SLOT_BULLET_HITS)) {
|
||||
if (entity && entity->GetIsTypeVehicle()) {
|
||||
surface = entity->AsVehicle()->IsSubBMX()
|
||||
? (eSurfaceType)(188) // todo: C* Surface
|
||||
: SURFACE_CAR;
|
||||
}
|
||||
PlayBulletHitCollisionSound(surface, posn, angleWithColPointNorm);
|
||||
if (!AEAudioHardware.IsSoundBankLoaded(SND_BANK_GENRL_BULLET_HITS, SND_BANK_SLOT_BULLET_HITS)) {
|
||||
return;
|
||||
}
|
||||
if (entity && entity->GetIsTypeVehicle()) {
|
||||
surface = entity->AsVehicle()->IsSubBMX()
|
||||
? (eSurfaceType)(188) // todo: C* Surface
|
||||
: SURFACE_CAR;
|
||||
}
|
||||
PlayBulletHitCollisionSound(surface, posn, angleWithColPointNorm);
|
||||
}
|
||||
|
||||
// 0x4DA2C0
|
||||
void CAECollisionAudioEntity::Service() {
|
||||
const auto time = CTimer::GetTimeInMS();
|
||||
for (auto& entry : m_Entries) {
|
||||
if (entry.m_nStatus != COL_AUDIO_ENTRY_STATUS_2 || time < entry.m_nTime)
|
||||
for (auto& entry : m_CollisionSoundList) {
|
||||
if (entry.Status != COLLISION_SOUND_LOOPING || time < entry.LoopStopTimeMs)
|
||||
continue;
|
||||
|
||||
entry = {};
|
||||
--m_nActiveCollisionSounds;
|
||||
--m_NumActiveCollisionSounds;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4DAA50
|
||||
eSoundID CAECollisionAudioEntity::ChooseCollisionSoundID(eSurfaceType surface) {
|
||||
const auto* const l = &gCollisionLookup[surface];
|
||||
if (l->MinSoundID == l->MaxSoundID) {
|
||||
return l->MinSoundID;
|
||||
}
|
||||
for (auto retry = 0; retry < 100; retry++) { // NOTSA: Handle very unlikely infinite loop
|
||||
const auto soundID = CAEAudioUtility::GetRandomNumberInRange(l->MinSoundID, l->MaxSoundID);
|
||||
if (soundID != m_CollisionSoundIDHistory[surface]) {
|
||||
return soundID;
|
||||
}
|
||||
}
|
||||
NOTSA_LOG_WARN("Failed to generate collision sound ID not in history!"); // NOTSA
|
||||
return l->MinSoundID; // NOTSA
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,33 +2,31 @@
|
|||
|
||||
#include "AEAudioEntity.h"
|
||||
|
||||
enum eCollisionAudioEntryStatus : uint8 {
|
||||
COL_AUDIO_ENTRY_STATUS_0,
|
||||
COL_AUDIO_ENTRY_STATUS_2 = 2,
|
||||
enum eCollisionSoundStatus : uint8 {
|
||||
COLLISION_SOUND_INACTIVE,
|
||||
COLLISION_SOUND_ONE_SHOT,
|
||||
COLLISION_SOUND_LOOPING,
|
||||
};
|
||||
|
||||
struct tCollisionAudioEntry {
|
||||
CEntity* m_Entity1{nullptr};
|
||||
CEntity* m_Entity2{nullptr};
|
||||
CAESound* m_Sound{nullptr};
|
||||
uint32 m_nTime{0};
|
||||
eCollisionAudioEntryStatus m_nStatus{COL_AUDIO_ENTRY_STATUS_0};
|
||||
eSurfaceType m_nSurface1{NUM_FUCKING_SURFACES + 1}; // ?
|
||||
eSurfaceType m_nSurface2{NUM_FUCKING_SURFACES + 1}; // ?
|
||||
|
||||
tCollisionAudioEntry() = default;
|
||||
struct tCollisionSound {
|
||||
CEntity* EntityA{ nullptr };
|
||||
CEntity* EntityB{ nullptr };
|
||||
CAESound* Sound{ nullptr };
|
||||
uint32 LoopStopTimeMs{ 0 };
|
||||
eCollisionSoundStatus Status{ COLLISION_SOUND_INACTIVE };
|
||||
eSurfaceType SurfaceA{ SURFACE_NUM_TYPES_FOR_COLLISION }; // ?
|
||||
eSurfaceType SurfaceB{ SURFACE_NUM_TYPES_FOR_COLLISION }; // ?
|
||||
};
|
||||
VALIDATE_SIZE(tCollisionAudioEntry, 0x14);
|
||||
VALIDATE_SIZE(tCollisionSound, 0x14);
|
||||
|
||||
class NOTSA_EXPORT_VTABLE CAECollisionAudioEntity : public CAEAudioEntity {
|
||||
public:
|
||||
static constexpr auto NUM_ENTRIES = 300u;
|
||||
|
||||
int16 m_aHistory[NUM_FUCKING_SURFACES]{255};
|
||||
int16 m_nLastBulletHitSoundID{-1};
|
||||
int16 m_nRandom{-1};
|
||||
int32 m_nActiveCollisionSounds{0};
|
||||
std::array<tCollisionAudioEntry, NUM_ENTRIES> m_Entries{};
|
||||
int16 m_CollisionSoundIDHistory[SURFACE_NUM_TYPES_FOR_COLLISION]{ 255 };
|
||||
int16 m_LastBulletHitSoundID{ -1 };
|
||||
int32 m_NumActiveCollisionSounds{ 0 };
|
||||
std::array<tCollisionSound, NUM_ENTRIES> m_CollisionSoundList{};
|
||||
|
||||
public:
|
||||
static void InjectHooks();
|
||||
|
|
@ -37,24 +35,51 @@ public:
|
|||
static void InitialisePostLoading();
|
||||
void Reset();
|
||||
|
||||
void AddCollisionSoundToList(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, CAESound* sound, eCollisionAudioEntryStatus status);
|
||||
void AddCollisionSoundToList(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, CAESound* sound, eCollisionSoundStatus status);
|
||||
|
||||
void GetCollisionSoundStatus(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, int32& outIndex);
|
||||
eCollisionSoundStatus GetCollisionSoundStatus(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, int32& outIndex);
|
||||
|
||||
void PlayLoopingCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, CVector& posn, uint8 a7);
|
||||
void UpdateLoopingCollisionSound();
|
||||
void PlayLoopingCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, const CVector& posn, bool isForceLooping);
|
||||
void UpdateLoopingCollisionSound(
|
||||
CAESound* pSound,
|
||||
CEntity* entityA,
|
||||
CEntity* entityB,
|
||||
eSurfaceType surfA,
|
||||
eSurfaceType surfB,
|
||||
float impulseForce,
|
||||
const CVector& position,
|
||||
bool isForceLooping
|
||||
);
|
||||
|
||||
void PlayOneShotCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, CVector& posn);
|
||||
std::pair<float, float> GetLoopingCollisionSoundVolumeAndSpeed(CEntity* entityA, CEntity* entityB, eSurfaceType surfA, eSurfaceType surfB, bool isForceLooping); // notsa
|
||||
void PlayOneShotCollisionSound(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, float a5, const CVector& posn);
|
||||
void PlayBulletHitCollisionSound(eSurfaceType surface, const CVector& posn, float angleWithColPointNorm);
|
||||
|
||||
void ReportGlassCollisionEvent(eAudioEvents glassSoundType, Const CVector& posn, uint32 time);
|
||||
void ReportGlassCollisionEvent(eAudioEvents glassSoundType, const CVector& posn, uint32 time);
|
||||
void ReportWaterSplash(CVector posn, float volume);
|
||||
void ReportWaterSplash(CPhysical* physical, float height, bool splashMoreThanOnce);
|
||||
void ReportObjectDestruction(CEntity* entity);
|
||||
void ReportCollision(CEntity* entity1, CEntity* entity2, eSurfaceType surf1, eSurfaceType surf2, const CVector& pos, const CVector* normal, float collisionImpact1, float collisionImpact2, bool bOnlyPlayOneShotCollisionSound, bool unknown);
|
||||
void ReportCollision(
|
||||
CEntity* entityA,
|
||||
CEntity* entityB,
|
||||
eSurfaceType surfA,
|
||||
eSurfaceType surfB,
|
||||
const CVector& pos,
|
||||
const CVector* normal,
|
||||
float impulseForce,
|
||||
float relVelSq,
|
||||
bool isForceOneShot,
|
||||
bool isForceLooping
|
||||
);
|
||||
void ReportBulletHit(CEntity* entity, eSurfaceType surface, const CVector& posn, float angleWithColPointNorm);
|
||||
|
||||
void Service();
|
||||
|
||||
// notsa
|
||||
bool CanAddNewSound() const { return m_NumActiveCollisionSounds <= m_CollisionSoundList.size(); }
|
||||
|
||||
protected:
|
||||
eSoundID ChooseCollisionSoundID(eSurfaceType surface);
|
||||
};
|
||||
|
||||
VALIDATE_SIZE(CAECollisionAudioEntity, 0x1978);
|
||||
|
|
|
|||
|
|
@ -226,12 +226,12 @@ public:
|
|||
bool CheckCollision();
|
||||
bool CheckCollision_SimpleCar();
|
||||
|
||||
void SetMoveSpeedXY(CVector2D v) { m_vecMoveSpeed = CVector{v.x, v.y, m_vecMoveSpeed.z}; }
|
||||
CVector& GetMoveSpeed() { return m_vecMoveSpeed; }
|
||||
void SetVelocity(CVector velocity) { m_vecMoveSpeed = velocity; } // 0x441130
|
||||
void ResetMoveSpeed() { SetVelocity(CVector{}); }
|
||||
void SetMoveSpeedXY(CVector2D v) { m_vecMoveSpeed = CVector{v.x, v.y, m_vecMoveSpeed.z}; }
|
||||
auto& GetMoveSpeed(this auto&& self) { return self.m_vecMoveSpeed; }
|
||||
void SetVelocity(CVector velocity) { m_vecMoveSpeed = velocity; } // 0x441130
|
||||
void ResetMoveSpeed() { SetVelocity(CVector{}); }
|
||||
|
||||
CVector& GetTurnSpeed() { return m_vecTurnSpeed; }
|
||||
auto& GetTurnSpeed(this auto&& self) { return self.m_vecTurnSpeed; }
|
||||
void ResetTurnSpeed() { m_vecTurnSpeed = CVector(); }
|
||||
|
||||
void ResetFrictionMoveSpeed() { m_vecFrictionMoveSpeed = CVector(); }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
enum eSurfaceType : uint8 {
|
||||
SURFACE_NONE = 0xFF, // NOTSA
|
||||
SURFACE_DEFAULT = 0,
|
||||
SURFACE_TARMAC = 1,
|
||||
SURFACE_TARMAC_FUCKED = 2,
|
||||
|
|
@ -188,5 +189,13 @@ enum eSurfaceType : uint8 {
|
|||
SURFACE_RAILTRACK = 178,
|
||||
|
||||
TOTAL_NUM_SURFACE_TYPES,
|
||||
NUM_FUCKING_SURFACES = 194
|
||||
|
||||
SURFACE_UNKNOWN_188 = 188, // BMX collision surface?
|
||||
SURFACE_UNKNOWN_190 = 190,
|
||||
SURFACE_UNKNOWN_191 = 191,
|
||||
SURFACE_UNKNOWN_192 = 192,
|
||||
SURFACE_UNKNOWN_193 = 193, // Used for basketball collision surface
|
||||
SURFACE_UNKNOWN_194 = 194, // Used for punchbag collision surface
|
||||
|
||||
SURFACE_NUM_TYPES_FOR_COLLISION = TOTAL_NUM_SURFACE_TYPES + 16,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue