From f811601c355bca0ebe898fe3cdce5df7d1bbdc00 Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Tue, 1 Jan 2019 22:33:11 +0100 Subject: [PATCH 01/12] engineSet/GetModelTime function --- Client/game_sa/CModelInfoSA.cpp | 32 ++++++++ Client/game_sa/CModelInfoSA.h | 2 + .../logic/luadefs/CLuaEngineDefs.cpp | 75 +++++++++++++++++++ .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 2 + Client/sdk/game/CModelInfo.h | 2 + Client/sdk/game/RenderWare.h | 7 ++ 6 files changed, 120 insertions(+) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index d1aac7a35d2..51f76e560a3 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -564,6 +564,38 @@ float CModelInfoSA::GetLODDistance() return 0.0f; } +bool CModelInfoSA::SetTime(char hour1, char hour2) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (time) + { + time->m_nTimeOn = hour1; + time->m_nTimeOff = hour2; + return true; + } + } + return false; +} + +bool CModelInfoSA::GetTime(char& hour1, char& hour2) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (time) + { + hour1 = time->m_nTimeOn; + hour2 = time->m_nTimeOff; + return true; + } + } + return false; +} + void CModelInfoSA::SetLODDistance(float fDistance) { #if 0 diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 32b9ff39bc7..5bd118d9592 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -326,6 +326,8 @@ class CModelInfoSA : public CModelInfo void RestreamIPL(void); static void StaticFlushPendingRestreamIPL(void); static void StaticSetHooks(void); + bool GetTime(char& hour1, char& hour2); + bool SetTime(char hour1, char hour2); void SetAlphaTransparencyEnabled(BOOL bEnabled); bool IsAlphaTransparencyEnabled(); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index d23582d8ff0..32217a56207 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -34,6 +34,8 @@ void CLuaEngineDefs::LoadFunctions(void) {"engineGetModelIDFromName", EngineGetModelIDFromName}, {"engineGetModelTextureNames", EngineGetModelTextureNames}, {"engineGetVisibleTextureNames", EngineGetVisibleTextureNames}, + {"engineSetModelTime", EngineSetModelTime }, + {"engineGetModelTime", EngineGetModelTime }, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -58,12 +60,14 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "restoreModel", "engineRestoreModel"); lua_classfunction(luaVM, "setAsynchronousLoading", "engineSetAsynchronousLoading"); lua_classfunction(luaVM, "setModelLODDistance", "engineSetModelLODDistance"); + lua_classfunction(luaVM, "setModelTime", "engineSetModelTime"); lua_classfunction(luaVM, "getVisibleTextureNames", "engineGetVisibleTextureNames"); lua_classfunction(luaVM, "getModelLODDistance", "engineGetModelLODDistance"); lua_classfunction(luaVM, "getModelTextureNames", "engineGetModelTextureNames"); lua_classfunction(luaVM, "getModelIDFromName", "engineGetModelIDFromName"); lua_classfunction(luaVM, "getModelNameFromID", "engineGetModelNameFromID"); + lua_classfunction(luaVM, "getModelTime", "engineGetModelTime"); // lua_classvariable ( luaVM, "modelLODDistance", "engineSetModelLODDistance", "engineGetModelLODDistance" ); .modelLODDistance[model] = distance // lua_classvariable ( luaVM, "modelNameFromID", NULL, "engineGetModelNameFromID" ); .modelNameFromID[id] = "name" @@ -913,3 +917,74 @@ int CLuaEngineDefs::EngineGetVisibleTextureNames(lua_State* luaVM) lua_pushboolean(luaVM, false); return 1; } + +int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) +{ + // bool engineSetModelTime ( int/string modelID, int hourOn, int hourOff ) + SString strModelId; + char hour[2]; + CScriptArgReader argStream(luaVM); + argStream.ReadString(strModelId); + argStream.ReadNumber(hour[0]); + argStream.ReadNumber(hour[1]); + + if (!argStream.HasErrors()) + { + ushort usModelID = CModelNames::ResolveModelID(strModelId); + CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModelID); + if (pModelInfo) + { + if (hour[0] > hour[1]) + std::swap(hour[0], hour[1]); + + if (hour[0] >= 0 && hour[0] <= 23 && hour[1] >= 0 && hour[1] <= 23) + { + lua_pushboolean(luaVM, pModelInfo->SetTime(hour[0], hour[1])); + return 1; + } + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaEngineDefs::EngineGetModelTime(lua_State* luaVM) +{ + // int, int engineSetModelTime ( int/string modelID ) + SString strModelId; + + CScriptArgReader argStream(luaVM); + argStream.ReadString(strModelId); + + if (!argStream.HasErrors()) + { + ushort usModelID = CModelNames::ResolveModelID(strModelId); + CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModelID); + if (pModelInfo) + { + char hour[2]; + if (pModelInfo->GetTime(hour[0], hour[1])) + { + lua_pushnumber(luaVM, hour[0]); + lua_pushnumber(luaVM, hour[1]); + return 2; + } + else // Model is incompatible, don't let confuse user. + { + lua_pushnumber(luaVM, 0); + lua_pushnumber(luaVM, 0); + return 2; + } + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 4e99d482ee4..1dd0988f65f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -44,6 +44,8 @@ class CLuaEngineDefs : public CLuaDefs LUA_DECLARE(EngineGetModelIDFromName); LUA_DECLARE(EngineGetModelTextureNames); LUA_DECLARE(EngineGetVisibleTextureNames); + LUA_DECLARE(EngineSetModelTime); + LUA_DECLARE(EngineGetModelTime); private: static void AddEngineColClass(lua_State* luaVM); diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 9096e07eb0a..e79411069c8 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -127,6 +127,8 @@ class CModelInfo virtual float GetLODDistance() = 0; virtual void SetLODDistance(float fDistance) = 0; virtual void RestreamIPL() = 0; + virtual bool GetTime(char& hour1, char& hour2) = 0; + virtual bool SetTime(char hour1, char hour2) = 0; virtual void ModelAddRef(EModelRequestType requestType, const char* szTag /* = NULL*/) = 0; virtual void RemoveRef(bool bRemoveExtraGTARef = false) = 0; diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index 8a0e73e3910..9e13e817711 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -424,3 +424,10 @@ struct RwError { int err1, err2; }; + +struct TimeInfo { + TimeInfo(char timeOn, char timeOff, short OtherTimeModel) : m_nTimeOn(timeOn), m_nTimeOff(timeOff), m_wOtherTimeModel(OtherTimeModel) {}; + char m_nTimeOn; + char m_nTimeOff; + short m_wOtherTimeModel; +}; From 917069ac869113463621a90fb303c815eb249412 Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Tue, 1 Jan 2019 22:39:16 +0100 Subject: [PATCH 02/12] renamed variables --- Client/game_sa/CModelInfoSA.cpp | 12 +++++----- Client/game_sa/CModelInfoSA.h | 4 ++-- .../logic/luadefs/CLuaEngineDefs.cpp | 22 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 51f76e560a3..c2288567b01 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -564,7 +564,7 @@ float CModelInfoSA::GetLODDistance() return 0.0f; } -bool CModelInfoSA::SetTime(char hour1, char hour2) +bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) { m_pInterface = ppModelInfo[m_dwModelID]; if (m_pInterface) @@ -572,15 +572,15 @@ bool CModelInfoSA::SetTime(char hour1, char hour2) TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); if (time) { - time->m_nTimeOn = hour1; - time->m_nTimeOff = hour2; + time->m_nTimeOn = cHourOn; + time->m_nTimeOff = cHourOff; return true; } } return false; } -bool CModelInfoSA::GetTime(char& hour1, char& hour2) +bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) { m_pInterface = ppModelInfo[m_dwModelID]; if (m_pInterface) @@ -588,8 +588,8 @@ bool CModelInfoSA::GetTime(char& hour1, char& hour2) TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); if (time) { - hour1 = time->m_nTimeOn; - hour2 = time->m_nTimeOff; + cHourOn = time->m_nTimeOn; + cHourOff = time->m_nTimeOff; return true; } } diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 5bd118d9592..9ceef1b0c70 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -326,8 +326,8 @@ class CModelInfoSA : public CModelInfo void RestreamIPL(void); static void StaticFlushPendingRestreamIPL(void); static void StaticSetHooks(void); - bool GetTime(char& hour1, char& hour2); - bool SetTime(char hour1, char hour2); + bool GetTime(char& cHourOn, char& cHourOff); + bool SetTime(char cHourOn, char cHourOff); void SetAlphaTransparencyEnabled(BOOL bEnabled); bool IsAlphaTransparencyEnabled(); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 32217a56207..3eaeb9f1565 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -922,11 +922,11 @@ int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) { // bool engineSetModelTime ( int/string modelID, int hourOn, int hourOff ) SString strModelId; - char hour[2]; + char cHourOn,cHourOff; CScriptArgReader argStream(luaVM); argStream.ReadString(strModelId); - argStream.ReadNumber(hour[0]); - argStream.ReadNumber(hour[1]); + argStream.ReadNumber(cHourOn); + argStream.ReadNumber(cHourOff); if (!argStream.HasErrors()) { @@ -934,12 +934,12 @@ int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModelID); if (pModelInfo) { - if (hour[0] > hour[1]) - std::swap(hour[0], hour[1]); + if (cHourOn > cHourOff) + std::swap(cHourOn, cHourOff); - if (hour[0] >= 0 && hour[0] <= 23 && hour[1] >= 0 && hour[1] <= 23) + if (cHourOn >= 0 && cHourOn <= 23 && cHourOff >= 0 && cHourOff <= 23) { - lua_pushboolean(luaVM, pModelInfo->SetTime(hour[0], hour[1])); + lua_pushboolean(luaVM, pModelInfo->SetTime(cHourOn, cHourOff)); return 1; } } @@ -966,11 +966,11 @@ int CLuaEngineDefs::EngineGetModelTime(lua_State* luaVM) CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModelID); if (pModelInfo) { - char hour[2]; - if (pModelInfo->GetTime(hour[0], hour[1])) + char cHourOn, cHourOff; + if (pModelInfo->GetTime(cHourOn, cHourOff)) { - lua_pushnumber(luaVM, hour[0]); - lua_pushnumber(luaVM, hour[1]); + lua_pushnumber(luaVM, cHourOn); + lua_pushnumber(luaVM, cHourOff); return 2; } else // Model is incompatible, don't let confuse user. From b4a324da553261dac0fb726c624190e97023e1a0 Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Tue, 1 Jan 2019 22:41:01 +0100 Subject: [PATCH 03/12] renamed missed variables --- Client/sdk/game/CModelInfo.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index e79411069c8..87d53754360 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -127,8 +127,8 @@ class CModelInfo virtual float GetLODDistance() = 0; virtual void SetLODDistance(float fDistance) = 0; virtual void RestreamIPL() = 0; - virtual bool GetTime(char& hour1, char& hour2) = 0; - virtual bool SetTime(char hour1, char hour2) = 0; + virtual bool GetTime(char& hourOn, char& hourOff) = 0; + virtual bool SetTime(char hourOn, char hourOff) = 0; virtual void ModelAddRef(EModelRequestType requestType, const char* szTag /* = NULL*/) = 0; virtual void RemoveRef(bool bRemoveExtraGTARef = false) = 0; From 655460dd1742c1746f45439567113ace7ca17f2e Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Tue, 1 Jan 2019 22:43:01 +0100 Subject: [PATCH 04/12] removed constructor and 2 spaces --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 4 ++-- Client/sdk/game/RenderWare.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 3eaeb9f1565..dd7c7ab6e90 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -34,8 +34,8 @@ void CLuaEngineDefs::LoadFunctions(void) {"engineGetModelIDFromName", EngineGetModelIDFromName}, {"engineGetModelTextureNames", EngineGetModelTextureNames}, {"engineGetVisibleTextureNames", EngineGetVisibleTextureNames}, - {"engineSetModelTime", EngineSetModelTime }, - {"engineGetModelTime", EngineGetModelTime }, + {"engineSetModelTime", EngineSetModelTime}, + {"engineGetModelTime", EngineGetModelTime}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index 9e13e817711..4852e066fe3 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -426,7 +426,6 @@ struct RwError }; struct TimeInfo { - TimeInfo(char timeOn, char timeOff, short OtherTimeModel) : m_nTimeOn(timeOn), m_nTimeOff(timeOff), m_wOtherTimeModel(OtherTimeModel) {}; char m_nTimeOn; char m_nTimeOff; short m_wOtherTimeModel; From d29b0aa2ed48d50fc26d0caa9c16e7a6e810dbcf Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Tue, 1 Jan 2019 23:54:44 +0100 Subject: [PATCH 05/12] Missed reset function --- Client/game_sa/CGameSA.cpp | 5 ++++ Client/game_sa/CGameSA.h | 1 + Client/game_sa/CModelInfoSA.cpp | 25 ++++++++++++++++---- Client/game_sa/CModelInfoSA.h | 2 ++ Client/mods/deathmatch/logic/CClientGame.cpp | 1 + Client/sdk/game/CGame.h | 1 + Client/sdk/game/RenderWare.h | 1 + 7 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 8137b5a9512..f2016a39768 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -829,6 +829,11 @@ void CGameSA::ResetModelLodDistances(void) CModelInfoSA::StaticResetLodDistances(); } +void CGameSA::ResetModelTimes(void) +{ + CModelInfoSA::StaticResetModelTimes(); +} + void CGameSA::ResetAlphaTransparencies(void) { CModelInfoSA::StaticResetAlphaTransparencies(); diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 6a57b3b2ac2..907515f8151 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -413,6 +413,7 @@ class CGameSA : public CGame void FlushPendingRestreamIPL(void); void ResetModelLodDistances(void); void ResetAlphaTransparencies(void); + void ResetModelTimes(void); void DisableVSync(void); void OnPedContextChange(CPed* pPedContext); diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index c2288567b01..36d5301ab30 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -20,6 +20,7 @@ std::map std::map CModelInfoSA::ms_ModelDefaultLodDistanceMap; std::map CModelInfoSA::ms_ModelDefaultAlphaTransparencyMap; std::unordered_map> CModelInfoSA::ms_ModelDefaultDummiesPosition; +std::map CModelInfoSA::ms_ModelDefaultModelTimeInfo; CModelInfoSA::CModelInfoSA(void) { @@ -569,11 +570,15 @@ bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) m_pInterface = ppModelInfo[m_dwModelID]; if (m_pInterface) { - TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (time) + TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (pTime) { - time->m_nTimeOn = cHourOn; - time->m_nTimeOff = cHourOff; + if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) + { + MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); + } + pTime->m_nTimeOn = cHourOn; + pTime->m_nTimeOff = cHourOff; return true; } } @@ -596,6 +601,18 @@ bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) return false; } +void CModelInfoSA::StaticResetModelTimes() +{ + // Restore default values + for (std::map::const_iterator iter = ms_ModelDefaultModelTimeInfo.begin(); iter != ms_ModelDefaultModelTimeInfo.end(); ++iter) + { + iter->first->m_nTimeOn = iter->second->m_nTimeOn; + iter->first->m_nTimeOff = iter->second->m_nTimeOff; + } + + ms_ModelDefaultModelTimeInfo.clear(); +} + void CModelInfoSA::SetLODDistance(float fDistance) { #if 0 diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 9ceef1b0c70..94a72567ccb 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -277,6 +277,7 @@ class CModelInfoSA : public CModelInfo static std::map ms_ModelDefaultLodDistanceMap; static std::map ms_ModelDefaultAlphaTransparencyMap; static std::unordered_map> ms_ModelDefaultDummiesPosition; + static std::map ms_ModelDefaultModelTimeInfo; bool m_bAddedRefForCollision; SVehicleSupportedUpgrades m_ModelSupportedUpgrades; @@ -328,6 +329,7 @@ class CModelInfoSA : public CModelInfo static void StaticSetHooks(void); bool GetTime(char& cHourOn, char& cHourOff); bool SetTime(char cHourOn, char cHourOff); + static void StaticResetModelTimes(void); void SetAlphaTransparencyEnabled(BOOL bEnabled); bool IsAlphaTransparencyEnabled(); diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 13ee1a0d871..873b92ca0c3 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -3541,6 +3541,7 @@ void CClientGame::Event_OnIngame(void) g_pGame->ResetModelLodDistances(); g_pGame->ResetAlphaTransparencies(); + g_pGame->ResetModelTimes(); // Make sure we can access all areas g_pGame->GetStats()->ModifyStat(CITIES_PASSED, 2.0); diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index c1e2c5adfd6..6e5d10a83f4 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -237,6 +237,7 @@ class __declspec(novtable) CGame virtual void FlushPendingRestreamIPL(void) = 0; virtual void ResetModelLodDistances(void) = 0; virtual void ResetAlphaTransparencies(void) = 0; + virtual void ResetModelTimes(void) = 0; virtual void DisableVSync(void) = 0; virtual void OnPedContextChange(CPed* pPedContext) = 0; diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index 4852e066fe3..9e13e817711 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -426,6 +426,7 @@ struct RwError }; struct TimeInfo { + TimeInfo(char timeOn, char timeOff, short OtherTimeModel) : m_nTimeOn(timeOn), m_nTimeOff(timeOff), m_wOtherTimeModel(OtherTimeModel) {}; char m_nTimeOn; char m_nTimeOff; short m_wOtherTimeModel; From 906e93ebb671ac35de05fcd9ca2257ab38efba7c Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Wed, 9 Jan 2019 09:16:15 +0100 Subject: [PATCH 06/12] changed name, updated hours --- .../logic/luadefs/CLuaEngineDefs.cpp | 18 +++++++++--------- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index dd7c7ab6e90..6d60cc829f7 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -34,8 +34,8 @@ void CLuaEngineDefs::LoadFunctions(void) {"engineGetModelIDFromName", EngineGetModelIDFromName}, {"engineGetModelTextureNames", EngineGetModelTextureNames}, {"engineGetVisibleTextureNames", EngineGetVisibleTextureNames}, - {"engineSetModelTime", EngineSetModelTime}, - {"engineGetModelTime", EngineGetModelTime}, + {"engineSetModelVisibleTime", EngineSetModelVisibleTime }, + {"engineGetModelVisibleTime", EngineGetModelVisibleTime }, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -60,14 +60,14 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "restoreModel", "engineRestoreModel"); lua_classfunction(luaVM, "setAsynchronousLoading", "engineSetAsynchronousLoading"); lua_classfunction(luaVM, "setModelLODDistance", "engineSetModelLODDistance"); - lua_classfunction(luaVM, "setModelTime", "engineSetModelTime"); + lua_classfunction(luaVM, "setModelVisibleTime", "engineSetModelVisibleTime"); lua_classfunction(luaVM, "getVisibleTextureNames", "engineGetVisibleTextureNames"); lua_classfunction(luaVM, "getModelLODDistance", "engineGetModelLODDistance"); lua_classfunction(luaVM, "getModelTextureNames", "engineGetModelTextureNames"); lua_classfunction(luaVM, "getModelIDFromName", "engineGetModelIDFromName"); lua_classfunction(luaVM, "getModelNameFromID", "engineGetModelNameFromID"); - lua_classfunction(luaVM, "getModelTime", "engineGetModelTime"); + lua_classfunction(luaVM, "getModelVisibleTime", "engineGetModelVisibleTime"); // lua_classvariable ( luaVM, "modelLODDistance", "engineSetModelLODDistance", "engineGetModelLODDistance" ); .modelLODDistance[model] = distance // lua_classvariable ( luaVM, "modelNameFromID", NULL, "engineGetModelNameFromID" ); .modelNameFromID[id] = "name" @@ -918,9 +918,9 @@ int CLuaEngineDefs::EngineGetVisibleTextureNames(lua_State* luaVM) return 1; } -int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) +int CLuaEngineDefs::EngineSetModelVisibleTime(lua_State* luaVM) { - // bool engineSetModelTime ( int/string modelID, int hourOn, int hourOff ) + // bool engineSetModelVisibleTime ( int/string modelID, int hourOn, int hourOff ) SString strModelId; char cHourOn,cHourOff; CScriptArgReader argStream(luaVM); @@ -937,7 +937,7 @@ int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) if (cHourOn > cHourOff) std::swap(cHourOn, cHourOff); - if (cHourOn >= 0 && cHourOn <= 23 && cHourOff >= 0 && cHourOff <= 23) + if (cHourOn >= 0 && cHourOn <= 24 && cHourOff >= 0 && cHourOff <= 24) { lua_pushboolean(luaVM, pModelInfo->SetTime(cHourOn, cHourOff)); return 1; @@ -952,9 +952,9 @@ int CLuaEngineDefs::EngineSetModelTime(lua_State* luaVM) return 1; } -int CLuaEngineDefs::EngineGetModelTime(lua_State* luaVM) +int CLuaEngineDefs::EngineGetModelVisibleTime(lua_State* luaVM) { - // int, int engineSetModelTime ( int/string modelID ) + // int, int engineGetModelVisibleTime ( int/string modelID ) SString strModelId; CScriptArgReader argStream(luaVM); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 1dd0988f65f..47f634a9a05 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -44,8 +44,8 @@ class CLuaEngineDefs : public CLuaDefs LUA_DECLARE(EngineGetModelIDFromName); LUA_DECLARE(EngineGetModelTextureNames); LUA_DECLARE(EngineGetVisibleTextureNames); - LUA_DECLARE(EngineSetModelTime); - LUA_DECLARE(EngineGetModelTime); + LUA_DECLARE(EngineSetModelVisibleTime); + LUA_DECLARE(EngineGetModelVisibleTime); private: static void AddEngineColClass(lua_State* luaVM); From caf28157c3c908a9bfd774fb46d1f9a6d901a283 Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Wed, 9 Jan 2019 09:26:18 +0100 Subject: [PATCH 07/12] changed incompatible models time from 0,0 to 0,24 --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 6d60cc829f7..9b64aba6f0d 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -976,7 +976,7 @@ int CLuaEngineDefs::EngineGetModelVisibleTime(lua_State* luaVM) else // Model is incompatible, don't let confuse user. { lua_pushnumber(luaVM, 0); - lua_pushnumber(luaVM, 0); + lua_pushnumber(luaVM, 24); return 2; } } From a4f511b9fd3d4f3c9d9cd9adead730cbb0b1c0ac Mon Sep 17 00:00:00 2001 From: CrosRoad95 Date: Mon, 14 Jan 2019 09:34:24 +0100 Subject: [PATCH 08/12] removed voids --- Client/game_sa/CGameSA.cpp | 2 +- Client/game_sa/CGameSA.h | 2 +- Client/game_sa/CModelInfoSA.h | 2 +- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 4 ++-- Client/sdk/game/CGame.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index fe7e7bcc899..f8f5e0090d0 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -829,7 +829,7 @@ void CGameSA::ResetModelLodDistances() CModelInfoSA::StaticResetLodDistances(); } -void CGameSA::ResetModelTimes(void) +void CGameSA::ResetModelTimes() { CModelInfoSA::StaticResetModelTimes(); } diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 8b5df5766e1..1e5e9df2725 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -414,7 +414,7 @@ class CGameSA : public CGame void ResetModelLodDistances(); void ResetAlphaTransparencies(); void DisableVSync(); - void ResetModelTimes(void); + void ResetModelTimes(); void OnPedContextChange(CPed* pPedContext); CPed* GetPedContext(); diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index de207c36a78..37fdcd4de1d 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -329,7 +329,7 @@ class CModelInfoSA : public CModelInfo static void StaticSetHooks(); bool GetTime(char& cHourOn, char& cHourOff); bool SetTime(char cHourOn, char cHourOff); - static void StaticResetModelTimes(void); + static void StaticResetModelTimes(); void SetAlphaTransparencyEnabled(BOOL bEnabled); bool IsAlphaTransparencyEnabled(); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index be51e6fe273..f0e6e1c42ba 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -34,8 +34,8 @@ void CLuaEngineDefs::LoadFunctions() {"engineGetModelIDFromName", EngineGetModelIDFromName}, {"engineGetModelTextureNames", EngineGetModelTextureNames}, {"engineGetVisibleTextureNames", EngineGetVisibleTextureNames}, - {"engineSetModelVisibleTime", EngineSetModelVisibleTime }, - {"engineGetModelVisibleTime", EngineGetModelVisibleTime }, + {"engineSetModelVisibleTime", EngineSetModelVisibleTime}, + {"engineGetModelVisibleTime", EngineGetModelVisibleTime}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 41875bea4ba..30140d175b5 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -238,7 +238,7 @@ class __declspec(novtable) CGame virtual void ResetModelLodDistances() = 0; virtual void ResetAlphaTransparencies() = 0; virtual void DisableVSync() = 0; - virtual void ResetModelTimes(void) = 0; + virtual void ResetModelTimes() = 0; virtual void OnPedContextChange(CPed* pPedContext) = 0; virtual CPed* GetPedContext() = 0; From 699c80c544fbdc9806cb5f3e0ffc6beaaed6b40f Mon Sep 17 00:00:00 2001 From: Qais Patankar Date: Tue, 14 Jan 2020 17:08:41 +0000 Subject: [PATCH 09/12] Use original line endings --- Client/game_sa/CModelInfoSA.cpp | 3094 +++++++++++++++---------------- Client/game_sa/CModelInfoSA.h | 770 ++++---- 2 files changed, 1932 insertions(+), 1932 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 2a9e8c54f0d..63350b18d96 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1,1547 +1,1547 @@ -/***************************************************************************** - * - * PROJECT: Multi Theft Auto v1.0 - * LICENSE: See LICENSE in the top level directory - * FILE: game_sa/CModelInfoSA.cpp - * PURPOSE: Entity model information handler - * - * Multi Theft Auto is available from http://www.multitheftauto.com/ - * - *****************************************************************************/ - -#include "StdInc.h" -#include "gamesa_renderware.h" - -extern CGameSA* pGame; - -CBaseModelInfoSAInterface** ppModelInfo = (CBaseModelInfoSAInterface**)ARRAY_ModelInfo; - -std::map CModelInfoSA::ms_RestreamTxdIDMap; -std::map CModelInfoSA::ms_ModelDefaultLodDistanceMap; -std::map CModelInfoSA::ms_ModelDefaultAlphaTransparencyMap; -std::unordered_map> CModelInfoSA::ms_ModelDefaultDummiesPosition; -std::map CModelInfoSA::ms_ModelDefaultModelTimeInfo; -std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; - -CModelInfoSA::CModelInfoSA() -{ - m_pInterface = NULL; - this->m_dwModelID = 0xFFFFFFFF; - m_dwReferences = 0; - m_dwPendingInterfaceRef = 0; - m_pOriginalColModelInterface = NULL; - m_pCustomClump = NULL; - m_pCustomColModel = NULL; - m_bAddedRefForCollision = false; -} - -CModelInfoSA::CModelInfoSA(DWORD dwModelID) -{ - this->m_dwModelID = dwModelID; - m_pInterface = ppModelInfo[m_dwModelID]; - m_dwReferences = 0; - m_dwPendingInterfaceRef = 0; - m_pOriginalColModelInterface = NULL; - m_pCustomClump = NULL; - m_pCustomColModel = NULL; - m_bAddedRefForCollision = false; -} - -CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() -{ - return m_pInterface = ppModelInfo[m_dwModelID]; -} - -BOOL CModelInfoSA::IsBoat() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBoat ( )"); - DWORD dwFunction = FUNC_IsBoatModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsCar() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsCar ( )"); - DWORD dwFunction = FUNC_IsCarModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsTrain() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsTrain ( )"); - DWORD dwFunction = FUNC_IsTrainModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsHeli() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsHeli ( )"); - DWORD dwFunction = FUNC_IsHeliModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsPlane() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsPlane ( )"); - DWORD dwFunction = FUNC_IsPlaneModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsBike() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBike ( )"); - DWORD dwFunction = FUNC_IsBikeModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsFakePlane() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsFakePlane ( )"); - DWORD dwFunction = FUNC_IsFakePlaneModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsMonsterTruck() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsMonsterTruck ( )"); - DWORD dwFunction = FUNC_IsMonsterTruckModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsQuadBike() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsQuadBike ( )"); - DWORD dwFunction = FUNC_IsQuadBikeModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsBmx() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBmx ( )"); - DWORD dwFunction = FUNC_IsBmxModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsTrailer() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsTrailer ( )"); - DWORD dwFunction = FUNC_IsTrailerModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsVehicle() -{ - /* - DEBUG_TRACE("BOOL CModelInfoSA::IsVehicle ( )"); - DWORD dwFunction = FUNC_IsVehicleModelType; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; - */ - - // Above doesn't seem to work - return m_dwModelID >= 400 && m_dwModelID <= 611; -} - -bool CModelInfoSA::IsPlayerModel() -{ - return (GetInterface() && GetInterface()->pColModel && GetInterface()->pColModel == (CColModelSAInterface*)VAR_CTempColModels_ModelPed1); -} - -BOOL CModelInfoSA::IsUpgrade() -{ - return m_dwModelID >= 1000 && m_dwModelID <= 1193; -} - -char* CModelInfoSA::GetNameIfVehicle() -{ - DEBUG_TRACE("char * CModelInfoSA::GetNameIfVehicle ( )"); - // if(this->IsVehicle()) - // { - DWORD dwModelInfo = ARRAY_ModelInfo; - DWORD dwFunc = FUNC_CText_Get; - DWORD ModelID = m_dwModelID; - DWORD dwReturn = 0; - - _asm - { - push eax - push ebx - push ecx - - mov ebx, ModelID - lea ebx, [ebx*4] - add ebx, ARRAY_ModelInfo - mov eax, [ebx] - add eax, 50 - - push eax - mov ecx, CLASS_CText - call dwFunc - - mov dwReturn, eax - - pop ecx - pop ebx - pop eax - } - return (char*)dwReturn; - // } - // return NULL; -} - -uint CModelInfoSA::GetAnimFileIndex() -{ - DWORD dwFunc = m_pInterface->VFTBL->GetAnimFileIndex; - DWORD dwThis = (DWORD)m_pInterface; - uint uiReturn = 0; - if (dwFunc) - { - _asm - { - mov ecx, dwThis - call dwFunc - mov uiReturn, eax - } - } - return uiReturn; -} - -VOID CModelInfoSA::Request(EModelRequestType requestType, const char* szTag) -{ - DEBUG_TRACE("VOID CModelInfoSA::Request( BOOL bAndLoad, BOOL bWaitForLoad )"); - // don't bother loading it if it already is - if (IsLoaded()) - return; - - if (m_dwModelID <= 288 && m_dwModelID != 7 && !pGame->GetModelInfo(7)->IsLoaded()) - { - // Skin 7 must be loaded in order for other skins to work. No, really. (#4010) - pGame->GetModelInfo(7)->Request(requestType, "Model 7"); - } - - // Bikes can sometimes get stuck when loading unless the anim file is handled like what is does here - // Don't change the code below unless you can test it (by recreating the problem it solves) - if (IsVehicle()) - { - uint uiAnimFileIndex = GetAnimFileIndex(); - if (uiAnimFileIndex != 0xffffffff) - { - uint uiAnimId = uiAnimFileIndex + 25575; - CModelInfoSA* pAnim = static_cast(pGame->GetModelInfo(uiAnimId)); - if (!pAnim) - { - if (uiAnimId != 25714) - LogEvent(505, "Model no anim", "", SString("%d (%d)", m_dwModelID, uiAnimId)); - } - else if (!pAnim->IsLoaded()) - { - OutputDebugLine(SString("[Models] Requesting anim file %d for model %d", uiAnimId, m_dwModelID)); - pAnim->Request(requestType, szTag); - } - } - } - - if (requestType == BLOCKING) - { - pGame->GetStreaming()->RequestModel(m_dwModelID, 0x16); - pGame->GetStreaming()->LoadAllRequestedModels(true, szTag); - if (!IsLoaded()) - { - // Try 3 more times, final time without high priority flag - int iCount = 0; - while (iCount++ < 10 && !IsLoaded()) - { - bool bOnlyPriorityModels = (iCount < 3 || iCount & 1); - pGame->GetStreaming()->LoadAllRequestedModels(bOnlyPriorityModels, szTag); - } - if (!IsLoaded()) - { - AddReportLog(6641, SString("Blocking load fail: %d (%s)", m_dwModelID, szTag)); - LogEvent(641, "Blocking load fail", "", SString("%d (%s)", m_dwModelID, szTag)); - dassert(0); - } - else - { - AddReportLog(6642, SString("Blocking load: %d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); - LogEvent(642, "Blocking load", "", SString("%d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); - } - } - } - else - { - pGame->GetStreaming()->RequestModel(m_dwModelID, 0x06); - } -} - -VOID CModelInfoSA::Remove() -{ - DEBUG_TRACE("VOID CModelInfoSA::Remove ( )"); - - // Don't remove if GTA refers to it somehow. - // Or we'll screw up SA's map for example. - - m_pInterface = ppModelInfo[m_dwModelID]; - - // Remove ref added for collision - if (m_bAddedRefForCollision) - { - m_bAddedRefForCollision = false; - if (m_pInterface->usNumberOfRefs > 0) - m_pInterface->usNumberOfRefs--; - } - - // Remove our reference - if (m_pInterface->usNumberOfRefs > 0) - m_pInterface->usNumberOfRefs--; - - // No references left? - if (m_pInterface->usNumberOfRefs == 0) - { - // We have a custom model? - if (m_pCustomClump) - { - // Mark us as unloaded. We manage the clump unloading. - // BYTE *ModelLoaded = (BYTE*)ARRAY_ModelLoaded; - // ModelLoaded[(m_dwModelID+m_dwModelID*4)<<2] = 0; - } - else - { - // Make our collision model original again before we unload. - RestoreColModel(); - - // Remove the model. - DWORD dwFunction = FUNC_RemoveModel; - DWORD ModelID = m_dwModelID; - _asm - { - push ModelID - call dwFunction - add esp, 4 - } - } - } -} - -BYTE CModelInfoSA::GetLevelFromPosition(CVector* vecPosition) -{ - DEBUG_TRACE("BYTE CModelInfoSA::GetLevelFromPosition ( CVector * vecPosition )"); - DWORD dwFunction = FUNC_GetLevelFromPosition; - BYTE bReturn = 0; - _asm - { - push vecPosition - call dwFunction - add esp, 4 - mov bReturn, al - } - return bReturn; -} - -BOOL CModelInfoSA::IsLoaded() -{ - if (DoIsLoaded()) - { - if (m_dwPendingInterfaceRef) - { - assert(m_dwReferences > 0); - m_pInterface = ppModelInfo[m_dwModelID]; - m_pInterface->usNumberOfRefs++; - m_dwPendingInterfaceRef = 0; - } - return true; - } - return false; -} - -BOOL CModelInfoSA::DoIsLoaded() -{ - DEBUG_TRACE("BOOL CModelInfoSA::DoIsLoaded ( )"); - - // return (BOOL)*(BYTE *)(ARRAY_ModelLoaded + 20*dwModelID); - BOOL bLoaded = pGame->GetStreaming()->HasModelLoaded(m_dwModelID); - - if (m_dwModelID < 20000) - { - m_pInterface = ppModelInfo[m_dwModelID]; - - if (bLoaded) - { - // Check rw object is there - if (!m_pInterface || !m_pInterface->pRwObject) - return false; - } - } - return bLoaded; -} - -BYTE CModelInfoSA::GetFlags() -{ - DWORD dwFunc = FUNC_GetModelFlags; - DWORD ModelID = m_dwModelID; - BYTE bFlags = 0; - _asm - { - push ModelID - call dwFunc - add esp, 4 - mov bFlags, al - } - return bFlags; -} - -CBoundingBox* CModelInfoSA::GetBoundingBox() -{ - DWORD dwFunc = FUNC_GetBoundingBox; - DWORD ModelID = m_dwModelID; - CBoundingBox* dwReturn = 0; - _asm - { - push ModelID - call dwFunc - add esp, 4 - mov dwReturn, eax - } - return dwReturn; -} - -bool CModelInfoSA::IsValid() -{ - if (m_dwModelID >= 20000 && m_dwModelID < MODELINFO_MAX) - return true; - return ppModelInfo[m_dwModelID] != 0; -} - -float CModelInfoSA::GetDistanceFromCentreOfMassToBaseOfModel() -{ - DWORD dwModelInfo = 0; - DWORD ModelID = m_dwModelID; - FLOAT fReturn = 0; - _asm - { - mov eax, ModelID - mov eax, ARRAY_ModelInfo[eax*4] - mov eax, [eax+20] - cmp eax, 0 - jz skip - fld [eax + 8] - fchs - fstp fReturn -skip: - } - return fReturn; -} - -unsigned short CModelInfoSA::GetTextureDictionaryID() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - return m_pInterface->usTextureDictionary; - - return 0; -} - -void CModelInfoSA::SetTextureDictionaryID(unsigned short usID) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - m_pInterface->usTextureDictionary = usID; -} - -float CModelInfoSA::GetLODDistance() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - return m_pInterface->fLodDistanceUnscaled; - - return 0.0f; -} - -bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (pTime) - { - if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) - { - MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); - } - pTime->m_nTimeOn = cHourOn; - pTime->m_nTimeOff = cHourOff; - return true; - } - } - return false; -} - -bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (time) - { - cHourOn = time->m_nTimeOn; - cHourOff = time->m_nTimeOff; - return true; - } - } - return false; -} - -void CModelInfoSA::StaticResetModelTimes() -{ - // Restore default values - for (std::map::const_iterator iter = ms_ModelDefaultModelTimeInfo.begin(); iter != ms_ModelDefaultModelTimeInfo.end(); ++iter) - { - iter->first->m_nTimeOn = iter->second->m_nTimeOn; - iter->first->m_nTimeOff = iter->second->m_nTimeOff; - } - - ms_ModelDefaultModelTimeInfo.clear(); -} - -void CModelInfoSA::SetLODDistance(float fDistance) -{ -#if 0 - // fLodDistanceUnscaled values: - // - // With the draw distance setting in GTA SP options menu set to maximum: - // 0 - 170 roughly correlates to a LOD distance of 0 - 300 game units - // 170 - 480 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in - // 490 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely - // - // With the draw distance setting in GTA SP options menu set to minimum: - // 0 - 325 roughly correlates to a LOD distance of 0 - 300 game units - // 340 - 960 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in - // 1000 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely - // - // So, to ensure the maximum draw distance with a working alpha fade-in, fLodDistanceUnscaled has to be - // no more than: 325 - (325-170) * draw_distance_setting - // - - // Change GTA draw distance value from 0.925 to 1.8 into 0 to 1 - float fDrawDistanceSetting = UnlerpClamped ( 0.925f, CSettingsSA ().GetDrawDistance (), 1.8f ); - - // Calc max setting allowed for fLodDistanceUnscaled to preserve alpha fade-in - float fMaximumValue = Lerp ( 325.f, fDrawDistanceSetting, 170.f ); - - // Ensure fDistance is in range - fDistance = std::min ( fDistance, fMaximumValue ); -#endif - // Limit to 325.f as it goes horrible after that - fDistance = std::min(fDistance, 325.f); - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // Save default value if not done yet - if (!MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) - MapSet(ms_ModelDefaultLodDistanceMap, m_dwModelID, m_pInterface->fLodDistanceUnscaled); - m_pInterface->fLodDistanceUnscaled = fDistance; - } -} - -void CModelInfoSA::StaticResetLodDistances() -{ - // Restore default values - for (std::map::const_iterator iter = ms_ModelDefaultLodDistanceMap.begin(); iter != ms_ModelDefaultLodDistanceMap.end(); ++iter) - { - CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; - if (pInterface) - pInterface->fLodDistanceUnscaled = iter->second; - } - - ms_ModelDefaultLodDistanceMap.clear(); -} - -void CModelInfoSA::RestreamIPL() -{ - // IPLs should not contain peds, weapons, vehicles and vehicle upgrades - if (m_dwModelID > 611 && (m_dwModelID < 1000 || m_dwModelID > 1193)) - MapSet(ms_RestreamTxdIDMap, GetTextureDictionaryID(), 0); -} - -void CModelInfoSA::StaticFlushPendingRestreamIPL() -{ - if (ms_RestreamTxdIDMap.empty()) - return; - // This function restreams all instances of the model *that are from the default SA world (ipl)*. - // In other words, it does not affect elements created by MTA. - // It's mostly a reimplementation of SA's DeleteAllRwObjects, except that it filters by model ID. - - ((void (*)())FUNC_FlushRequestList)(); - - std::set removedModels; - - for (int i = 0; i < 2 * NUM_StreamSectorRows * NUM_StreamSectorCols; i++) - { - DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamSectors)[i]; - while (pSectorEntry) - { - CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; - - // Possible bug - pEntity seems to be invalid here occasionally - if (pEntity->vtbl->DeleteRwObject != 0x00534030) - { - // Log info - OutputDebugString(SString("Entity 0x%08x (with model %d) at ARRAY_StreamSectors[%d,%d] is invalid\n", pEntity, pEntity->m_nModelIndex, - i / 2 % NUM_StreamSectorRows, i / 2 / NUM_StreamSectorCols)); - // Assert in debug - #if MTA_DEBUG - assert(pEntity->vtbl->DeleteRwObject == 0x00534030); - #endif - pSectorEntry = (DWORD*)pSectorEntry[1]; - continue; - } - - if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) - { - if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) - { - _asm - { - mov ecx, pEntity - mov eax, [ecx] - call dword ptr [eax+20h] - } - removedModels.insert(pEntity->m_nModelIndex); - } - } - - pSectorEntry = (DWORD*)pSectorEntry[1]; - } - } - - for (int i = 0; i < NUM_StreamRepeatSectorRows * NUM_StreamRepeatSectorCols; i++) - { - DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamRepeatSectors)[3 * i + 2]; - while (pSectorEntry) - { - CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; - if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) - { - if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) - { - _asm - { - mov ecx, pEntity - mov eax, [ecx] - call dword ptr [eax+20h] - } - removedModels.insert(pEntity->m_nModelIndex); - } - } - pSectorEntry = (DWORD*)pSectorEntry[1]; - } - } - - ms_RestreamTxdIDMap.clear(); - - std::set::iterator it; - for (it = removedModels.begin(); it != removedModels.end(); it++) - { - ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(*it); - MemPut(ARRAY_ModelLoaded + 20 * (*it), 0); - } -} - -void CModelInfoSA::ModelAddRef(EModelRequestType requestType, const char* szTag) -{ - // Are we not loaded? - if (!IsLoaded()) - { - // Request it. Wait for it to load if we're asked to. - if (pGame && pGame->IsASyncLoadingEnabled()) - Request(requestType, szTag); - else - Request(BLOCKING, szTag); - } - - // Increment the references. - if (m_dwReferences == 0) - { - assert(!m_dwPendingInterfaceRef); - if (IsLoaded()) - { - m_pInterface = ppModelInfo[m_dwModelID]; - m_pInterface->usNumberOfRefs++; - } - else - m_dwPendingInterfaceRef = 1; - } - - m_dwReferences++; -} - -int CModelInfoSA::GetRefCount() -{ - return static_cast(m_dwReferences); -} - -void CModelInfoSA::RemoveRef(bool bRemoveExtraGTARef) -{ - // Decrement the references - if (m_dwReferences > 0) - m_dwReferences--; - - if (m_dwReferences == 0 && m_dwPendingInterfaceRef) - { - m_dwPendingInterfaceRef = 0; - return; - } - - // Handle extra ref if requested - if (bRemoveExtraGTARef) - { - // Remove ref added by GTA. - if (m_pInterface->usNumberOfRefs > 1) - { - DWORD dwFunction = FUNC_RemoveRef; - CBaseModelInfoSAInterface* pInterface = m_pInterface; - _asm - { - mov ecx, pInterface - call dwFunction - } - } - } - - // Unload it if 0 references left and we're not CJ model. - // And if we're loaded. - if (m_dwReferences == 0 && m_dwModelID != 0 && IsLoaded()) - { - Remove(); - } -} - -void CModelInfoSA::SetAlphaTransparencyEnabled(BOOL bEnabled) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - if (!MapContains(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID)) - { - MapSet(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID, (BYTE)(m_pInterface->bAlphaTransparency)); - } - m_pInterface->bAlphaTransparency = bEnabled; - } -} - -bool CModelInfoSA::IsAlphaTransparencyEnabled() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - return m_pInterface->bAlphaTransparency; - } - return false; -} - -void CModelInfoSA::StaticResetAlphaTransparencies() -{ - for (std::map::const_iterator iter = ms_ModelDefaultAlphaTransparencyMap.begin(); iter != ms_ModelDefaultAlphaTransparencyMap.end(); iter++) - { - CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; - if (pInterface) - { - pInterface->bAlphaTransparency = iter->second; - } - } - - ms_ModelDefaultAlphaTransparencyMap.clear(); -} - -void CModelInfoSA::ResetAlphaTransparency() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - BYTE* pbEnabled = MapFind(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); - if (pbEnabled) - { - m_pInterface->bAlphaTransparency = *pbEnabled; - MapRemove(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); - } - } -} - -short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) -{ - short sreturn = -1; - if (usUpgrade >= 1000 && usUpgrade <= 1193) - { - DWORD ModelID = m_dwModelID; - _asm - { - mov eax, ModelID - movsx edx, usUpgrade - mov eax, ARRAY_ModelInfo[eax*4] - mov ax, [eax+edx*2+0x2D6] - mov sreturn, ax - } - } - return sreturn; -} - -bool CModelInfoSA::IsUpgradeAvailable(eVehicleUpgradePosn posn) -{ - bool bRet = false; - DWORD ModelID = m_dwModelID; - _asm - { - mov eax, ModelID - lea ecx, ARRAY_ModelInfo[eax*4] - - mov eax, posn - mov ecx, [ecx+0x5C] - shl eax, 5 - push esi - mov esi, [ecx+eax+0D0h] - xor edx, edx - test esi, esi - setnl dl - mov al, dl - pop esi - - mov bRet, al - } - return bRet; -} - -void CModelInfoSA::SetCustomCarPlateText(const char* szText) -{ - char* szStoredText; - DWORD ModelID = m_dwModelID; - _asm - { - push ecx - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - add ecx, 40 - mov szStoredText, ecx - pop ecx - } - - if (szText) strncpy(szStoredText, szText, 8); - else szStoredText[0] = 0; -} - -unsigned int CModelInfoSA::GetNumRemaps() -{ - DWORD dwFunc = FUNC_CVehicleModelInfo__GetNumRemaps; - DWORD ModelID = m_dwModelID; - unsigned int uiReturn = 0; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - call dwFunc - mov uiReturn, eax - } - return uiReturn; -} - -void* CModelInfoSA::GetVehicleSuspensionData() -{ - return GetInterface()->pColModel->pColData->pSuspensionLines; -} - -void* CModelInfoSA::SetVehicleSuspensionData(void* pSuspensionLines) -{ - CColDataSA* pColData = GetInterface()->pColModel->pColData; - void* pOrigSuspensionLines = pColData->pSuspensionLines; - pColData->pSuspensionLines = pSuspensionLines; - return pOrigSuspensionLines; -} - -CVector CModelInfoSA::GetVehicleExhaustFumesPosition() -{ - return GetVehicleDummyPosition(eVehicleDummies::EXHAUST); -} - -void CModelInfoSA::SetVehicleExhaustFumesPosition(const CVector& vecPosition) -{ - return SetVehicleDummyPosition(eVehicleDummies::EXHAUST, vecPosition); -} - -CVector CModelInfoSA::GetVehicleDummyPosition(eVehicleDummies eDummy) -{ - if (!IsVehicle()) - return CVector(); - - // Request model load right now if not loaded yet (#9897) - if (!IsLoaded()) - Request(BLOCKING, "GetVehicleDummyPosition"); - - auto pVehicleModel = reinterpret_cast(m_pInterface); - return pVehicleModel->pVisualInfo->vecDummies[eDummy]; -} - -void CModelInfoSA::SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) -{ - if (!IsVehicle()) - return; - - // Request model load right now if not loaded yet (#9897) - if (!IsLoaded()) - Request(BLOCKING, "SetVehicleDummyPosition"); - - // Store default position in map - auto pVehicleModel = reinterpret_cast(m_pInterface); - auto iter = ms_ModelDefaultDummiesPosition.find(pVehicleModel); - if (iter == ms_ModelDefaultDummiesPosition.end()) - { - ms_ModelDefaultDummiesPosition.insert({pVehicleModel, std::map()}); - // Increment this model references count, so we don't unload it before we have a chance to reset the positions - m_pInterface->usNumberOfRefs++; - } - - if (ms_ModelDefaultDummiesPosition[pVehicleModel].find(eDummy) == ms_ModelDefaultDummiesPosition[pVehicleModel].end()) - { - ms_ModelDefaultDummiesPosition[pVehicleModel][eDummy] = pVehicleModel->pVisualInfo->vecDummies[eDummy]; - } - - // Set dummy position - pVehicleModel->pVisualInfo->vecDummies[eDummy] = vecPosition; -} - -void CModelInfoSA::ResetAllVehicleDummies() -{ - for (auto& info : ms_ModelDefaultDummiesPosition) - { - CVehicleModelInfoSAInterface* pVehicleModel = info.first; - for (auto& dummy : ms_ModelDefaultDummiesPosition[pVehicleModel]) - { - // TODO: Find out why this is a nullptr, and fix underlying bug - if (pVehicleModel->pVisualInfo != nullptr) - pVehicleModel->pVisualInfo->vecDummies[dummy.first] = dummy.second; - } - ms_ModelDefaultDummiesPosition[pVehicleModel].clear(); - // Decrement reference counter, since we reverted all position changes, the model can be safely unloaded - info.first->usNumberOfRefs--; - } - ms_ModelDefaultDummiesPosition.clear(); -} - -void CModelInfoSA::SetCustomModel(RpClump* pClump) -{ - // Error - if (pClump == NULL) - return; - - // Store the custom clump - m_pCustomClump = pClump; - - // Replace the model if we're loaded. - if (IsLoaded()) - { - switch (GetModelType()) - { - case MODEL_INFO_TYPE_PED: - return pGame->GetRenderWare()->ReplacePedModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_WEAPON: - return pGame->GetRenderWare()->ReplaceWeaponModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_VEHICLE: - return pGame->GetRenderWare()->ReplaceVehicleModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_ATOMIC: - case MODEL_INFO_TYPE_LOD_ATOMIC: - case MODEL_INFO_TYPE_TIME: - return pGame->GetRenderWare()->ReplaceAllAtomicsInModel(pClump, static_cast(m_dwModelID)); - } - } -} - -void CModelInfoSA::RestoreOriginalModel() -{ - // Are we loaded? - if (IsLoaded()) - { - ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(static_cast(m_dwModelID)); - } - - // Reset the stored custom vehicle clump - m_pCustomClump = NULL; -} - -void CModelInfoSA::SetColModel(CColModel* pColModel) -{ - // Grab the interfaces - CColModelSAInterface* pColModelInterface = pColModel->GetInterface(); - - if (!m_bAddedRefForCollision) - { - // Prevent this model from unloading while we have custom collision - ModelAddRef(BLOCKING, "for collision"); - m_bAddedRefForCollision = true; - } - - // Should always be loaded at this point - - // Skip setting if already done - if (m_pCustomColModel == pColModel) - return; - - // Store the col model we set - m_pCustomColModel = pColModel; - - // Do the following only if we're loaded - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // If no collision model has been set before, store the original in case we want to restore it - if (!m_pOriginalColModelInterface) - m_pOriginalColModelInterface = m_pInterface->pColModel; - - // Apply some low-level hacks - pColModelInterface->level = 0xA9; - - // Call SetColModel - DWORD dwFunc = FUNC_SetColModel; - DWORD ModelID = m_dwModelID; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - push 1 - push pColModelInterface - call dwFunc - } - - // FUNC_SetColModel resets bDoWeOwnTheColModel - m_pInterface->bDoWeOwnTheColModel = false; - m_pInterface->bCollisionWasStreamedWithModel = false; - - // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) - DWORD func = 0x5B2C20; - _asm - { - push pColModelInterface - push ModelID - call func - add esp, 8 - } - - // Set some lighting for this collision if not already present - CColDataSA* pColData = pColModelInterface->pColData; - if (pColData) - { - for (uint i = 0; i < pColData->numColTriangles; i++) - { - CColTriangleSA* pTriangle = pColData->pColTriangles + i; - if (pTriangle->lighting.night == 0 && pTriangle->lighting.day == 0) - { - pTriangle->lighting.night = 1; - pTriangle->lighting.day = 12; - } - } - } - } -} - -void CModelInfoSA::RestoreColModel() -{ - // Are we loaded? - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // We only have to store if the collision model was set - // Also only if we have a col model set - if (m_pOriginalColModelInterface && m_pCustomColModel) - { - DWORD dwFunc = FUNC_SetColModel; - DWORD dwOriginalColModelInterface = (DWORD)m_pOriginalColModelInterface; - DWORD ModelID = m_dwModelID; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - push 1 - push dwOriginalColModelInterface - call dwFunc - } - - // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) - DWORD func = 0x5B2C20; - _asm - { - push dwOriginalColModelInterface - push ModelID - call func - add esp, 8 - } - // (IJs) Document this function some time - } - } - - // We currently have no custom model loaded - m_pCustomColModel = NULL; - - // Remove ref added for collision - if (m_bAddedRefForCollision) - { - m_bAddedRefForCollision = false; - RemoveRef(); - } -} - -void CModelInfoSA::MakeCustomModel() -{ - // We have a custom model? - if (m_pCustomClump) - { - SetCustomModel(m_pCustomClump); - } - - // Custom collision model is not NULL and it's different from the original? - if (m_pCustomColModel) - { - SetColModel(m_pCustomColModel); - } -} - -void CModelInfoSA::GetVoice(short* psVoiceType, short* psVoiceID) -{ - if (psVoiceType) - *psVoiceType = GetPedModelInfoInterface()->sVoiceType; - if (psVoiceID) - *psVoiceID = GetPedModelInfoInterface()->sFirstVoice; -} - -void CModelInfoSA::GetVoice(const char** pszVoiceType, const char** pszVoice) -{ - short sVoiceType, sVoiceID; - GetVoice(&sVoiceType, &sVoiceID); - if (pszVoiceType) - *pszVoiceType = CPedSoundSA::GetVoiceTypeNameFromID(sVoiceType); - if (pszVoice) - *pszVoice = CPedSoundSA::GetVoiceNameFromID(sVoiceType, sVoiceID); -} - -void CModelInfoSA::SetVoice(short sVoiceType, short sVoiceID) -{ - GetPedModelInfoInterface()->sVoiceType = sVoiceType; - GetPedModelInfoInterface()->sFirstVoice = sVoiceID; - GetPedModelInfoInterface()->sLastVoice = sVoiceID; - GetPedModelInfoInterface()->sNextVoice = sVoiceID; -} - -void CModelInfoSA::SetVoice(const char* szVoiceType, const char* szVoice) -{ - short sVoiceType = CPedSoundSA::GetVoiceTypeIDFromName(szVoiceType); - if (sVoiceType < 0) - return; - short sVoiceID = CPedSoundSA::GetVoiceIDFromName(sVoiceType, szVoice); - if (sVoiceID < 0) - return; - SetVoice(sVoiceType, sVoiceID); -} - -void CModelInfoSA::MakePedModel(char* szTexture) -{ - // Create a new CPedModelInfo - CPedModelInfoSA pedModelInfo; - ppModelInfo[m_dwModelID] = (CBaseModelInfoSAInterface*)pedModelInfo.GetPedModelInfoInterface(); - - // Load our texture - pGame->GetStreaming()->RequestSpecialModel(m_dwModelID, szTexture, 0); -} - -void CModelInfoSA::DeallocateModel(void) -{ - Remove(); - ppModelInfo[m_dwModelID] = nullptr; -} -////////////////////////////////////////////////////////////////////////////////////////// -// -// Hook for NodeNameStreamRead -// -// Ignore extra characters in dff frame name -// -////////////////////////////////////////////////////////////////////////////////////////// -__declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) -{ - // Calc sizes - const uint uiMaxBufferSize = 24; - uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); - uint uiAmountToSkip = uiSize - uiAmountToRead; - - // Read good bit - RwStreamRead(stream, pDest, uiAmountToRead); - pDest[uiAmountToRead] = 0; - - // Skip bad bit (this might not be required) - if (uiAmountToSkip > 0) - RwStreamSkip(stream, uiAmountToSkip); -} - -// Hook info -#define HOOKPOS_NodeNameStreamRead 0x072FA68 -#define HOOKSIZE_NodeNameStreamRead 15 -DWORD RETURN_NodeNameStreamRead = 0x072FA77; -void _declspec(naked) HOOK_NodeNameStreamRead() -{ - _asm - { - pushad - push edi - push esi - push ebx - call OnMY_NodeNameStreamRead - add esp, 4*3 - popad - - jmp RETURN_NodeNameStreamRead - } -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -// Setup hooks -// -////////////////////////////////////////////////////////////////////////////////////////// -void CModelInfoSA::StaticSetHooks() -{ - EZHookInstall(NodeNameStreamRead); -} - -// Recursive RwFrame children searching function -void CModelInfoSA::RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel) -{ - for (RwFrame* ret = parent->child; ret != NULL; ret = ret->next) - { - // recurse into the child - if (ret->child != NULL) - { - RwSetSupportedUpgrades(ret, dwModel); - } - SString strName = ret->szName; - // Spoiler - if (strName == "ug_bonnet") - { - m_ModelSupportedUpgrades.m_bBonnet = true; - } - else if (strName == "ug_bonnet_left") - { - m_ModelSupportedUpgrades.m_bBonnet_Left = true; - } - else if (strName == "ug_bonnet_left_dam") - { - m_ModelSupportedUpgrades.m_bBonnet_Left_dam = true; - } - else if (strName == "ug_bonnet_right") - { - m_ModelSupportedUpgrades.m_bBonnet_Right = true; - } - else if (strName == "ug_bonnet_right_dam") - { - m_ModelSupportedUpgrades.m_bBonnet_Right_dam = true; - } - // Spoiler - else if (strName == "ug_spoiler") - { - m_ModelSupportedUpgrades.m_bSpoiler = true; - } - else if (strName == "ug_spoiler_dam") - { - m_ModelSupportedUpgrades.m_bSpoiler_dam = true; - } - // Bonnet - else if (strName == "ug_lights") - { - m_ModelSupportedUpgrades.m_bLamps = true; - } - else if (strName == "ug_lights_dam") - { - m_ModelSupportedUpgrades.m_bLamps_dam = true; - } - // Roof - else if (strName == "ug_roof") - { - m_ModelSupportedUpgrades.m_bRoof = true; - } - // Side Skirt - else if (strName == "ug_wing_right") - { - m_ModelSupportedUpgrades.m_bSideSkirt_Right = true; - } - // Side Skirt - else if (strName == "ug_wing_left") - { - m_ModelSupportedUpgrades.m_bSideSkirt_Left = true; - } - // Exhaust - else if (strName == "exhaust_ok") - { - m_ModelSupportedUpgrades.m_bExhaust = true; - } - // Front bullbars - else if (strName == "ug_frontbullbar") - { - m_ModelSupportedUpgrades.m_bFrontBullbars = true; - } - // rear bullbars - else if (strName == "ug_backbullbar") - { - m_ModelSupportedUpgrades.m_bRearBullbars = true; - } - // Front bumper - else if (strName == "bump_front_dummy") - { - m_ModelSupportedUpgrades.m_bFrontBumper = true; - } - // Rear bumper - else if (strName == "bump_rear_dummy") - { - m_ModelSupportedUpgrades.m_bRearBumper = true; - } - // Rear bumper - else if (strName == "misc_c") - { - m_ModelSupportedUpgrades.m_bMisc = true; - } - } -} - -void CModelInfoSA::InitialiseSupportedUpgrades(RpClump* pClump) -{ - m_ModelSupportedUpgrades.Reset(); - RwFrame* pFrame = RpGetFrame(pClump); - RwSetSupportedUpgrades(pFrame, m_dwModelID); - m_ModelSupportedUpgrades.m_bInitialised = true; -} - -void CModelInfoSA::ResetSupportedUpgrades() -{ - m_ModelSupportedUpgrades.Reset(); -} - -void CModelInfoSA::SetObjectPropertiesGroup(unsigned short usNewGroup) -{ - unsigned short usOrgGroup = GetObjectPropertiesGroup(); - if (usOrgGroup == usNewGroup) - return; - - if (!MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID)) - MapSet(ms_OriginalObjectPropertiesGroups, m_dwModelID, usOrgGroup); - - GetInterface()->usDynamicIndex = usNewGroup; -} - -unsigned short CModelInfoSA::GetObjectPropertiesGroup() -{ - unsigned short usGroup = GetInterface()->usDynamicIndex; - if (usGroup == 0xFFFF) - usGroup = 0; - - return usGroup; -} - -void CModelInfoSA::RestoreObjectPropertiesGroup() -{ - unsigned short* usGroupInMap = MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID); - if (usGroupInMap) - { - GetInterface()->usDynamicIndex = *usGroupInMap; - MapRemove(ms_OriginalObjectPropertiesGroups, m_dwModelID); - } -} - -void CModelInfoSA::RestoreAllObjectsPropertiesGroups() -{ - for (const auto& pair : ms_OriginalObjectPropertiesGroups) - { - pGame->GetModelInfo(pair.first)->GetInterface()->usDynamicIndex = pair.second; - } - ms_OriginalObjectPropertiesGroups.clear(); -} - -eModelInfoType CModelInfoSA::GetModelType() -{ - return ((eModelInfoType(*)())m_pInterface->VFTBL->GetModelType)(); -} - -bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) -{ - bool isTowable = true; - - const bool isTowTruck = towingModel->GetModel() == 525; - const bool isTractor = towingModel->GetModel() == 531; - - if (IsTrain() || towingModel->IsTrain()) - { - // A train is never towing other vehicles. Trains are linked by other means - isTowable = false; - } - else if (isTowTruck || isTractor) - { - const bool isFarmTrailer = GetModel() == 610; - - // Tow truck (525) and tractor (531) can only tow certain vehicle types without issues - if (IsBoat() || IsBike() || IsBmx()) - { - isTowable = false; - } - else if (IsTrailer() && !(isTractor && isFarmTrailer)) - { - isTowable = false; - } - } - - return isTowable; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -// CModelInfoSA::ForceUnload -// -// 1. Unload the dff from memory -// 2. Unload the associated txd from memory -// 3. Cross fingers -// -// Why do we do this? -// Player model adds (seemingly) unnecessary refs -// (Will crash if anything is actually using the model or txd) -// -// Won't work if there is a custom model replacement -// -// Returns true if model was unloaded -// -////////////////////////////////////////////////////////////////////////////////////////// -bool CModelInfoSA::ForceUnload() -{ - CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface(); - - // Need to have at least one ref to delete pRwObject - if (pModelInfoSAInterface->usNumberOfRefs == 0 && pModelInfoSAInterface->pRwObject != NULL) - { - pModelInfoSAInterface->usNumberOfRefs++; - } - - // Keep removing refs from the model until is it gone - uint uiLimit = 100; - while (pModelInfoSAInterface->usNumberOfRefs > 0 && uiLimit--) - { - RemoveRef(); - } - - // Did it work? - if (pModelInfoSAInterface->usNumberOfRefs > 0 || pModelInfoSAInterface->pRwObject != NULL) - return false; - - // If success, then remove txd - ushort usTxdId = GetTextureDictionaryID(); - if (usTxdId) - pGame->GetRenderWare()->TxdForceUnload(usTxdId, true); - - return true; -} +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CModelInfoSA.cpp + * PURPOSE: Entity model information handler + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "gamesa_renderware.h" + +extern CGameSA* pGame; + +CBaseModelInfoSAInterface** ppModelInfo = (CBaseModelInfoSAInterface**)ARRAY_ModelInfo; + +std::map CModelInfoSA::ms_RestreamTxdIDMap; +std::map CModelInfoSA::ms_ModelDefaultLodDistanceMap; +std::map CModelInfoSA::ms_ModelDefaultAlphaTransparencyMap; +std::unordered_map> CModelInfoSA::ms_ModelDefaultDummiesPosition; +std::map CModelInfoSA::ms_ModelDefaultModelTimeInfo; +std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; + +CModelInfoSA::CModelInfoSA() +{ + m_pInterface = NULL; + this->m_dwModelID = 0xFFFFFFFF; + m_dwReferences = 0; + m_dwPendingInterfaceRef = 0; + m_pOriginalColModelInterface = NULL; + m_pCustomClump = NULL; + m_pCustomColModel = NULL; + m_bAddedRefForCollision = false; +} + +CModelInfoSA::CModelInfoSA(DWORD dwModelID) +{ + this->m_dwModelID = dwModelID; + m_pInterface = ppModelInfo[m_dwModelID]; + m_dwReferences = 0; + m_dwPendingInterfaceRef = 0; + m_pOriginalColModelInterface = NULL; + m_pCustomClump = NULL; + m_pCustomColModel = NULL; + m_bAddedRefForCollision = false; +} + +CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() +{ + return m_pInterface = ppModelInfo[m_dwModelID]; +} + +BOOL CModelInfoSA::IsBoat() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBoat ( )"); + DWORD dwFunction = FUNC_IsBoatModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsCar() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsCar ( )"); + DWORD dwFunction = FUNC_IsCarModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsTrain() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsTrain ( )"); + DWORD dwFunction = FUNC_IsTrainModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsHeli() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsHeli ( )"); + DWORD dwFunction = FUNC_IsHeliModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsPlane() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsPlane ( )"); + DWORD dwFunction = FUNC_IsPlaneModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsBike() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBike ( )"); + DWORD dwFunction = FUNC_IsBikeModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsFakePlane() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsFakePlane ( )"); + DWORD dwFunction = FUNC_IsFakePlaneModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsMonsterTruck() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsMonsterTruck ( )"); + DWORD dwFunction = FUNC_IsMonsterTruckModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsQuadBike() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsQuadBike ( )"); + DWORD dwFunction = FUNC_IsQuadBikeModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsBmx() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBmx ( )"); + DWORD dwFunction = FUNC_IsBmxModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsTrailer() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsTrailer ( )"); + DWORD dwFunction = FUNC_IsTrailerModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsVehicle() +{ + /* + DEBUG_TRACE("BOOL CModelInfoSA::IsVehicle ( )"); + DWORD dwFunction = FUNC_IsVehicleModelType; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; + */ + + // Above doesn't seem to work + return m_dwModelID >= 400 && m_dwModelID <= 611; +} + +bool CModelInfoSA::IsPlayerModel() +{ + return (GetInterface() && GetInterface()->pColModel && GetInterface()->pColModel == (CColModelSAInterface*)VAR_CTempColModels_ModelPed1); +} + +BOOL CModelInfoSA::IsUpgrade() +{ + return m_dwModelID >= 1000 && m_dwModelID <= 1193; +} + +char* CModelInfoSA::GetNameIfVehicle() +{ + DEBUG_TRACE("char * CModelInfoSA::GetNameIfVehicle ( )"); + // if(this->IsVehicle()) + // { + DWORD dwModelInfo = ARRAY_ModelInfo; + DWORD dwFunc = FUNC_CText_Get; + DWORD ModelID = m_dwModelID; + DWORD dwReturn = 0; + + _asm + { + push eax + push ebx + push ecx + + mov ebx, ModelID + lea ebx, [ebx*4] + add ebx, ARRAY_ModelInfo + mov eax, [ebx] + add eax, 50 + + push eax + mov ecx, CLASS_CText + call dwFunc + + mov dwReturn, eax + + pop ecx + pop ebx + pop eax + } + return (char*)dwReturn; + // } + // return NULL; +} + +uint CModelInfoSA::GetAnimFileIndex() +{ + DWORD dwFunc = m_pInterface->VFTBL->GetAnimFileIndex; + DWORD dwThis = (DWORD)m_pInterface; + uint uiReturn = 0; + if (dwFunc) + { + _asm + { + mov ecx, dwThis + call dwFunc + mov uiReturn, eax + } + } + return uiReturn; +} + +VOID CModelInfoSA::Request(EModelRequestType requestType, const char* szTag) +{ + DEBUG_TRACE("VOID CModelInfoSA::Request( BOOL bAndLoad, BOOL bWaitForLoad )"); + // don't bother loading it if it already is + if (IsLoaded()) + return; + + if (m_dwModelID <= 288 && m_dwModelID != 7 && !pGame->GetModelInfo(7)->IsLoaded()) + { + // Skin 7 must be loaded in order for other skins to work. No, really. (#4010) + pGame->GetModelInfo(7)->Request(requestType, "Model 7"); + } + + // Bikes can sometimes get stuck when loading unless the anim file is handled like what is does here + // Don't change the code below unless you can test it (by recreating the problem it solves) + if (IsVehicle()) + { + uint uiAnimFileIndex = GetAnimFileIndex(); + if (uiAnimFileIndex != 0xffffffff) + { + uint uiAnimId = uiAnimFileIndex + 25575; + CModelInfoSA* pAnim = static_cast(pGame->GetModelInfo(uiAnimId)); + if (!pAnim) + { + if (uiAnimId != 25714) + LogEvent(505, "Model no anim", "", SString("%d (%d)", m_dwModelID, uiAnimId)); + } + else if (!pAnim->IsLoaded()) + { + OutputDebugLine(SString("[Models] Requesting anim file %d for model %d", uiAnimId, m_dwModelID)); + pAnim->Request(requestType, szTag); + } + } + } + + if (requestType == BLOCKING) + { + pGame->GetStreaming()->RequestModel(m_dwModelID, 0x16); + pGame->GetStreaming()->LoadAllRequestedModels(true, szTag); + if (!IsLoaded()) + { + // Try 3 more times, final time without high priority flag + int iCount = 0; + while (iCount++ < 10 && !IsLoaded()) + { + bool bOnlyPriorityModels = (iCount < 3 || iCount & 1); + pGame->GetStreaming()->LoadAllRequestedModels(bOnlyPriorityModels, szTag); + } + if (!IsLoaded()) + { + AddReportLog(6641, SString("Blocking load fail: %d (%s)", m_dwModelID, szTag)); + LogEvent(641, "Blocking load fail", "", SString("%d (%s)", m_dwModelID, szTag)); + dassert(0); + } + else + { + AddReportLog(6642, SString("Blocking load: %d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); + LogEvent(642, "Blocking load", "", SString("%d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); + } + } + } + else + { + pGame->GetStreaming()->RequestModel(m_dwModelID, 0x06); + } +} + +VOID CModelInfoSA::Remove() +{ + DEBUG_TRACE("VOID CModelInfoSA::Remove ( )"); + + // Don't remove if GTA refers to it somehow. + // Or we'll screw up SA's map for example. + + m_pInterface = ppModelInfo[m_dwModelID]; + + // Remove ref added for collision + if (m_bAddedRefForCollision) + { + m_bAddedRefForCollision = false; + if (m_pInterface->usNumberOfRefs > 0) + m_pInterface->usNumberOfRefs--; + } + + // Remove our reference + if (m_pInterface->usNumberOfRefs > 0) + m_pInterface->usNumberOfRefs--; + + // No references left? + if (m_pInterface->usNumberOfRefs == 0) + { + // We have a custom model? + if (m_pCustomClump) + { + // Mark us as unloaded. We manage the clump unloading. + // BYTE *ModelLoaded = (BYTE*)ARRAY_ModelLoaded; + // ModelLoaded[(m_dwModelID+m_dwModelID*4)<<2] = 0; + } + else + { + // Make our collision model original again before we unload. + RestoreColModel(); + + // Remove the model. + DWORD dwFunction = FUNC_RemoveModel; + DWORD ModelID = m_dwModelID; + _asm + { + push ModelID + call dwFunction + add esp, 4 + } + } + } +} + +BYTE CModelInfoSA::GetLevelFromPosition(CVector* vecPosition) +{ + DEBUG_TRACE("BYTE CModelInfoSA::GetLevelFromPosition ( CVector * vecPosition )"); + DWORD dwFunction = FUNC_GetLevelFromPosition; + BYTE bReturn = 0; + _asm + { + push vecPosition + call dwFunction + add esp, 4 + mov bReturn, al + } + return bReturn; +} + +BOOL CModelInfoSA::IsLoaded() +{ + if (DoIsLoaded()) + { + if (m_dwPendingInterfaceRef) + { + assert(m_dwReferences > 0); + m_pInterface = ppModelInfo[m_dwModelID]; + m_pInterface->usNumberOfRefs++; + m_dwPendingInterfaceRef = 0; + } + return true; + } + return false; +} + +BOOL CModelInfoSA::DoIsLoaded() +{ + DEBUG_TRACE("BOOL CModelInfoSA::DoIsLoaded ( )"); + + // return (BOOL)*(BYTE *)(ARRAY_ModelLoaded + 20*dwModelID); + BOOL bLoaded = pGame->GetStreaming()->HasModelLoaded(m_dwModelID); + + if (m_dwModelID < 20000) + { + m_pInterface = ppModelInfo[m_dwModelID]; + + if (bLoaded) + { + // Check rw object is there + if (!m_pInterface || !m_pInterface->pRwObject) + return false; + } + } + return bLoaded; +} + +BYTE CModelInfoSA::GetFlags() +{ + DWORD dwFunc = FUNC_GetModelFlags; + DWORD ModelID = m_dwModelID; + BYTE bFlags = 0; + _asm + { + push ModelID + call dwFunc + add esp, 4 + mov bFlags, al + } + return bFlags; +} + +CBoundingBox* CModelInfoSA::GetBoundingBox() +{ + DWORD dwFunc = FUNC_GetBoundingBox; + DWORD ModelID = m_dwModelID; + CBoundingBox* dwReturn = 0; + _asm + { + push ModelID + call dwFunc + add esp, 4 + mov dwReturn, eax + } + return dwReturn; +} + +bool CModelInfoSA::IsValid() +{ + if (m_dwModelID >= 20000 && m_dwModelID < MODELINFO_MAX) + return true; + return ppModelInfo[m_dwModelID] != 0; +} + +float CModelInfoSA::GetDistanceFromCentreOfMassToBaseOfModel() +{ + DWORD dwModelInfo = 0; + DWORD ModelID = m_dwModelID; + FLOAT fReturn = 0; + _asm + { + mov eax, ModelID + mov eax, ARRAY_ModelInfo[eax*4] + mov eax, [eax+20] + cmp eax, 0 + jz skip + fld [eax + 8] + fchs + fstp fReturn +skip: + } + return fReturn; +} + +unsigned short CModelInfoSA::GetTextureDictionaryID() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + return m_pInterface->usTextureDictionary; + + return 0; +} + +void CModelInfoSA::SetTextureDictionaryID(unsigned short usID) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + m_pInterface->usTextureDictionary = usID; +} + +float CModelInfoSA::GetLODDistance() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + return m_pInterface->fLodDistanceUnscaled; + + return 0.0f; +} + +bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (pTime) + { + if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) + { + MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); + } + pTime->m_nTimeOn = cHourOn; + pTime->m_nTimeOff = cHourOff; + return true; + } + } + return false; +} + +bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (time) + { + cHourOn = time->m_nTimeOn; + cHourOff = time->m_nTimeOff; + return true; + } + } + return false; +} + +void CModelInfoSA::StaticResetModelTimes() +{ + // Restore default values + for (std::map::const_iterator iter = ms_ModelDefaultModelTimeInfo.begin(); iter != ms_ModelDefaultModelTimeInfo.end(); ++iter) + { + iter->first->m_nTimeOn = iter->second->m_nTimeOn; + iter->first->m_nTimeOff = iter->second->m_nTimeOff; + } + + ms_ModelDefaultModelTimeInfo.clear(); +} + +void CModelInfoSA::SetLODDistance(float fDistance) +{ +#if 0 + // fLodDistanceUnscaled values: + // + // With the draw distance setting in GTA SP options menu set to maximum: + // 0 - 170 roughly correlates to a LOD distance of 0 - 300 game units + // 170 - 480 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in + // 490 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely + // + // With the draw distance setting in GTA SP options menu set to minimum: + // 0 - 325 roughly correlates to a LOD distance of 0 - 300 game units + // 340 - 960 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in + // 1000 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely + // + // So, to ensure the maximum draw distance with a working alpha fade-in, fLodDistanceUnscaled has to be + // no more than: 325 - (325-170) * draw_distance_setting + // + + // Change GTA draw distance value from 0.925 to 1.8 into 0 to 1 + float fDrawDistanceSetting = UnlerpClamped ( 0.925f, CSettingsSA ().GetDrawDistance (), 1.8f ); + + // Calc max setting allowed for fLodDistanceUnscaled to preserve alpha fade-in + float fMaximumValue = Lerp ( 325.f, fDrawDistanceSetting, 170.f ); + + // Ensure fDistance is in range + fDistance = std::min ( fDistance, fMaximumValue ); +#endif + // Limit to 325.f as it goes horrible after that + fDistance = std::min(fDistance, 325.f); + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // Save default value if not done yet + if (!MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) + MapSet(ms_ModelDefaultLodDistanceMap, m_dwModelID, m_pInterface->fLodDistanceUnscaled); + m_pInterface->fLodDistanceUnscaled = fDistance; + } +} + +void CModelInfoSA::StaticResetLodDistances() +{ + // Restore default values + for (std::map::const_iterator iter = ms_ModelDefaultLodDistanceMap.begin(); iter != ms_ModelDefaultLodDistanceMap.end(); ++iter) + { + CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; + if (pInterface) + pInterface->fLodDistanceUnscaled = iter->second; + } + + ms_ModelDefaultLodDistanceMap.clear(); +} + +void CModelInfoSA::RestreamIPL() +{ + // IPLs should not contain peds, weapons, vehicles and vehicle upgrades + if (m_dwModelID > 611 && (m_dwModelID < 1000 || m_dwModelID > 1193)) + MapSet(ms_RestreamTxdIDMap, GetTextureDictionaryID(), 0); +} + +void CModelInfoSA::StaticFlushPendingRestreamIPL() +{ + if (ms_RestreamTxdIDMap.empty()) + return; + // This function restreams all instances of the model *that are from the default SA world (ipl)*. + // In other words, it does not affect elements created by MTA. + // It's mostly a reimplementation of SA's DeleteAllRwObjects, except that it filters by model ID. + + ((void (*)())FUNC_FlushRequestList)(); + + std::set removedModels; + + for (int i = 0; i < 2 * NUM_StreamSectorRows * NUM_StreamSectorCols; i++) + { + DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamSectors)[i]; + while (pSectorEntry) + { + CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; + + // Possible bug - pEntity seems to be invalid here occasionally + if (pEntity->vtbl->DeleteRwObject != 0x00534030) + { + // Log info + OutputDebugString(SString("Entity 0x%08x (with model %d) at ARRAY_StreamSectors[%d,%d] is invalid\n", pEntity, pEntity->m_nModelIndex, + i / 2 % NUM_StreamSectorRows, i / 2 / NUM_StreamSectorCols)); + // Assert in debug + #if MTA_DEBUG + assert(pEntity->vtbl->DeleteRwObject == 0x00534030); + #endif + pSectorEntry = (DWORD*)pSectorEntry[1]; + continue; + } + + if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) + { + if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) + { + _asm + { + mov ecx, pEntity + mov eax, [ecx] + call dword ptr [eax+20h] + } + removedModels.insert(pEntity->m_nModelIndex); + } + } + + pSectorEntry = (DWORD*)pSectorEntry[1]; + } + } + + for (int i = 0; i < NUM_StreamRepeatSectorRows * NUM_StreamRepeatSectorCols; i++) + { + DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamRepeatSectors)[3 * i + 2]; + while (pSectorEntry) + { + CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; + if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) + { + if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) + { + _asm + { + mov ecx, pEntity + mov eax, [ecx] + call dword ptr [eax+20h] + } + removedModels.insert(pEntity->m_nModelIndex); + } + } + pSectorEntry = (DWORD*)pSectorEntry[1]; + } + } + + ms_RestreamTxdIDMap.clear(); + + std::set::iterator it; + for (it = removedModels.begin(); it != removedModels.end(); it++) + { + ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(*it); + MemPut(ARRAY_ModelLoaded + 20 * (*it), 0); + } +} + +void CModelInfoSA::ModelAddRef(EModelRequestType requestType, const char* szTag) +{ + // Are we not loaded? + if (!IsLoaded()) + { + // Request it. Wait for it to load if we're asked to. + if (pGame && pGame->IsASyncLoadingEnabled()) + Request(requestType, szTag); + else + Request(BLOCKING, szTag); + } + + // Increment the references. + if (m_dwReferences == 0) + { + assert(!m_dwPendingInterfaceRef); + if (IsLoaded()) + { + m_pInterface = ppModelInfo[m_dwModelID]; + m_pInterface->usNumberOfRefs++; + } + else + m_dwPendingInterfaceRef = 1; + } + + m_dwReferences++; +} + +int CModelInfoSA::GetRefCount() +{ + return static_cast(m_dwReferences); +} + +void CModelInfoSA::RemoveRef(bool bRemoveExtraGTARef) +{ + // Decrement the references + if (m_dwReferences > 0) + m_dwReferences--; + + if (m_dwReferences == 0 && m_dwPendingInterfaceRef) + { + m_dwPendingInterfaceRef = 0; + return; + } + + // Handle extra ref if requested + if (bRemoveExtraGTARef) + { + // Remove ref added by GTA. + if (m_pInterface->usNumberOfRefs > 1) + { + DWORD dwFunction = FUNC_RemoveRef; + CBaseModelInfoSAInterface* pInterface = m_pInterface; + _asm + { + mov ecx, pInterface + call dwFunction + } + } + } + + // Unload it if 0 references left and we're not CJ model. + // And if we're loaded. + if (m_dwReferences == 0 && m_dwModelID != 0 && IsLoaded()) + { + Remove(); + } +} + +void CModelInfoSA::SetAlphaTransparencyEnabled(BOOL bEnabled) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + if (!MapContains(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID)) + { + MapSet(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID, (BYTE)(m_pInterface->bAlphaTransparency)); + } + m_pInterface->bAlphaTransparency = bEnabled; + } +} + +bool CModelInfoSA::IsAlphaTransparencyEnabled() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + return m_pInterface->bAlphaTransparency; + } + return false; +} + +void CModelInfoSA::StaticResetAlphaTransparencies() +{ + for (std::map::const_iterator iter = ms_ModelDefaultAlphaTransparencyMap.begin(); iter != ms_ModelDefaultAlphaTransparencyMap.end(); iter++) + { + CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; + if (pInterface) + { + pInterface->bAlphaTransparency = iter->second; + } + } + + ms_ModelDefaultAlphaTransparencyMap.clear(); +} + +void CModelInfoSA::ResetAlphaTransparency() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + BYTE* pbEnabled = MapFind(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); + if (pbEnabled) + { + m_pInterface->bAlphaTransparency = *pbEnabled; + MapRemove(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); + } + } +} + +short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) +{ + short sreturn = -1; + if (usUpgrade >= 1000 && usUpgrade <= 1193) + { + DWORD ModelID = m_dwModelID; + _asm + { + mov eax, ModelID + movsx edx, usUpgrade + mov eax, ARRAY_ModelInfo[eax*4] + mov ax, [eax+edx*2+0x2D6] + mov sreturn, ax + } + } + return sreturn; +} + +bool CModelInfoSA::IsUpgradeAvailable(eVehicleUpgradePosn posn) +{ + bool bRet = false; + DWORD ModelID = m_dwModelID; + _asm + { + mov eax, ModelID + lea ecx, ARRAY_ModelInfo[eax*4] + + mov eax, posn + mov ecx, [ecx+0x5C] + shl eax, 5 + push esi + mov esi, [ecx+eax+0D0h] + xor edx, edx + test esi, esi + setnl dl + mov al, dl + pop esi + + mov bRet, al + } + return bRet; +} + +void CModelInfoSA::SetCustomCarPlateText(const char* szText) +{ + char* szStoredText; + DWORD ModelID = m_dwModelID; + _asm + { + push ecx + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + add ecx, 40 + mov szStoredText, ecx + pop ecx + } + + if (szText) strncpy(szStoredText, szText, 8); + else szStoredText[0] = 0; +} + +unsigned int CModelInfoSA::GetNumRemaps() +{ + DWORD dwFunc = FUNC_CVehicleModelInfo__GetNumRemaps; + DWORD ModelID = m_dwModelID; + unsigned int uiReturn = 0; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + call dwFunc + mov uiReturn, eax + } + return uiReturn; +} + +void* CModelInfoSA::GetVehicleSuspensionData() +{ + return GetInterface()->pColModel->pColData->pSuspensionLines; +} + +void* CModelInfoSA::SetVehicleSuspensionData(void* pSuspensionLines) +{ + CColDataSA* pColData = GetInterface()->pColModel->pColData; + void* pOrigSuspensionLines = pColData->pSuspensionLines; + pColData->pSuspensionLines = pSuspensionLines; + return pOrigSuspensionLines; +} + +CVector CModelInfoSA::GetVehicleExhaustFumesPosition() +{ + return GetVehicleDummyPosition(eVehicleDummies::EXHAUST); +} + +void CModelInfoSA::SetVehicleExhaustFumesPosition(const CVector& vecPosition) +{ + return SetVehicleDummyPosition(eVehicleDummies::EXHAUST, vecPosition); +} + +CVector CModelInfoSA::GetVehicleDummyPosition(eVehicleDummies eDummy) +{ + if (!IsVehicle()) + return CVector(); + + // Request model load right now if not loaded yet (#9897) + if (!IsLoaded()) + Request(BLOCKING, "GetVehicleDummyPosition"); + + auto pVehicleModel = reinterpret_cast(m_pInterface); + return pVehicleModel->pVisualInfo->vecDummies[eDummy]; +} + +void CModelInfoSA::SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) +{ + if (!IsVehicle()) + return; + + // Request model load right now if not loaded yet (#9897) + if (!IsLoaded()) + Request(BLOCKING, "SetVehicleDummyPosition"); + + // Store default position in map + auto pVehicleModel = reinterpret_cast(m_pInterface); + auto iter = ms_ModelDefaultDummiesPosition.find(pVehicleModel); + if (iter == ms_ModelDefaultDummiesPosition.end()) + { + ms_ModelDefaultDummiesPosition.insert({pVehicleModel, std::map()}); + // Increment this model references count, so we don't unload it before we have a chance to reset the positions + m_pInterface->usNumberOfRefs++; + } + + if (ms_ModelDefaultDummiesPosition[pVehicleModel].find(eDummy) == ms_ModelDefaultDummiesPosition[pVehicleModel].end()) + { + ms_ModelDefaultDummiesPosition[pVehicleModel][eDummy] = pVehicleModel->pVisualInfo->vecDummies[eDummy]; + } + + // Set dummy position + pVehicleModel->pVisualInfo->vecDummies[eDummy] = vecPosition; +} + +void CModelInfoSA::ResetAllVehicleDummies() +{ + for (auto& info : ms_ModelDefaultDummiesPosition) + { + CVehicleModelInfoSAInterface* pVehicleModel = info.first; + for (auto& dummy : ms_ModelDefaultDummiesPosition[pVehicleModel]) + { + // TODO: Find out why this is a nullptr, and fix underlying bug + if (pVehicleModel->pVisualInfo != nullptr) + pVehicleModel->pVisualInfo->vecDummies[dummy.first] = dummy.second; + } + ms_ModelDefaultDummiesPosition[pVehicleModel].clear(); + // Decrement reference counter, since we reverted all position changes, the model can be safely unloaded + info.first->usNumberOfRefs--; + } + ms_ModelDefaultDummiesPosition.clear(); +} + +void CModelInfoSA::SetCustomModel(RpClump* pClump) +{ + // Error + if (pClump == NULL) + return; + + // Store the custom clump + m_pCustomClump = pClump; + + // Replace the model if we're loaded. + if (IsLoaded()) + { + switch (GetModelType()) + { + case MODEL_INFO_TYPE_PED: + return pGame->GetRenderWare()->ReplacePedModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_WEAPON: + return pGame->GetRenderWare()->ReplaceWeaponModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_VEHICLE: + return pGame->GetRenderWare()->ReplaceVehicleModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_ATOMIC: + case MODEL_INFO_TYPE_LOD_ATOMIC: + case MODEL_INFO_TYPE_TIME: + return pGame->GetRenderWare()->ReplaceAllAtomicsInModel(pClump, static_cast(m_dwModelID)); + } + } +} + +void CModelInfoSA::RestoreOriginalModel() +{ + // Are we loaded? + if (IsLoaded()) + { + ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(static_cast(m_dwModelID)); + } + + // Reset the stored custom vehicle clump + m_pCustomClump = NULL; +} + +void CModelInfoSA::SetColModel(CColModel* pColModel) +{ + // Grab the interfaces + CColModelSAInterface* pColModelInterface = pColModel->GetInterface(); + + if (!m_bAddedRefForCollision) + { + // Prevent this model from unloading while we have custom collision + ModelAddRef(BLOCKING, "for collision"); + m_bAddedRefForCollision = true; + } + + // Should always be loaded at this point + + // Skip setting if already done + if (m_pCustomColModel == pColModel) + return; + + // Store the col model we set + m_pCustomColModel = pColModel; + + // Do the following only if we're loaded + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // If no collision model has been set before, store the original in case we want to restore it + if (!m_pOriginalColModelInterface) + m_pOriginalColModelInterface = m_pInterface->pColModel; + + // Apply some low-level hacks + pColModelInterface->level = 0xA9; + + // Call SetColModel + DWORD dwFunc = FUNC_SetColModel; + DWORD ModelID = m_dwModelID; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + push 1 + push pColModelInterface + call dwFunc + } + + // FUNC_SetColModel resets bDoWeOwnTheColModel + m_pInterface->bDoWeOwnTheColModel = false; + m_pInterface->bCollisionWasStreamedWithModel = false; + + // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) + DWORD func = 0x5B2C20; + _asm + { + push pColModelInterface + push ModelID + call func + add esp, 8 + } + + // Set some lighting for this collision if not already present + CColDataSA* pColData = pColModelInterface->pColData; + if (pColData) + { + for (uint i = 0; i < pColData->numColTriangles; i++) + { + CColTriangleSA* pTriangle = pColData->pColTriangles + i; + if (pTriangle->lighting.night == 0 && pTriangle->lighting.day == 0) + { + pTriangle->lighting.night = 1; + pTriangle->lighting.day = 12; + } + } + } + } +} + +void CModelInfoSA::RestoreColModel() +{ + // Are we loaded? + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // We only have to store if the collision model was set + // Also only if we have a col model set + if (m_pOriginalColModelInterface && m_pCustomColModel) + { + DWORD dwFunc = FUNC_SetColModel; + DWORD dwOriginalColModelInterface = (DWORD)m_pOriginalColModelInterface; + DWORD ModelID = m_dwModelID; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + push 1 + push dwOriginalColModelInterface + call dwFunc + } + + // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) + DWORD func = 0x5B2C20; + _asm + { + push dwOriginalColModelInterface + push ModelID + call func + add esp, 8 + } + // (IJs) Document this function some time + } + } + + // We currently have no custom model loaded + m_pCustomColModel = NULL; + + // Remove ref added for collision + if (m_bAddedRefForCollision) + { + m_bAddedRefForCollision = false; + RemoveRef(); + } +} + +void CModelInfoSA::MakeCustomModel() +{ + // We have a custom model? + if (m_pCustomClump) + { + SetCustomModel(m_pCustomClump); + } + + // Custom collision model is not NULL and it's different from the original? + if (m_pCustomColModel) + { + SetColModel(m_pCustomColModel); + } +} + +void CModelInfoSA::GetVoice(short* psVoiceType, short* psVoiceID) +{ + if (psVoiceType) + *psVoiceType = GetPedModelInfoInterface()->sVoiceType; + if (psVoiceID) + *psVoiceID = GetPedModelInfoInterface()->sFirstVoice; +} + +void CModelInfoSA::GetVoice(const char** pszVoiceType, const char** pszVoice) +{ + short sVoiceType, sVoiceID; + GetVoice(&sVoiceType, &sVoiceID); + if (pszVoiceType) + *pszVoiceType = CPedSoundSA::GetVoiceTypeNameFromID(sVoiceType); + if (pszVoice) + *pszVoice = CPedSoundSA::GetVoiceNameFromID(sVoiceType, sVoiceID); +} + +void CModelInfoSA::SetVoice(short sVoiceType, short sVoiceID) +{ + GetPedModelInfoInterface()->sVoiceType = sVoiceType; + GetPedModelInfoInterface()->sFirstVoice = sVoiceID; + GetPedModelInfoInterface()->sLastVoice = sVoiceID; + GetPedModelInfoInterface()->sNextVoice = sVoiceID; +} + +void CModelInfoSA::SetVoice(const char* szVoiceType, const char* szVoice) +{ + short sVoiceType = CPedSoundSA::GetVoiceTypeIDFromName(szVoiceType); + if (sVoiceType < 0) + return; + short sVoiceID = CPedSoundSA::GetVoiceIDFromName(sVoiceType, szVoice); + if (sVoiceID < 0) + return; + SetVoice(sVoiceType, sVoiceID); +} + +void CModelInfoSA::MakePedModel(char* szTexture) +{ + // Create a new CPedModelInfo + CPedModelInfoSA pedModelInfo; + ppModelInfo[m_dwModelID] = (CBaseModelInfoSAInterface*)pedModelInfo.GetPedModelInfoInterface(); + + // Load our texture + pGame->GetStreaming()->RequestSpecialModel(m_dwModelID, szTexture, 0); +} + +void CModelInfoSA::DeallocateModel(void) +{ + Remove(); + ppModelInfo[m_dwModelID] = nullptr; +} +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for NodeNameStreamRead +// +// Ignore extra characters in dff frame name +// +////////////////////////////////////////////////////////////////////////////////////////// +__declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) +{ + // Calc sizes + const uint uiMaxBufferSize = 24; + uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); + uint uiAmountToSkip = uiSize - uiAmountToRead; + + // Read good bit + RwStreamRead(stream, pDest, uiAmountToRead); + pDest[uiAmountToRead] = 0; + + // Skip bad bit (this might not be required) + if (uiAmountToSkip > 0) + RwStreamSkip(stream, uiAmountToSkip); +} + +// Hook info +#define HOOKPOS_NodeNameStreamRead 0x072FA68 +#define HOOKSIZE_NodeNameStreamRead 15 +DWORD RETURN_NodeNameStreamRead = 0x072FA77; +void _declspec(naked) HOOK_NodeNameStreamRead() +{ + _asm + { + pushad + push edi + push esi + push ebx + call OnMY_NodeNameStreamRead + add esp, 4*3 + popad + + jmp RETURN_NodeNameStreamRead + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Setup hooks +// +////////////////////////////////////////////////////////////////////////////////////////// +void CModelInfoSA::StaticSetHooks() +{ + EZHookInstall(NodeNameStreamRead); +} + +// Recursive RwFrame children searching function +void CModelInfoSA::RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel) +{ + for (RwFrame* ret = parent->child; ret != NULL; ret = ret->next) + { + // recurse into the child + if (ret->child != NULL) + { + RwSetSupportedUpgrades(ret, dwModel); + } + SString strName = ret->szName; + // Spoiler + if (strName == "ug_bonnet") + { + m_ModelSupportedUpgrades.m_bBonnet = true; + } + else if (strName == "ug_bonnet_left") + { + m_ModelSupportedUpgrades.m_bBonnet_Left = true; + } + else if (strName == "ug_bonnet_left_dam") + { + m_ModelSupportedUpgrades.m_bBonnet_Left_dam = true; + } + else if (strName == "ug_bonnet_right") + { + m_ModelSupportedUpgrades.m_bBonnet_Right = true; + } + else if (strName == "ug_bonnet_right_dam") + { + m_ModelSupportedUpgrades.m_bBonnet_Right_dam = true; + } + // Spoiler + else if (strName == "ug_spoiler") + { + m_ModelSupportedUpgrades.m_bSpoiler = true; + } + else if (strName == "ug_spoiler_dam") + { + m_ModelSupportedUpgrades.m_bSpoiler_dam = true; + } + // Bonnet + else if (strName == "ug_lights") + { + m_ModelSupportedUpgrades.m_bLamps = true; + } + else if (strName == "ug_lights_dam") + { + m_ModelSupportedUpgrades.m_bLamps_dam = true; + } + // Roof + else if (strName == "ug_roof") + { + m_ModelSupportedUpgrades.m_bRoof = true; + } + // Side Skirt + else if (strName == "ug_wing_right") + { + m_ModelSupportedUpgrades.m_bSideSkirt_Right = true; + } + // Side Skirt + else if (strName == "ug_wing_left") + { + m_ModelSupportedUpgrades.m_bSideSkirt_Left = true; + } + // Exhaust + else if (strName == "exhaust_ok") + { + m_ModelSupportedUpgrades.m_bExhaust = true; + } + // Front bullbars + else if (strName == "ug_frontbullbar") + { + m_ModelSupportedUpgrades.m_bFrontBullbars = true; + } + // rear bullbars + else if (strName == "ug_backbullbar") + { + m_ModelSupportedUpgrades.m_bRearBullbars = true; + } + // Front bumper + else if (strName == "bump_front_dummy") + { + m_ModelSupportedUpgrades.m_bFrontBumper = true; + } + // Rear bumper + else if (strName == "bump_rear_dummy") + { + m_ModelSupportedUpgrades.m_bRearBumper = true; + } + // Rear bumper + else if (strName == "misc_c") + { + m_ModelSupportedUpgrades.m_bMisc = true; + } + } +} + +void CModelInfoSA::InitialiseSupportedUpgrades(RpClump* pClump) +{ + m_ModelSupportedUpgrades.Reset(); + RwFrame* pFrame = RpGetFrame(pClump); + RwSetSupportedUpgrades(pFrame, m_dwModelID); + m_ModelSupportedUpgrades.m_bInitialised = true; +} + +void CModelInfoSA::ResetSupportedUpgrades() +{ + m_ModelSupportedUpgrades.Reset(); +} + +void CModelInfoSA::SetObjectPropertiesGroup(unsigned short usNewGroup) +{ + unsigned short usOrgGroup = GetObjectPropertiesGroup(); + if (usOrgGroup == usNewGroup) + return; + + if (!MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID)) + MapSet(ms_OriginalObjectPropertiesGroups, m_dwModelID, usOrgGroup); + + GetInterface()->usDynamicIndex = usNewGroup; +} + +unsigned short CModelInfoSA::GetObjectPropertiesGroup() +{ + unsigned short usGroup = GetInterface()->usDynamicIndex; + if (usGroup == 0xFFFF) + usGroup = 0; + + return usGroup; +} + +void CModelInfoSA::RestoreObjectPropertiesGroup() +{ + unsigned short* usGroupInMap = MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID); + if (usGroupInMap) + { + GetInterface()->usDynamicIndex = *usGroupInMap; + MapRemove(ms_OriginalObjectPropertiesGroups, m_dwModelID); + } +} + +void CModelInfoSA::RestoreAllObjectsPropertiesGroups() +{ + for (const auto& pair : ms_OriginalObjectPropertiesGroups) + { + pGame->GetModelInfo(pair.first)->GetInterface()->usDynamicIndex = pair.second; + } + ms_OriginalObjectPropertiesGroups.clear(); +} + +eModelInfoType CModelInfoSA::GetModelType() +{ + return ((eModelInfoType(*)())m_pInterface->VFTBL->GetModelType)(); +} + +bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) +{ + bool isTowable = true; + + const bool isTowTruck = towingModel->GetModel() == 525; + const bool isTractor = towingModel->GetModel() == 531; + + if (IsTrain() || towingModel->IsTrain()) + { + // A train is never towing other vehicles. Trains are linked by other means + isTowable = false; + } + else if (isTowTruck || isTractor) + { + const bool isFarmTrailer = GetModel() == 610; + + // Tow truck (525) and tractor (531) can only tow certain vehicle types without issues + if (IsBoat() || IsBike() || IsBmx()) + { + isTowable = false; + } + else if (IsTrailer() && !(isTractor && isFarmTrailer)) + { + isTowable = false; + } + } + + return isTowable; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CModelInfoSA::ForceUnload +// +// 1. Unload the dff from memory +// 2. Unload the associated txd from memory +// 3. Cross fingers +// +// Why do we do this? +// Player model adds (seemingly) unnecessary refs +// (Will crash if anything is actually using the model or txd) +// +// Won't work if there is a custom model replacement +// +// Returns true if model was unloaded +// +////////////////////////////////////////////////////////////////////////////////////////// +bool CModelInfoSA::ForceUnload() +{ + CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface(); + + // Need to have at least one ref to delete pRwObject + if (pModelInfoSAInterface->usNumberOfRefs == 0 && pModelInfoSAInterface->pRwObject != NULL) + { + pModelInfoSAInterface->usNumberOfRefs++; + } + + // Keep removing refs from the model until is it gone + uint uiLimit = 100; + while (pModelInfoSAInterface->usNumberOfRefs > 0 && uiLimit--) + { + RemoveRef(); + } + + // Did it work? + if (pModelInfoSAInterface->usNumberOfRefs > 0 || pModelInfoSAInterface->pRwObject != NULL) + return false; + + // If success, then remove txd + ushort usTxdId = GetTextureDictionaryID(); + if (usTxdId) + pGame->GetRenderWare()->TxdForceUnload(usTxdId, true); + + return true; +} diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 3aac96c2a4b..4183427fdbe 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -1,385 +1,385 @@ -/***************************************************************************** - * - * PROJECT: Multi Theft Auto v1.0 - * LICENSE: See LICENSE in the top level directory - * FILE: game_sa/CModelInfoSA.h - * PURPOSE: Header file for entity model information handler class - * - * Multi Theft Auto is available from http://www.multitheftauto.com/ - * - *****************************************************************************/ - -#pragma once - -#include -#include - -#include "CColModelSA.h" -#include "CRenderWareSA.h" -class CPedModelInfoSA; -class CPedModelInfoSAInterface; - -#define RpGetFrame(__c) ((RwFrame*)(((RwObject *)(__c))->parent)) - -#define ARRAY_ModelLoaded 0x8E4CD0 // ##SA## - -#define FUNC_CStreaming__HasModelLoaded 0x4044C0 - -// CModelInfo/ARRAY_ModelInfo __thiscall to load/replace vehicle models -#define FUNC_LoadVehicleModel 0x4C95C0 -#define FUNC_LoadWeaponModel 0x4C9910 -#define FUNC_LoadPedModel 0x4C7340 - -#define DWORD_AtomicsReplacerModelID 0xB71840 -#define FUNC_AtomicsReplacer 0x537150 - -#define ARRAY_ModelInfo 0xA9B0C8 -#define CLASS_CText 0xC1B340 -#define FUNC_CText_Get 0x6A0050 -#define FUNC_GetModelFlags 0x4044E0 -#define FUNC_GetBoundingBox 0x4082F0 - -#define FUNC_RemoveRef 0x4C4BB0 -#define FUNC_IsBoatModel 0x4c5a70 -#define FUNC_IsCarModel 0x4c5aa0 -#define FUNC_IsTrainModel 0x4c5ad0 -#define FUNC_IsHeliModel 0x4c5b00 -#define FUNC_IsPlaneModel 0x4c5b30 -#define FUNC_IsBikeModel 0x4c5b60 -#define FUNC_IsFakePlaneModel 0x4c5b90 -#define FUNC_IsMonsterTruckModel 0x4c5bc0 -#define FUNC_IsQuadBikeModel 0x4c5bf0 -#define FUNC_IsBmxModel 0x4c5c20 -#define FUNC_IsTrailerModel 0x4c5c50 -#define FUNC_IsVehicleModelType 0x4c5c80 - -#define FUNC_RemoveModel 0x4089a0 -#define FUNC_FlushRequestList 0x40E4E0 - -#define FUNC_HasVehicleUpgradeLoaded 0x407820 -#define FUNC_RequestVehicleUpgrade 0x408C70 - -#define FUNC_CVehicleModelInfo__GetNumRemaps 0x4C86B0 -#define FUNC_CVehicleStructure_delete 0x4C9580 - -#define FUNC_SetColModel 0x4C4BC0 -#define FUNC_AddPedModel 0x4c67a0 -#define VAR_CTempColModels_ModelPed1 0x968DF0 -/** - * \todo Fill this class with info from R* - */ -class CBaseModelInfo_SA_VTBL -{ -public: - DWORD Destructor; - DWORD AsAtomicModelInfoPtr; // () - DWORD AsDamageAtomicModelInfoPtr; // () - DWORD AsLodAtomicModelInfoPtr; // () - DWORD GetModelType; // () - Not defined in the base - DWORD GetTimeInfo; // () - DWORD Init; // () - DWORD Shutdown; // () - DWORD DeleteRwObject; // () - Not defined in the base - DWORD GetRwModelType; // () - Not defined in the base - DWORD CreateInstance_; // (RwMatrixTag*) - Not defined in the base - DWORD CreateInstance; // () - Not defined in the base - DWORD SetAnimFile; // (char const*) - DWORD ConvertAnimFileIndex; // () - DWORD GetAnimFileIndex; // () -}; - -class CAtomicModelInfo_SA_VTBL : public CBaseModelInfo_SA_VTBL -{ - DWORD SetAtomic; // (RpAtomic*) -}; - -class CDamageAtomicModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL -{ -}; - -class CLodAtomicModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL -{ -}; - -class CTimeModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL -{ -}; - -class CLodTimeModelInfo_SA_VTBL : public CLodAtomicModelInfo_SA_VTBL -{ -}; - -class CClumpModelInfo_SA_VTBL : public CBaseModelInfo_SA_VTBL -{ - DWORD SetClump; // (RpClump*) -}; - -class CWeaponModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL -{ -}; - -class CPedModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL -{ -}; - -class CVehicleModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL -{ -}; - -class CBaseModelInfoSAInterface -{ -public: - CBaseModelInfo_SA_VTBL* VFTBL; // +0 - - unsigned long ulHashKey; // +4 Generated by CKeyGen::GetUppercaseKey(char const *) called by CBaseModelInfo::SetModelName(char const *) - unsigned short usNumberOfRefs : 16; // +8 - unsigned short usTextureDictionary : 16; // +10 - unsigned char ucAlpha : 8; // +12 - - unsigned char ucNumOf2DEffects : 8; // +13 - unsigned short usUnknown : 16; // +14 Something with 2d effects - - unsigned short usDynamicIndex : 16; // +16 - - // Flags used by CBaseModelInfo - unsigned char bHasBeenPreRendered : 1; // +18 - unsigned char bAlphaTransparency : 1; - unsigned char bIsLod : 1; - unsigned char bDontWriteZBuffer : 1; - unsigned char bDontCastShadowsOn : 1; - unsigned char bDrawAdditive : 1; - unsigned char bDrawLast : 1; - unsigned char bDoWeOwnTheColModel : 1; - - unsigned char dwUnknownFlag25 : 1; // +19 - unsigned char dwUnknownFlag26 : 1; - unsigned char dwUnknownFlag27 : 1; - unsigned char bSwaysInWind : 1; - unsigned char bCollisionWasStreamedWithModel : 1; // CClumpModelInfo::SetCollisionWasStreamedWithModel(unsigned int) - unsigned char bDontCollideWithFlyer : 1; // CAtomicModelInfo::SetDontCollideWithFlyer(unsigned int) - unsigned char bHasComplexHierarchy : 1; // CClumpModelInfo::SetHasComplexHierarchy(unsigned int) - unsigned char bWetRoadReflection : 1; // CAtomicModelInfo::SetWetRoadReflection(unsigned int) - - CColModelSAInterface* pColModel; // +20 CColModel: public CBoundingBox - - float fLodDistanceUnscaled; // +24 Scaled is this value multiplied with flt_B6F118 - RwObject* pRwObject; // +28 - - // CWeaponModelInfo: - // +36 = Weapon info as int - - // CPedModelInfo: - // +36 = Motion anim group (AssocGroupID, long) - // +40 = Default ped type (long) - // +44 = Default ped stats (ePedStats) - // +48 = Can drive cars (byte) - // +50 = Ped flags (short) - // +52 = Hit col model (CColModel*) - // +56 = First radio station - // +57 = Second radio station - // +58 = Race (byte) - // +60 = Audio ped type (short) - // +62 = First voice - // +64 = Last voice - // +66 = Next voice (short) - - // CVehicleModelInfo: - // +36 = Custom plate material (RpMaterial*) - // +49 = Custom plate design (byte) - // +50 = Pointer to game name (const char*) - // +60 = Vehicle type (enum, int) - // +64 = Wheel scale (float). Front/rear? - // +68 = Wheel scale (float). Front/rear? - // +72 = Wheel model id - // +74 = Vehicle handling ID (word) - // +76 = Number of doors (byte) - // +77 = Vehicle list (byte) - // +78 = Vehicle flags (byte) - // +79 = Wheel upgrade class (byte) - // +80 = Number of times used (byte) - // +82 = Vehicle freq (short) - // +84 = Component rules mask (long) - // +88 = Steer angle - // +92 = Pointer to some class containing back seat position @ +60 probably dummy storage. - // +180 = Vehicle upgrade position descriptors array (32 bytes each) - // +720 = Number of possible colors - // +726 = Word array as referenced in CVehicleModelInfo::GetVehicleUpgrade(int) - // +762 = Array of WORD containing something relative to paintjobs - // +772 = Anim file index -}; - -class CVehicleModelVisualInfoSAInterface // Not sure about this name. If somebody knows more, please change -{ -public: - CVector vecDummies[15]; -}; - -class CVehicleModelInfoSAInterface : public CBaseModelInfoSAInterface -{ -public: - uint32 pad1; // +32 - RpMaterial* pPlateMaterial; // +36 - char plateText[8]; - char pad[2]; - char gameName[8]; - char pad2[2]; - unsigned int uiVehicleType; - float fWheelSizeFront; - float fWheelSizeRear; - short sWheelModel; - short sHandlingID; - byte ucNumDoors; - byte ucVehicleList; - byte ucVehicleFlags; - byte ucWheelUpgradeClass; - byte ucTimesUsed; - short sVehFrequency; - unsigned int uiComponentRules; - float fSteeringAngle; - CVehicleModelVisualInfoSAInterface* pVisualInfo; // +92 -}; - -enum eModelInfoType : unsigned char -{ - MODEL_INFO_TYPE_ATOMIC = 1, - MODEL_INFO_TYPE_TIME = 3, - MODEL_INFO_TYPE_WEAPON = 4, - MODEL_INFO_TYPE_CLUMP = 5, - MODEL_INFO_TYPE_VEHICLE = 6, - MODEL_INFO_TYPE_PED = 7, - MODEL_INFO_TYPE_LOD_ATOMIC = 8, -}; - -/** - * \todo Someone move GetLevelFromPosition out of here or delete it entirely please - */ - -class CModelInfoSA : public CModelInfo -{ -protected: - CBaseModelInfoSAInterface* m_pInterface; - DWORD m_dwModelID; - DWORD m_dwReferences; - DWORD m_dwPendingInterfaceRef; - CColModel* m_pCustomColModel; - CColModelSAInterface* m_pOriginalColModelInterface; - RpClump* m_pCustomClump; - static std::map ms_RestreamTxdIDMap; - static std::map ms_ModelDefaultLodDistanceMap; - static std::map ms_ModelDefaultAlphaTransparencyMap; - static std::unordered_map> ms_ModelDefaultDummiesPosition; - static std::map ms_ModelDefaultModelTimeInfo; - static std::unordered_map ms_OriginalObjectPropertiesGroups; - bool m_bAddedRefForCollision; - SVehicleSupportedUpgrades m_ModelSupportedUpgrades; - -public: - CModelInfoSA(); - CModelInfoSA(DWORD dwModelID); - - CBaseModelInfoSAInterface* GetInterface(); - CPedModelInfoSAInterface* GetPedModelInfoInterface() { return reinterpret_cast(GetInterface()); } - - DWORD GetModel() { return m_dwModelID; } - eModelInfoType GetModelType(); - uint GetAnimFileIndex(); - - bool IsPlayerModel(); - - BOOL IsBoat(); - BOOL IsCar(); - BOOL IsTrain(); - BOOL IsHeli(); - BOOL IsPlane(); - BOOL IsBike(); - BOOL IsFakePlane(); - BOOL IsMonsterTruck(); - BOOL IsQuadBike(); - BOOL IsBmx(); - BOOL IsTrailer(); - BOOL IsVehicle(); - BOOL IsUpgrade(); - - char* GetNameIfVehicle(); - - VOID Request(EModelRequestType requestType, const char* szTag); - VOID Remove(); - BYTE GetLevelFromPosition(CVector* vecPosition); - BOOL IsLoaded(); - BOOL DoIsLoaded(); - BYTE GetFlags(); - CBoundingBox* GetBoundingBox(); - bool IsValid(); - float GetDistanceFromCentreOfMassToBaseOfModel(); - unsigned short GetTextureDictionaryID(); - void SetTextureDictionaryID(unsigned short usID); - float GetLODDistance(); - void SetLODDistance(float fDistance); - static void StaticResetLodDistances(); - void RestreamIPL(); - static void StaticFlushPendingRestreamIPL(); - static void StaticSetHooks(); - bool GetTime(char& cHourOn, char& cHourOff); - bool SetTime(char cHourOn, char cHourOff); - static void StaticResetModelTimes(); - - void SetAlphaTransparencyEnabled(BOOL bEnabled); - bool IsAlphaTransparencyEnabled(); - void ResetAlphaTransparency(); - static void StaticResetAlphaTransparencies(); - - void ModelAddRef(EModelRequestType requestType, const char* szTag); - int GetRefCount(); - void RemoveRef(bool bRemoveExtraGTARef = false); - bool ForceUnload(); - - // CVehicleModelInfo specific - short GetAvailableVehicleMod(unsigned short usSlot); - bool IsUpgradeAvailable(eVehicleUpgradePosn posn); - void SetCustomCarPlateText(const char* szText); - unsigned int GetNumRemaps(); - void* GetVehicleSuspensionData(); - void* SetVehicleSuspensionData(void* pSuspensionLines); - CVector GetVehicleExhaustFumesPosition() override; - void SetVehicleExhaustFumesPosition(const CVector& vecPosition) override; - CVector GetVehicleDummyPosition(eVehicleDummies eDummy) override; - void SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) override; - static void ResetAllVehicleDummies(); - - // ONLY use for peds - void GetVoice(short* psVoiceType, short* psVoice); - void GetVoice(const char** pszVoiceType, const char** szVoice); - void SetVoice(short sVoiceType, short sVoice); - void SetVoice(const char* szVoiceType, const char* szVoice); - - // Custom collision related functions - void SetCustomModel(RpClump* pClump); - void RestoreOriginalModel(); - void SetColModel(CColModel* pColModel); - void RestoreColModel(); - void MakeCustomModel(); - - void SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; } - - RwObject* GetRwObject() { return m_pInterface ? m_pInterface->pRwObject : NULL; } - - // CModelInfoSA methods - void MakePedModel(char* szTexture); - void DeallocateModel(void); - - SVehicleSupportedUpgrades GetVehicleSupportedUpgrades() { return m_ModelSupportedUpgrades; } - - void InitialiseSupportedUpgrades(RpClump* pClump); - void ResetSupportedUpgrades(); - - void SetObjectPropertiesGroup(unsigned short usObjectGroup); - unsigned short GetObjectPropertiesGroup(); - void RestoreObjectPropertiesGroup(); - static void RestoreAllObjectsPropertiesGroups(); - - // Vehicle towing functions - bool IsTowableBy(CModelInfo* towingModel) override; - -private: - void RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel); -}; +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CModelInfoSA.h + * PURPOSE: Header file for entity model information handler class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include + +#include "CColModelSA.h" +#include "CRenderWareSA.h" +class CPedModelInfoSA; +class CPedModelInfoSAInterface; + +#define RpGetFrame(__c) ((RwFrame*)(((RwObject *)(__c))->parent)) + +#define ARRAY_ModelLoaded 0x8E4CD0 // ##SA## + +#define FUNC_CStreaming__HasModelLoaded 0x4044C0 + +// CModelInfo/ARRAY_ModelInfo __thiscall to load/replace vehicle models +#define FUNC_LoadVehicleModel 0x4C95C0 +#define FUNC_LoadWeaponModel 0x4C9910 +#define FUNC_LoadPedModel 0x4C7340 + +#define DWORD_AtomicsReplacerModelID 0xB71840 +#define FUNC_AtomicsReplacer 0x537150 + +#define ARRAY_ModelInfo 0xA9B0C8 +#define CLASS_CText 0xC1B340 +#define FUNC_CText_Get 0x6A0050 +#define FUNC_GetModelFlags 0x4044E0 +#define FUNC_GetBoundingBox 0x4082F0 + +#define FUNC_RemoveRef 0x4C4BB0 +#define FUNC_IsBoatModel 0x4c5a70 +#define FUNC_IsCarModel 0x4c5aa0 +#define FUNC_IsTrainModel 0x4c5ad0 +#define FUNC_IsHeliModel 0x4c5b00 +#define FUNC_IsPlaneModel 0x4c5b30 +#define FUNC_IsBikeModel 0x4c5b60 +#define FUNC_IsFakePlaneModel 0x4c5b90 +#define FUNC_IsMonsterTruckModel 0x4c5bc0 +#define FUNC_IsQuadBikeModel 0x4c5bf0 +#define FUNC_IsBmxModel 0x4c5c20 +#define FUNC_IsTrailerModel 0x4c5c50 +#define FUNC_IsVehicleModelType 0x4c5c80 + +#define FUNC_RemoveModel 0x4089a0 +#define FUNC_FlushRequestList 0x40E4E0 + +#define FUNC_HasVehicleUpgradeLoaded 0x407820 +#define FUNC_RequestVehicleUpgrade 0x408C70 + +#define FUNC_CVehicleModelInfo__GetNumRemaps 0x4C86B0 +#define FUNC_CVehicleStructure_delete 0x4C9580 + +#define FUNC_SetColModel 0x4C4BC0 +#define FUNC_AddPedModel 0x4c67a0 +#define VAR_CTempColModels_ModelPed1 0x968DF0 +/** + * \todo Fill this class with info from R* + */ +class CBaseModelInfo_SA_VTBL +{ +public: + DWORD Destructor; + DWORD AsAtomicModelInfoPtr; // () + DWORD AsDamageAtomicModelInfoPtr; // () + DWORD AsLodAtomicModelInfoPtr; // () + DWORD GetModelType; // () - Not defined in the base + DWORD GetTimeInfo; // () + DWORD Init; // () + DWORD Shutdown; // () + DWORD DeleteRwObject; // () - Not defined in the base + DWORD GetRwModelType; // () - Not defined in the base + DWORD CreateInstance_; // (RwMatrixTag*) - Not defined in the base + DWORD CreateInstance; // () - Not defined in the base + DWORD SetAnimFile; // (char const*) + DWORD ConvertAnimFileIndex; // () + DWORD GetAnimFileIndex; // () +}; + +class CAtomicModelInfo_SA_VTBL : public CBaseModelInfo_SA_VTBL +{ + DWORD SetAtomic; // (RpAtomic*) +}; + +class CDamageAtomicModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL +{ +}; + +class CLodAtomicModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL +{ +}; + +class CTimeModelInfo_SA_VTBL : public CAtomicModelInfo_SA_VTBL +{ +}; + +class CLodTimeModelInfo_SA_VTBL : public CLodAtomicModelInfo_SA_VTBL +{ +}; + +class CClumpModelInfo_SA_VTBL : public CBaseModelInfo_SA_VTBL +{ + DWORD SetClump; // (RpClump*) +}; + +class CWeaponModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL +{ +}; + +class CPedModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL +{ +}; + +class CVehicleModelInfo_SA_VTBL : public CClumpModelInfo_SA_VTBL +{ +}; + +class CBaseModelInfoSAInterface +{ +public: + CBaseModelInfo_SA_VTBL* VFTBL; // +0 + + unsigned long ulHashKey; // +4 Generated by CKeyGen::GetUppercaseKey(char const *) called by CBaseModelInfo::SetModelName(char const *) + unsigned short usNumberOfRefs : 16; // +8 + unsigned short usTextureDictionary : 16; // +10 + unsigned char ucAlpha : 8; // +12 + + unsigned char ucNumOf2DEffects : 8; // +13 + unsigned short usUnknown : 16; // +14 Something with 2d effects + + unsigned short usDynamicIndex : 16; // +16 + + // Flags used by CBaseModelInfo + unsigned char bHasBeenPreRendered : 1; // +18 + unsigned char bAlphaTransparency : 1; + unsigned char bIsLod : 1; + unsigned char bDontWriteZBuffer : 1; + unsigned char bDontCastShadowsOn : 1; + unsigned char bDrawAdditive : 1; + unsigned char bDrawLast : 1; + unsigned char bDoWeOwnTheColModel : 1; + + unsigned char dwUnknownFlag25 : 1; // +19 + unsigned char dwUnknownFlag26 : 1; + unsigned char dwUnknownFlag27 : 1; + unsigned char bSwaysInWind : 1; + unsigned char bCollisionWasStreamedWithModel : 1; // CClumpModelInfo::SetCollisionWasStreamedWithModel(unsigned int) + unsigned char bDontCollideWithFlyer : 1; // CAtomicModelInfo::SetDontCollideWithFlyer(unsigned int) + unsigned char bHasComplexHierarchy : 1; // CClumpModelInfo::SetHasComplexHierarchy(unsigned int) + unsigned char bWetRoadReflection : 1; // CAtomicModelInfo::SetWetRoadReflection(unsigned int) + + CColModelSAInterface* pColModel; // +20 CColModel: public CBoundingBox + + float fLodDistanceUnscaled; // +24 Scaled is this value multiplied with flt_B6F118 + RwObject* pRwObject; // +28 + + // CWeaponModelInfo: + // +36 = Weapon info as int + + // CPedModelInfo: + // +36 = Motion anim group (AssocGroupID, long) + // +40 = Default ped type (long) + // +44 = Default ped stats (ePedStats) + // +48 = Can drive cars (byte) + // +50 = Ped flags (short) + // +52 = Hit col model (CColModel*) + // +56 = First radio station + // +57 = Second radio station + // +58 = Race (byte) + // +60 = Audio ped type (short) + // +62 = First voice + // +64 = Last voice + // +66 = Next voice (short) + + // CVehicleModelInfo: + // +36 = Custom plate material (RpMaterial*) + // +49 = Custom plate design (byte) + // +50 = Pointer to game name (const char*) + // +60 = Vehicle type (enum, int) + // +64 = Wheel scale (float). Front/rear? + // +68 = Wheel scale (float). Front/rear? + // +72 = Wheel model id + // +74 = Vehicle handling ID (word) + // +76 = Number of doors (byte) + // +77 = Vehicle list (byte) + // +78 = Vehicle flags (byte) + // +79 = Wheel upgrade class (byte) + // +80 = Number of times used (byte) + // +82 = Vehicle freq (short) + // +84 = Component rules mask (long) + // +88 = Steer angle + // +92 = Pointer to some class containing back seat position @ +60 probably dummy storage. + // +180 = Vehicle upgrade position descriptors array (32 bytes each) + // +720 = Number of possible colors + // +726 = Word array as referenced in CVehicleModelInfo::GetVehicleUpgrade(int) + // +762 = Array of WORD containing something relative to paintjobs + // +772 = Anim file index +}; + +class CVehicleModelVisualInfoSAInterface // Not sure about this name. If somebody knows more, please change +{ +public: + CVector vecDummies[15]; +}; + +class CVehicleModelInfoSAInterface : public CBaseModelInfoSAInterface +{ +public: + uint32 pad1; // +32 + RpMaterial* pPlateMaterial; // +36 + char plateText[8]; + char pad[2]; + char gameName[8]; + char pad2[2]; + unsigned int uiVehicleType; + float fWheelSizeFront; + float fWheelSizeRear; + short sWheelModel; + short sHandlingID; + byte ucNumDoors; + byte ucVehicleList; + byte ucVehicleFlags; + byte ucWheelUpgradeClass; + byte ucTimesUsed; + short sVehFrequency; + unsigned int uiComponentRules; + float fSteeringAngle; + CVehicleModelVisualInfoSAInterface* pVisualInfo; // +92 +}; + +enum eModelInfoType : unsigned char +{ + MODEL_INFO_TYPE_ATOMIC = 1, + MODEL_INFO_TYPE_TIME = 3, + MODEL_INFO_TYPE_WEAPON = 4, + MODEL_INFO_TYPE_CLUMP = 5, + MODEL_INFO_TYPE_VEHICLE = 6, + MODEL_INFO_TYPE_PED = 7, + MODEL_INFO_TYPE_LOD_ATOMIC = 8, +}; + +/** + * \todo Someone move GetLevelFromPosition out of here or delete it entirely please + */ + +class CModelInfoSA : public CModelInfo +{ +protected: + CBaseModelInfoSAInterface* m_pInterface; + DWORD m_dwModelID; + DWORD m_dwReferences; + DWORD m_dwPendingInterfaceRef; + CColModel* m_pCustomColModel; + CColModelSAInterface* m_pOriginalColModelInterface; + RpClump* m_pCustomClump; + static std::map ms_RestreamTxdIDMap; + static std::map ms_ModelDefaultLodDistanceMap; + static std::map ms_ModelDefaultAlphaTransparencyMap; + static std::unordered_map> ms_ModelDefaultDummiesPosition; + static std::map ms_ModelDefaultModelTimeInfo; + static std::unordered_map ms_OriginalObjectPropertiesGroups; + bool m_bAddedRefForCollision; + SVehicleSupportedUpgrades m_ModelSupportedUpgrades; + +public: + CModelInfoSA(); + CModelInfoSA(DWORD dwModelID); + + CBaseModelInfoSAInterface* GetInterface(); + CPedModelInfoSAInterface* GetPedModelInfoInterface() { return reinterpret_cast(GetInterface()); } + + DWORD GetModel() { return m_dwModelID; } + eModelInfoType GetModelType(); + uint GetAnimFileIndex(); + + bool IsPlayerModel(); + + BOOL IsBoat(); + BOOL IsCar(); + BOOL IsTrain(); + BOOL IsHeli(); + BOOL IsPlane(); + BOOL IsBike(); + BOOL IsFakePlane(); + BOOL IsMonsterTruck(); + BOOL IsQuadBike(); + BOOL IsBmx(); + BOOL IsTrailer(); + BOOL IsVehicle(); + BOOL IsUpgrade(); + + char* GetNameIfVehicle(); + + VOID Request(EModelRequestType requestType, const char* szTag); + VOID Remove(); + BYTE GetLevelFromPosition(CVector* vecPosition); + BOOL IsLoaded(); + BOOL DoIsLoaded(); + BYTE GetFlags(); + CBoundingBox* GetBoundingBox(); + bool IsValid(); + float GetDistanceFromCentreOfMassToBaseOfModel(); + unsigned short GetTextureDictionaryID(); + void SetTextureDictionaryID(unsigned short usID); + float GetLODDistance(); + void SetLODDistance(float fDistance); + static void StaticResetLodDistances(); + void RestreamIPL(); + static void StaticFlushPendingRestreamIPL(); + static void StaticSetHooks(); + bool GetTime(char& cHourOn, char& cHourOff); + bool SetTime(char cHourOn, char cHourOff); + static void StaticResetModelTimes(); + + void SetAlphaTransparencyEnabled(BOOL bEnabled); + bool IsAlphaTransparencyEnabled(); + void ResetAlphaTransparency(); + static void StaticResetAlphaTransparencies(); + + void ModelAddRef(EModelRequestType requestType, const char* szTag); + int GetRefCount(); + void RemoveRef(bool bRemoveExtraGTARef = false); + bool ForceUnload(); + + // CVehicleModelInfo specific + short GetAvailableVehicleMod(unsigned short usSlot); + bool IsUpgradeAvailable(eVehicleUpgradePosn posn); + void SetCustomCarPlateText(const char* szText); + unsigned int GetNumRemaps(); + void* GetVehicleSuspensionData(); + void* SetVehicleSuspensionData(void* pSuspensionLines); + CVector GetVehicleExhaustFumesPosition() override; + void SetVehicleExhaustFumesPosition(const CVector& vecPosition) override; + CVector GetVehicleDummyPosition(eVehicleDummies eDummy) override; + void SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) override; + static void ResetAllVehicleDummies(); + + // ONLY use for peds + void GetVoice(short* psVoiceType, short* psVoice); + void GetVoice(const char** pszVoiceType, const char** szVoice); + void SetVoice(short sVoiceType, short sVoice); + void SetVoice(const char* szVoiceType, const char* szVoice); + + // Custom collision related functions + void SetCustomModel(RpClump* pClump); + void RestoreOriginalModel(); + void SetColModel(CColModel* pColModel); + void RestoreColModel(); + void MakeCustomModel(); + + void SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; } + + RwObject* GetRwObject() { return m_pInterface ? m_pInterface->pRwObject : NULL; } + + // CModelInfoSA methods + void MakePedModel(char* szTexture); + void DeallocateModel(void); + + SVehicleSupportedUpgrades GetVehicleSupportedUpgrades() { return m_ModelSupportedUpgrades; } + + void InitialiseSupportedUpgrades(RpClump* pClump); + void ResetSupportedUpgrades(); + + void SetObjectPropertiesGroup(unsigned short usObjectGroup); + unsigned short GetObjectPropertiesGroup(); + void RestoreObjectPropertiesGroup(); + static void RestoreAllObjectsPropertiesGroups(); + + // Vehicle towing functions + bool IsTowableBy(CModelInfo* towingModel) override; + +private: + void RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel); +}; From a36c94e84d5f6a353e3b6732deec1eba7dd80c4a Mon Sep 17 00:00:00 2001 From: Qais Patankar Date: Tue, 14 Jan 2020 17:12:13 +0000 Subject: [PATCH 10/12] throw errors if invalid --- .../deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 08a1294998c..391e3735129 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -231,7 +231,7 @@ int CLuaEngineDefs::EngineLoadDFF(lua_State* luaVM) } SString filePath; - + if (bIsRawData || CResourceManager::ParseResourcePathInput(input, pResource, &filePath)) { // Grab the resource root entity @@ -1099,7 +1099,7 @@ int CLuaEngineDefs::EngineSetModelVisibleTime(lua_State* luaVM) } } else - m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + luaL_error(luaVM, argStream.GetFullErrorMessage()); // Failed lua_pushboolean(luaVM, false); @@ -1136,7 +1136,7 @@ int CLuaEngineDefs::EngineGetModelVisibleTime(lua_State* luaVM) } } else - m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + luaL_error(luaVM, argStream.GetFullErrorMessage()); // Failed lua_pushboolean(luaVM, false); @@ -1568,7 +1568,7 @@ int CLuaEngineDefs::EngineGetModelPhysicalPropertiesGroup(lua_State* luaVM) } argStream.SetCustomError("Expected valid model ID at argument 1"); } - + return luaL_error(luaVM, argStream.GetFullErrorMessage()); } @@ -1605,7 +1605,7 @@ int CLuaEngineDefs::EngineSetModelPhysicalPropertiesGroup(lua_State* luaVM) } argStream.SetCustomError("Expected valid model ID at argument 1"); } - + return luaL_error(luaVM, argStream.GetFullErrorMessage()); } @@ -1634,7 +1634,7 @@ int CLuaEngineDefs::EngineRestoreModelPhysicalPropertiesGroup(lua_State* luaVM) } argStream.SetCustomError("Expected valid model ID at argument 1"); } - + return luaL_error(luaVM, argStream.GetFullErrorMessage()); } @@ -1818,7 +1818,7 @@ int CLuaEngineDefs::EngineSetObjectGroupPhysicalProperty(lua_State* luaVM) } } } - + return luaL_error(luaVM, argStream.GetFullErrorMessage()); } From bfcfc069cc507057a72084b9247812aae88d7fd7 Mon Sep 17 00:00:00 2001 From: Qais Patankar Date: Tue, 14 Jan 2020 17:15:49 +0000 Subject: [PATCH 11/12] error earlier instead --- Client/game_sa/CModelInfoSA.cpp | 49 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 63350b18d96..f5a56cc04f9 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -566,37 +566,34 @@ float CModelInfoSA::GetLODDistance() bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) { m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (pTime) - { - if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) - { - MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); - } - pTime->m_nTimeOn = cHourOn; - pTime->m_nTimeOff = cHourOff; - return true; - } - } - return false; + if (!m_pInterface) + return false; + + TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (!pTime) + return false; + + if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) + MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); + + pTime->m_nTimeOn = cHourOn; + pTime->m_nTimeOff = cHourOff; + return true; } bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) { m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (time) - { - cHourOn = time->m_nTimeOn; - cHourOff = time->m_nTimeOff; - return true; - } - } - return false; + if (!m_pInterface) + return false; + + TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (!time) + return false; + + cHourOn = time->m_nTimeOn; + cHourOff = time->m_nTimeOff; + return true; } void CModelInfoSA::StaticResetModelTimes() From 3fac4b32c593051070cde0837f67f074ce707008 Mon Sep 17 00:00:00 2001 From: Qais Patankar Date: Tue, 21 Jan 2020 16:01:06 +0000 Subject: [PATCH 12/12] crlf Client/game_sa/CModelInfoSA.cpp --- Client/game_sa/CModelInfoSA.cpp | 3114 +++++++++++++++---------------- 1 file changed, 1557 insertions(+), 1557 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index df284ef908f..841786d7241 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1,1557 +1,1557 @@ -/***************************************************************************** - * - * PROJECT: Multi Theft Auto v1.0 - * LICENSE: See LICENSE in the top level directory - * FILE: game_sa/CModelInfoSA.cpp - * PURPOSE: Entity model information handler - * - * Multi Theft Auto is available from http://www.multitheftauto.com/ - * - *****************************************************************************/ - -#include "StdInc.h" -#include "gamesa_renderware.h" - -extern CGameSA* pGame; - -CBaseModelInfoSAInterface** ppModelInfo = (CBaseModelInfoSAInterface**)ARRAY_ModelInfo; - -std::map CModelInfoSA::ms_RestreamTxdIDMap; -std::map CModelInfoSA::ms_ModelDefaultLodDistanceMap; -std::map CModelInfoSA::ms_ModelDefaultAlphaTransparencyMap; -std::unordered_map> CModelInfoSA::ms_ModelDefaultDummiesPosition; -std::map CModelInfoSA::ms_ModelDefaultModelTimeInfo; -std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; - -CModelInfoSA::CModelInfoSA() -{ - m_pInterface = NULL; - this->m_dwModelID = 0xFFFFFFFF; - m_dwReferences = 0; - m_dwPendingInterfaceRef = 0; - m_pOriginalColModelInterface = NULL; - m_pCustomClump = NULL; - m_pCustomColModel = NULL; - m_bAddedRefForCollision = false; -} - -CModelInfoSA::CModelInfoSA(DWORD dwModelID) -{ - this->m_dwModelID = dwModelID; - m_pInterface = ppModelInfo[m_dwModelID]; - m_dwReferences = 0; - m_dwPendingInterfaceRef = 0; - m_pOriginalColModelInterface = NULL; - m_pCustomClump = NULL; - m_pCustomColModel = NULL; - m_bAddedRefForCollision = false; -} - -CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() -{ - return m_pInterface = ppModelInfo[m_dwModelID]; -} - -BOOL CModelInfoSA::IsBoat() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBoat ( )"); - DWORD dwFunction = FUNC_IsBoatModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsCar() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsCar ( )"); - DWORD dwFunction = FUNC_IsCarModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsTrain() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsTrain ( )"); - DWORD dwFunction = FUNC_IsTrainModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsHeli() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsHeli ( )"); - DWORD dwFunction = FUNC_IsHeliModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsPlane() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsPlane ( )"); - DWORD dwFunction = FUNC_IsPlaneModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsBike() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBike ( )"); - DWORD dwFunction = FUNC_IsBikeModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsFakePlane() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsFakePlane ( )"); - DWORD dwFunction = FUNC_IsFakePlaneModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsMonsterTruck() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsMonsterTruck ( )"); - DWORD dwFunction = FUNC_IsMonsterTruckModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsQuadBike() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsQuadBike ( )"); - DWORD dwFunction = FUNC_IsQuadBikeModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsBmx() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsBmx ( )"); - DWORD dwFunction = FUNC_IsBmxModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsTrailer() -{ - DEBUG_TRACE("BOOL CModelInfoSA::IsTrailer ( )"); - DWORD dwFunction = FUNC_IsTrailerModel; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; -} - -BOOL CModelInfoSA::IsVehicle() -{ - /* - DEBUG_TRACE("BOOL CModelInfoSA::IsVehicle ( )"); - DWORD dwFunction = FUNC_IsVehicleModelType; - DWORD ModelID = m_dwModelID; - BYTE bReturn = 0; - _asm - { - push ModelID - call dwFunction - mov bReturn, al - add esp, 4 - } - return (BOOL)bReturn; - */ - - // Above doesn't seem to work - return m_dwModelID >= 400 && m_dwModelID <= 611; -} - -bool CModelInfoSA::IsPlayerModel() -{ - return (GetInterface() && GetInterface()->pColModel && GetInterface()->pColModel == (CColModelSAInterface*)VAR_CTempColModels_ModelPed1); -} - -BOOL CModelInfoSA::IsUpgrade() -{ - return m_dwModelID >= 1000 && m_dwModelID <= 1193; -} - -char* CModelInfoSA::GetNameIfVehicle() -{ - DEBUG_TRACE("char * CModelInfoSA::GetNameIfVehicle ( )"); - // if(this->IsVehicle()) - // { - DWORD dwModelInfo = ARRAY_ModelInfo; - DWORD dwFunc = FUNC_CText_Get; - DWORD ModelID = m_dwModelID; - DWORD dwReturn = 0; - - _asm - { - push eax - push ebx - push ecx - - mov ebx, ModelID - lea ebx, [ebx*4] - add ebx, ARRAY_ModelInfo - mov eax, [ebx] - add eax, 50 - - push eax - mov ecx, CLASS_CText - call dwFunc - - mov dwReturn, eax - - pop ecx - pop ebx - pop eax - } - return (char*)dwReturn; - // } - // return NULL; -} - -uint CModelInfoSA::GetAnimFileIndex() -{ - DWORD dwFunc = m_pInterface->VFTBL->GetAnimFileIndex; - DWORD dwThis = (DWORD)m_pInterface; - uint uiReturn = 0; - if (dwFunc) - { - _asm - { - mov ecx, dwThis - call dwFunc - mov uiReturn, eax - } - } - return uiReturn; -} - -VOID CModelInfoSA::Request(EModelRequestType requestType, const char* szTag) -{ - DEBUG_TRACE("VOID CModelInfoSA::Request( BOOL bAndLoad, BOOL bWaitForLoad )"); - // don't bother loading it if it already is - if (IsLoaded()) - return; - - if (m_dwModelID <= 288 && m_dwModelID != 7 && !pGame->GetModelInfo(7)->IsLoaded()) - { - // Skin 7 must be loaded in order for other skins to work. No, really. (#4010) - pGame->GetModelInfo(7)->Request(requestType, "Model 7"); - } - - // Bikes can sometimes get stuck when loading unless the anim file is handled like what is does here - // Don't change the code below unless you can test it (by recreating the problem it solves) - if (IsVehicle()) - { - uint uiAnimFileIndex = GetAnimFileIndex(); - if (uiAnimFileIndex != 0xffffffff) - { - uint uiAnimId = uiAnimFileIndex + 25575; - CModelInfoSA* pAnim = static_cast(pGame->GetModelInfo(uiAnimId)); - if (!pAnim) - { - if (uiAnimId != 25714) - LogEvent(505, "Model no anim", "", SString("%d (%d)", m_dwModelID, uiAnimId)); - } - else if (!pAnim->IsLoaded()) - { - OutputDebugLine(SString("[Models] Requesting anim file %d for model %d", uiAnimId, m_dwModelID)); - pAnim->Request(requestType, szTag); - } - } - } - - if (requestType == BLOCKING) - { - pGame->GetStreaming()->RequestModel(m_dwModelID, 0x16); - pGame->GetStreaming()->LoadAllRequestedModels(true, szTag); - if (!IsLoaded()) - { - // Try 3 more times, final time without high priority flag - int iCount = 0; - while (iCount++ < 10 && !IsLoaded()) - { - bool bOnlyPriorityModels = (iCount < 3 || iCount & 1); - pGame->GetStreaming()->LoadAllRequestedModels(bOnlyPriorityModels, szTag); - } - if (!IsLoaded()) - { - AddReportLog(6641, SString("Blocking load fail: %d (%s)", m_dwModelID, szTag)); - LogEvent(641, "Blocking load fail", "", SString("%d (%s)", m_dwModelID, szTag)); - dassert(0); - } - else - { - AddReportLog(6642, SString("Blocking load: %d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); - LogEvent(642, "Blocking load", "", SString("%d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); - } - } - } - else - { - pGame->GetStreaming()->RequestModel(m_dwModelID, 0x06); - } -} - -VOID CModelInfoSA::Remove() -{ - DEBUG_TRACE("VOID CModelInfoSA::Remove ( )"); - - // Don't remove if GTA refers to it somehow. - // Or we'll screw up SA's map for example. - - m_pInterface = ppModelInfo[m_dwModelID]; - - // Remove ref added for collision - if (m_bAddedRefForCollision) - { - m_bAddedRefForCollision = false; - if (m_pInterface->usNumberOfRefs > 0) - m_pInterface->usNumberOfRefs--; - } - - // Remove our reference - if (m_pInterface->usNumberOfRefs > 0) - m_pInterface->usNumberOfRefs--; - - // No references left? - if (m_pInterface->usNumberOfRefs == 0) - { - // We have a custom model? - if (m_pCustomClump) - { - // Mark us as unloaded. We manage the clump unloading. - // BYTE *ModelLoaded = (BYTE*)ARRAY_ModelLoaded; - // ModelLoaded[(m_dwModelID+m_dwModelID*4)<<2] = 0; - } - else - { - // Make our collision model original again before we unload. - RestoreColModel(); - - // Remove the model. - DWORD dwFunction = FUNC_RemoveModel; - DWORD ModelID = m_dwModelID; - _asm - { - push ModelID - call dwFunction - add esp, 4 - } - } - } -} - -BYTE CModelInfoSA::GetLevelFromPosition(CVector* vecPosition) -{ - DEBUG_TRACE("BYTE CModelInfoSA::GetLevelFromPosition ( CVector * vecPosition )"); - DWORD dwFunction = FUNC_GetLevelFromPosition; - BYTE bReturn = 0; - _asm - { - push vecPosition - call dwFunction - add esp, 4 - mov bReturn, al - } - return bReturn; -} - -BOOL CModelInfoSA::IsLoaded() -{ - if (DoIsLoaded()) - { - if (m_dwPendingInterfaceRef) - { - assert(m_dwReferences > 0); - m_pInterface = ppModelInfo[m_dwModelID]; - m_pInterface->usNumberOfRefs++; - m_dwPendingInterfaceRef = 0; - } - return true; - } - return false; -} - -BOOL CModelInfoSA::DoIsLoaded() -{ - DEBUG_TRACE("BOOL CModelInfoSA::DoIsLoaded ( )"); - - // return (BOOL)*(BYTE *)(ARRAY_ModelLoaded + 20*dwModelID); - BOOL bLoaded = pGame->GetStreaming()->HasModelLoaded(m_dwModelID); - - if (m_dwModelID < 20000) - { - m_pInterface = ppModelInfo[m_dwModelID]; - - if (bLoaded) - { - // Check rw object is there - if (!m_pInterface || !m_pInterface->pRwObject) - return false; - } - } - return bLoaded; -} - -BYTE CModelInfoSA::GetFlags() -{ - DWORD dwFunc = FUNC_GetModelFlags; - DWORD ModelID = m_dwModelID; - BYTE bFlags = 0; - _asm - { - push ModelID - call dwFunc - add esp, 4 - mov bFlags, al - } - return bFlags; -} - -CBoundingBox* CModelInfoSA::GetBoundingBox() -{ - DWORD dwFunc = FUNC_GetBoundingBox; - DWORD ModelID = m_dwModelID; - CBoundingBox* dwReturn = 0; - _asm - { - push ModelID - call dwFunc - add esp, 4 - mov dwReturn, eax - } - return dwReturn; -} - -bool CModelInfoSA::IsValid() -{ - if (m_dwModelID >= 20000 && m_dwModelID < MODELINFO_MAX) - return true; - return ppModelInfo[m_dwModelID] != 0; -} - -float CModelInfoSA::GetDistanceFromCentreOfMassToBaseOfModel() -{ - DWORD dwModelInfo = 0; - DWORD ModelID = m_dwModelID; - FLOAT fReturn = 0; - _asm - { - mov eax, ModelID - mov eax, ARRAY_ModelInfo[eax*4] - mov eax, [eax+20] - cmp eax, 0 - jz skip - fld [eax + 8] - fchs - fstp fReturn -skip: - } - return fReturn; -} - -unsigned short CModelInfoSA::GetTextureDictionaryID() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - return m_pInterface->usTextureDictionary; - - return 0; -} - -void CModelInfoSA::SetTextureDictionaryID(unsigned short usID) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - m_pInterface->usTextureDictionary = usID; -} - -float CModelInfoSA::GetLODDistance() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - return m_pInterface->fLodDistanceUnscaled; - - return 0.0f; -} - -bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (!m_pInterface) - return false; - - TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (!pTime) - return false; - - if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) - MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); - - pTime->m_nTimeOn = cHourOn; - pTime->m_nTimeOff = cHourOff; - return true; -} - -bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (!m_pInterface) - return false; - - TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); - if (!time) - return false; - - cHourOn = time->m_nTimeOn; - cHourOff = time->m_nTimeOff; - return true; -} - -void CModelInfoSA::StaticResetModelTimes() -{ - // Restore default values - for (std::map::const_iterator iter = ms_ModelDefaultModelTimeInfo.begin(); iter != ms_ModelDefaultModelTimeInfo.end(); ++iter) - { - iter->first->m_nTimeOn = iter->second->m_nTimeOn; - iter->first->m_nTimeOff = iter->second->m_nTimeOff; - } - - ms_ModelDefaultModelTimeInfo.clear(); -} - -float CModelInfoSA::GetOriginalLODDistance() -{ - // Return default LOD distance value (if doesn't exist, LOD distance hasn't been changed) - if (MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) - return MapGet(ms_ModelDefaultLodDistanceMap, m_dwModelID); - - return 0.0f; -} - -void CModelInfoSA::SetLODDistance(float fDistance, bool bOverrideMaxDistance) -{ -#if 0 - // fLodDistanceUnscaled values: - // - // With the draw distance setting in GTA SP options menu set to maximum: - // 0 - 170 roughly correlates to a LOD distance of 0 - 300 game units - // 170 - 480 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in - // 490 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely - // - // With the draw distance setting in GTA SP options menu set to minimum: - // 0 - 325 roughly correlates to a LOD distance of 0 - 300 game units - // 340 - 960 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in - // 1000 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely - // - // So, to ensure the maximum draw distance with a working alpha fade-in, fLodDistanceUnscaled has to be - // no more than: 325 - (325-170) * draw_distance_setting - // - if (!bOverrideMaxDistance) { - // Change GTA draw distance value from 0.925 to 1.8 into 0 to 1 - float fDrawDistanceSetting = UnlerpClamped(0.925f, CSettingsSA().GetDrawDistance(), 1.8f); - - // Calc max setting allowed for fLodDistanceUnscaled to preserve alpha fade-in - float fMaximumValue = Lerp(325.f, fDrawDistanceSetting, 170.f); - - // Ensure fDistance is in range - fDistance = std::min(fDistance, fMaximumValue); - } -#endif - if (!bOverrideMaxDistance) { - // Limit to 325.f as it goes horrible after that - fDistance = std::min(fDistance, 325.f); - } - - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // Save default value if not done yet - if (!MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) - MapSet(ms_ModelDefaultLodDistanceMap, m_dwModelID, m_pInterface->fLodDistanceUnscaled); - m_pInterface->fLodDistanceUnscaled = fDistance; - } -} - -void CModelInfoSA::StaticResetLodDistances() -{ - // Restore default values - for (std::map::const_iterator iter = ms_ModelDefaultLodDistanceMap.begin(); iter != ms_ModelDefaultLodDistanceMap.end(); ++iter) - { - CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; - if (pInterface) - pInterface->fLodDistanceUnscaled = iter->second; - } - - ms_ModelDefaultLodDistanceMap.clear(); -} - -void CModelInfoSA::RestreamIPL() -{ - // IPLs should not contain peds, weapons, vehicles and vehicle upgrades - if (m_dwModelID > 611 && (m_dwModelID < 1000 || m_dwModelID > 1193)) - MapSet(ms_RestreamTxdIDMap, GetTextureDictionaryID(), 0); -} - -void CModelInfoSA::StaticFlushPendingRestreamIPL() -{ - if (ms_RestreamTxdIDMap.empty()) - return; - // This function restreams all instances of the model *that are from the default SA world (ipl)*. - // In other words, it does not affect elements created by MTA. - // It's mostly a reimplementation of SA's DeleteAllRwObjects, except that it filters by model ID. - - ((void (*)())FUNC_FlushRequestList)(); - - std::set removedModels; - - for (int i = 0; i < 2 * NUM_StreamSectorRows * NUM_StreamSectorCols; i++) - { - DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamSectors)[i]; - while (pSectorEntry) - { - CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; - - // Possible bug - pEntity seems to be invalid here occasionally - if (pEntity->vtbl->DeleteRwObject != 0x00534030) - { - // Log info - OutputDebugString(SString("Entity 0x%08x (with model %d) at ARRAY_StreamSectors[%d,%d] is invalid\n", pEntity, pEntity->m_nModelIndex, - i / 2 % NUM_StreamSectorRows, i / 2 / NUM_StreamSectorCols)); - // Assert in debug - #if MTA_DEBUG - assert(pEntity->vtbl->DeleteRwObject == 0x00534030); - #endif - pSectorEntry = (DWORD*)pSectorEntry[1]; - continue; - } - - if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) - { - if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) - { - _asm - { - mov ecx, pEntity - mov eax, [ecx] - call dword ptr [eax+20h] - } - removedModels.insert(pEntity->m_nModelIndex); - } - } - - pSectorEntry = (DWORD*)pSectorEntry[1]; - } - } - - for (int i = 0; i < NUM_StreamRepeatSectorRows * NUM_StreamRepeatSectorCols; i++) - { - DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamRepeatSectors)[3 * i + 2]; - while (pSectorEntry) - { - CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; - if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) - { - if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) - { - _asm - { - mov ecx, pEntity - mov eax, [ecx] - call dword ptr [eax+20h] - } - removedModels.insert(pEntity->m_nModelIndex); - } - } - pSectorEntry = (DWORD*)pSectorEntry[1]; - } - } - - ms_RestreamTxdIDMap.clear(); - - std::set::iterator it; - for (it = removedModels.begin(); it != removedModels.end(); it++) - { - ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(*it); - MemPut(ARRAY_ModelLoaded + 20 * (*it), 0); - } -} - -void CModelInfoSA::ModelAddRef(EModelRequestType requestType, const char* szTag) -{ - // Are we not loaded? - if (!IsLoaded()) - { - // Request it. Wait for it to load if we're asked to. - if (pGame && pGame->IsASyncLoadingEnabled()) - Request(requestType, szTag); - else - Request(BLOCKING, szTag); - } - - // Increment the references. - if (m_dwReferences == 0) - { - assert(!m_dwPendingInterfaceRef); - if (IsLoaded()) - { - m_pInterface = ppModelInfo[m_dwModelID]; - m_pInterface->usNumberOfRefs++; - } - else - m_dwPendingInterfaceRef = 1; - } - - m_dwReferences++; -} - -int CModelInfoSA::GetRefCount() -{ - return static_cast(m_dwReferences); -} - -void CModelInfoSA::RemoveRef(bool bRemoveExtraGTARef) -{ - // Decrement the references - if (m_dwReferences > 0) - m_dwReferences--; - - if (m_dwReferences == 0 && m_dwPendingInterfaceRef) - { - m_dwPendingInterfaceRef = 0; - return; - } - - // Handle extra ref if requested - if (bRemoveExtraGTARef) - { - // Remove ref added by GTA. - if (m_pInterface->usNumberOfRefs > 1) - { - DWORD dwFunction = FUNC_RemoveRef; - CBaseModelInfoSAInterface* pInterface = m_pInterface; - _asm - { - mov ecx, pInterface - call dwFunction - } - } - } - - // Unload it if 0 references left and we're not CJ model. - // And if we're loaded. - if (m_dwReferences == 0 && m_dwModelID != 0 && IsLoaded()) - { - Remove(); - } -} - -void CModelInfoSA::SetAlphaTransparencyEnabled(BOOL bEnabled) -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - if (!MapContains(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID)) - { - MapSet(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID, (BYTE)(m_pInterface->bAlphaTransparency)); - } - m_pInterface->bAlphaTransparency = bEnabled; - } -} - -bool CModelInfoSA::IsAlphaTransparencyEnabled() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - return m_pInterface->bAlphaTransparency; - } - return false; -} - -void CModelInfoSA::StaticResetAlphaTransparencies() -{ - for (std::map::const_iterator iter = ms_ModelDefaultAlphaTransparencyMap.begin(); iter != ms_ModelDefaultAlphaTransparencyMap.end(); iter++) - { - CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; - if (pInterface) - { - pInterface->bAlphaTransparency = iter->second; - } - } - - ms_ModelDefaultAlphaTransparencyMap.clear(); -} - -void CModelInfoSA::ResetAlphaTransparency() -{ - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - BYTE* pbEnabled = MapFind(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); - if (pbEnabled) - { - m_pInterface->bAlphaTransparency = *pbEnabled; - MapRemove(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); - } - } -} - -short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) -{ - short sreturn = -1; - if (usUpgrade >= 1000 && usUpgrade <= 1193) - { - DWORD ModelID = m_dwModelID; - _asm - { - mov eax, ModelID - movsx edx, usUpgrade - mov eax, ARRAY_ModelInfo[eax*4] - mov ax, [eax+edx*2+0x2D6] - mov sreturn, ax - } - } - return sreturn; -} - -bool CModelInfoSA::IsUpgradeAvailable(eVehicleUpgradePosn posn) -{ - bool bRet = false; - DWORD ModelID = m_dwModelID; - _asm - { - mov eax, ModelID - lea ecx, ARRAY_ModelInfo[eax*4] - - mov eax, posn - mov ecx, [ecx+0x5C] - shl eax, 5 - push esi - mov esi, [ecx+eax+0D0h] - xor edx, edx - test esi, esi - setnl dl - mov al, dl - pop esi - - mov bRet, al - } - return bRet; -} - -void CModelInfoSA::SetCustomCarPlateText(const char* szText) -{ - char* szStoredText; - DWORD ModelID = m_dwModelID; - _asm - { - push ecx - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - add ecx, 40 - mov szStoredText, ecx - pop ecx - } - - if (szText) strncpy(szStoredText, szText, 8); - else szStoredText[0] = 0; -} - -unsigned int CModelInfoSA::GetNumRemaps() -{ - DWORD dwFunc = FUNC_CVehicleModelInfo__GetNumRemaps; - DWORD ModelID = m_dwModelID; - unsigned int uiReturn = 0; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - call dwFunc - mov uiReturn, eax - } - return uiReturn; -} - -void* CModelInfoSA::GetVehicleSuspensionData() -{ - return GetInterface()->pColModel->pColData->pSuspensionLines; -} - -void* CModelInfoSA::SetVehicleSuspensionData(void* pSuspensionLines) -{ - CColDataSA* pColData = GetInterface()->pColModel->pColData; - void* pOrigSuspensionLines = pColData->pSuspensionLines; - pColData->pSuspensionLines = pSuspensionLines; - return pOrigSuspensionLines; -} - -CVector CModelInfoSA::GetVehicleExhaustFumesPosition() -{ - return GetVehicleDummyPosition(eVehicleDummies::EXHAUST); -} - -void CModelInfoSA::SetVehicleExhaustFumesPosition(const CVector& vecPosition) -{ - return SetVehicleDummyPosition(eVehicleDummies::EXHAUST, vecPosition); -} - -CVector CModelInfoSA::GetVehicleDummyPosition(eVehicleDummies eDummy) -{ - if (!IsVehicle()) - return CVector(); - - // Request model load right now if not loaded yet (#9897) - if (!IsLoaded()) - Request(BLOCKING, "GetVehicleDummyPosition"); - - auto pVehicleModel = reinterpret_cast(m_pInterface); - return pVehicleModel->pVisualInfo->vecDummies[eDummy]; -} - -void CModelInfoSA::SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) -{ - if (!IsVehicle()) - return; - - // Request model load right now if not loaded yet (#9897) - if (!IsLoaded()) - Request(BLOCKING, "SetVehicleDummyPosition"); - - // Store default position in map - auto pVehicleModel = reinterpret_cast(m_pInterface); - auto iter = ms_ModelDefaultDummiesPosition.find(pVehicleModel); - if (iter == ms_ModelDefaultDummiesPosition.end()) - { - ms_ModelDefaultDummiesPosition.insert({pVehicleModel, std::map()}); - // Increment this model references count, so we don't unload it before we have a chance to reset the positions - m_pInterface->usNumberOfRefs++; - } - - if (ms_ModelDefaultDummiesPosition[pVehicleModel].find(eDummy) == ms_ModelDefaultDummiesPosition[pVehicleModel].end()) - { - ms_ModelDefaultDummiesPosition[pVehicleModel][eDummy] = pVehicleModel->pVisualInfo->vecDummies[eDummy]; - } - - // Set dummy position - pVehicleModel->pVisualInfo->vecDummies[eDummy] = vecPosition; -} - -void CModelInfoSA::ResetAllVehicleDummies() -{ - for (auto& info : ms_ModelDefaultDummiesPosition) - { - CVehicleModelInfoSAInterface* pVehicleModel = info.first; - for (auto& dummy : ms_ModelDefaultDummiesPosition[pVehicleModel]) - { - // TODO: Find out why this is a nullptr, and fix underlying bug - if (pVehicleModel->pVisualInfo != nullptr) - pVehicleModel->pVisualInfo->vecDummies[dummy.first] = dummy.second; - } - ms_ModelDefaultDummiesPosition[pVehicleModel].clear(); - // Decrement reference counter, since we reverted all position changes, the model can be safely unloaded - info.first->usNumberOfRefs--; - } - ms_ModelDefaultDummiesPosition.clear(); -} - -void CModelInfoSA::SetCustomModel(RpClump* pClump) -{ - // Error - if (pClump == NULL) - return; - - // Store the custom clump - m_pCustomClump = pClump; - - // Replace the model if we're loaded. - if (IsLoaded()) - { - switch (GetModelType()) - { - case MODEL_INFO_TYPE_PED: - return pGame->GetRenderWare()->ReplacePedModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_WEAPON: - return pGame->GetRenderWare()->ReplaceWeaponModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_VEHICLE: - return pGame->GetRenderWare()->ReplaceVehicleModel(pClump, static_cast(m_dwModelID)); - case MODEL_INFO_TYPE_ATOMIC: - case MODEL_INFO_TYPE_LOD_ATOMIC: - case MODEL_INFO_TYPE_TIME: - return pGame->GetRenderWare()->ReplaceAllAtomicsInModel(pClump, static_cast(m_dwModelID)); - } - } -} - -void CModelInfoSA::RestoreOriginalModel() -{ - // Are we loaded? - if (IsLoaded()) - { - ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(static_cast(m_dwModelID)); - } - - // Reset the stored custom vehicle clump - m_pCustomClump = NULL; -} - -void CModelInfoSA::SetColModel(CColModel* pColModel) -{ - // Grab the interfaces - CColModelSAInterface* pColModelInterface = pColModel->GetInterface(); - - if (!m_bAddedRefForCollision) - { - // Prevent this model from unloading while we have custom collision - ModelAddRef(BLOCKING, "for collision"); - m_bAddedRefForCollision = true; - } - - // Should always be loaded at this point - - // Skip setting if already done - if (m_pCustomColModel == pColModel) - return; - - // Store the col model we set - m_pCustomColModel = pColModel; - - // Do the following only if we're loaded - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // If no collision model has been set before, store the original in case we want to restore it - if (!m_pOriginalColModelInterface) - m_pOriginalColModelInterface = m_pInterface->pColModel; - - // Apply some low-level hacks - pColModelInterface->level = 0xA9; - - // Call SetColModel - DWORD dwFunc = FUNC_SetColModel; - DWORD ModelID = m_dwModelID; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - push 1 - push pColModelInterface - call dwFunc - } - - // FUNC_SetColModel resets bDoWeOwnTheColModel - m_pInterface->bDoWeOwnTheColModel = false; - m_pInterface->bCollisionWasStreamedWithModel = false; - - // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) - DWORD func = 0x5B2C20; - _asm - { - push pColModelInterface - push ModelID - call func - add esp, 8 - } - - // Set some lighting for this collision if not already present - CColDataSA* pColData = pColModelInterface->pColData; - if (pColData) - { - for (uint i = 0; i < pColData->numColTriangles; i++) - { - CColTriangleSA* pTriangle = pColData->pColTriangles + i; - if (pTriangle->lighting.night == 0 && pTriangle->lighting.day == 0) - { - pTriangle->lighting.night = 1; - pTriangle->lighting.day = 12; - } - } - } - } -} - -void CModelInfoSA::RestoreColModel() -{ - // Are we loaded? - m_pInterface = ppModelInfo[m_dwModelID]; - if (m_pInterface) - { - // We only have to store if the collision model was set - // Also only if we have a col model set - if (m_pOriginalColModelInterface && m_pCustomColModel) - { - DWORD dwFunc = FUNC_SetColModel; - DWORD dwOriginalColModelInterface = (DWORD)m_pOriginalColModelInterface; - DWORD ModelID = m_dwModelID; - _asm - { - mov ecx, ModelID - mov ecx, ARRAY_ModelInfo[ecx*4] - push 1 - push dwOriginalColModelInterface - call dwFunc - } - - // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) - DWORD func = 0x5B2C20; - _asm - { - push dwOriginalColModelInterface - push ModelID - call func - add esp, 8 - } - // (IJs) Document this function some time - } - } - - // We currently have no custom model loaded - m_pCustomColModel = NULL; - - // Remove ref added for collision - if (m_bAddedRefForCollision) - { - m_bAddedRefForCollision = false; - RemoveRef(); - } -} - -void CModelInfoSA::MakeCustomModel() -{ - // We have a custom model? - if (m_pCustomClump) - { - SetCustomModel(m_pCustomClump); - } - - // Custom collision model is not NULL and it's different from the original? - if (m_pCustomColModel) - { - SetColModel(m_pCustomColModel); - } -} - -void CModelInfoSA::GetVoice(short* psVoiceType, short* psVoiceID) -{ - if (psVoiceType) - *psVoiceType = GetPedModelInfoInterface()->sVoiceType; - if (psVoiceID) - *psVoiceID = GetPedModelInfoInterface()->sFirstVoice; -} - -void CModelInfoSA::GetVoice(const char** pszVoiceType, const char** pszVoice) -{ - short sVoiceType, sVoiceID; - GetVoice(&sVoiceType, &sVoiceID); - if (pszVoiceType) - *pszVoiceType = CPedSoundSA::GetVoiceTypeNameFromID(sVoiceType); - if (pszVoice) - *pszVoice = CPedSoundSA::GetVoiceNameFromID(sVoiceType, sVoiceID); -} - -void CModelInfoSA::SetVoice(short sVoiceType, short sVoiceID) -{ - GetPedModelInfoInterface()->sVoiceType = sVoiceType; - GetPedModelInfoInterface()->sFirstVoice = sVoiceID; - GetPedModelInfoInterface()->sLastVoice = sVoiceID; - GetPedModelInfoInterface()->sNextVoice = sVoiceID; -} - -void CModelInfoSA::SetVoice(const char* szVoiceType, const char* szVoice) -{ - short sVoiceType = CPedSoundSA::GetVoiceTypeIDFromName(szVoiceType); - if (sVoiceType < 0) - return; - short sVoiceID = CPedSoundSA::GetVoiceIDFromName(sVoiceType, szVoice); - if (sVoiceID < 0) - return; - SetVoice(sVoiceType, sVoiceID); -} - -void CModelInfoSA::MakePedModel(char* szTexture) -{ - // Create a new CPedModelInfo - CPedModelInfoSA pedModelInfo; - ppModelInfo[m_dwModelID] = (CBaseModelInfoSAInterface*)pedModelInfo.GetPedModelInfoInterface(); - - // Load our texture - pGame->GetStreaming()->RequestSpecialModel(m_dwModelID, szTexture, 0); -} - -void CModelInfoSA::DeallocateModel(void) -{ - Remove(); - ppModelInfo[m_dwModelID] = nullptr; -} -////////////////////////////////////////////////////////////////////////////////////////// -// -// Hook for NodeNameStreamRead -// -// Ignore extra characters in dff frame name -// -////////////////////////////////////////////////////////////////////////////////////////// -__declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) -{ - // Calc sizes - const uint uiMaxBufferSize = 24; - uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); - uint uiAmountToSkip = uiSize - uiAmountToRead; - - // Read good bit - RwStreamRead(stream, pDest, uiAmountToRead); - pDest[uiAmountToRead] = 0; - - // Skip bad bit (this might not be required) - if (uiAmountToSkip > 0) - RwStreamSkip(stream, uiAmountToSkip); -} - -// Hook info -#define HOOKPOS_NodeNameStreamRead 0x072FA68 -#define HOOKSIZE_NodeNameStreamRead 15 -DWORD RETURN_NodeNameStreamRead = 0x072FA77; -void _declspec(naked) HOOK_NodeNameStreamRead() -{ - _asm - { - pushad - push edi - push esi - push ebx - call OnMY_NodeNameStreamRead - add esp, 4*3 - popad - - jmp RETURN_NodeNameStreamRead - } -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -// Setup hooks -// -////////////////////////////////////////////////////////////////////////////////////////// -void CModelInfoSA::StaticSetHooks() -{ - EZHookInstall(NodeNameStreamRead); -} - -// Recursive RwFrame children searching function -void CModelInfoSA::RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel) -{ - for (RwFrame* ret = parent->child; ret != NULL; ret = ret->next) - { - // recurse into the child - if (ret->child != NULL) - { - RwSetSupportedUpgrades(ret, dwModel); - } - SString strName = ret->szName; - // Spoiler - if (strName == "ug_bonnet") - { - m_ModelSupportedUpgrades.m_bBonnet = true; - } - else if (strName == "ug_bonnet_left") - { - m_ModelSupportedUpgrades.m_bBonnet_Left = true; - } - else if (strName == "ug_bonnet_left_dam") - { - m_ModelSupportedUpgrades.m_bBonnet_Left_dam = true; - } - else if (strName == "ug_bonnet_right") - { - m_ModelSupportedUpgrades.m_bBonnet_Right = true; - } - else if (strName == "ug_bonnet_right_dam") - { - m_ModelSupportedUpgrades.m_bBonnet_Right_dam = true; - } - // Spoiler - else if (strName == "ug_spoiler") - { - m_ModelSupportedUpgrades.m_bSpoiler = true; - } - else if (strName == "ug_spoiler_dam") - { - m_ModelSupportedUpgrades.m_bSpoiler_dam = true; - } - // Bonnet - else if (strName == "ug_lights") - { - m_ModelSupportedUpgrades.m_bLamps = true; - } - else if (strName == "ug_lights_dam") - { - m_ModelSupportedUpgrades.m_bLamps_dam = true; - } - // Roof - else if (strName == "ug_roof") - { - m_ModelSupportedUpgrades.m_bRoof = true; - } - // Side Skirt - else if (strName == "ug_wing_right") - { - m_ModelSupportedUpgrades.m_bSideSkirt_Right = true; - } - // Side Skirt - else if (strName == "ug_wing_left") - { - m_ModelSupportedUpgrades.m_bSideSkirt_Left = true; - } - // Exhaust - else if (strName == "exhaust_ok") - { - m_ModelSupportedUpgrades.m_bExhaust = true; - } - // Front bullbars - else if (strName == "ug_frontbullbar") - { - m_ModelSupportedUpgrades.m_bFrontBullbars = true; - } - // rear bullbars - else if (strName == "ug_backbullbar") - { - m_ModelSupportedUpgrades.m_bRearBullbars = true; - } - // Front bumper - else if (strName == "bump_front_dummy") - { - m_ModelSupportedUpgrades.m_bFrontBumper = true; - } - // Rear bumper - else if (strName == "bump_rear_dummy") - { - m_ModelSupportedUpgrades.m_bRearBumper = true; - } - // Rear bumper - else if (strName == "misc_c") - { - m_ModelSupportedUpgrades.m_bMisc = true; - } - } -} - -void CModelInfoSA::InitialiseSupportedUpgrades(RpClump* pClump) -{ - m_ModelSupportedUpgrades.Reset(); - RwFrame* pFrame = RpGetFrame(pClump); - RwSetSupportedUpgrades(pFrame, m_dwModelID); - m_ModelSupportedUpgrades.m_bInitialised = true; -} - -void CModelInfoSA::ResetSupportedUpgrades() -{ - m_ModelSupportedUpgrades.Reset(); -} - -void CModelInfoSA::SetObjectPropertiesGroup(unsigned short usNewGroup) -{ - unsigned short usOrgGroup = GetObjectPropertiesGroup(); - if (usOrgGroup == usNewGroup) - return; - - if (!MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID)) - MapSet(ms_OriginalObjectPropertiesGroups, m_dwModelID, usOrgGroup); - - GetInterface()->usDynamicIndex = usNewGroup; -} - -unsigned short CModelInfoSA::GetObjectPropertiesGroup() -{ - unsigned short usGroup = GetInterface()->usDynamicIndex; - if (usGroup == 0xFFFF) - usGroup = 0; - - return usGroup; -} - -void CModelInfoSA::RestoreObjectPropertiesGroup() -{ - unsigned short* usGroupInMap = MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID); - if (usGroupInMap) - { - GetInterface()->usDynamicIndex = *usGroupInMap; - MapRemove(ms_OriginalObjectPropertiesGroups, m_dwModelID); - } -} - -void CModelInfoSA::RestoreAllObjectsPropertiesGroups() -{ - for (const auto& pair : ms_OriginalObjectPropertiesGroups) - { - pGame->GetModelInfo(pair.first)->GetInterface()->usDynamicIndex = pair.second; - } - ms_OriginalObjectPropertiesGroups.clear(); -} - -eModelInfoType CModelInfoSA::GetModelType() -{ - return ((eModelInfoType(*)())m_pInterface->VFTBL->GetModelType)(); -} - -bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) -{ - bool isTowable = true; - - const bool isTowTruck = towingModel->GetModel() == 525; - const bool isTractor = towingModel->GetModel() == 531; - - if (IsTrain() || towingModel->IsTrain()) - { - // A train is never towing other vehicles. Trains are linked by other means - isTowable = false; - } - else if (isTowTruck || isTractor) - { - const bool isFarmTrailer = GetModel() == 610; - - // Tow truck (525) and tractor (531) can only tow certain vehicle types without issues - if (IsBoat() || IsBike() || IsBmx()) - { - isTowable = false; - } - else if (IsTrailer() && !(isTractor && isFarmTrailer)) - { - isTowable = false; - } - } - - return isTowable; -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -// CModelInfoSA::ForceUnload -// -// 1. Unload the dff from memory -// 2. Unload the associated txd from memory -// 3. Cross fingers -// -// Why do we do this? -// Player model adds (seemingly) unnecessary refs -// (Will crash if anything is actually using the model or txd) -// -// Won't work if there is a custom model replacement -// -// Returns true if model was unloaded -// -////////////////////////////////////////////////////////////////////////////////////////// -bool CModelInfoSA::ForceUnload() -{ - CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface(); - - // Need to have at least one ref to delete pRwObject - if (pModelInfoSAInterface->usNumberOfRefs == 0 && pModelInfoSAInterface->pRwObject != NULL) - { - pModelInfoSAInterface->usNumberOfRefs++; - } - - // Keep removing refs from the model until is it gone - uint uiLimit = 100; - while (pModelInfoSAInterface->usNumberOfRefs > 0 && uiLimit--) - { - RemoveRef(); - } - - // Did it work? - if (pModelInfoSAInterface->usNumberOfRefs > 0 || pModelInfoSAInterface->pRwObject != NULL) - return false; - - // If success, then remove txd - ushort usTxdId = GetTextureDictionaryID(); - if (usTxdId) - pGame->GetRenderWare()->TxdForceUnload(usTxdId, true); - - return true; -} +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CModelInfoSA.cpp + * PURPOSE: Entity model information handler + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "gamesa_renderware.h" + +extern CGameSA* pGame; + +CBaseModelInfoSAInterface** ppModelInfo = (CBaseModelInfoSAInterface**)ARRAY_ModelInfo; + +std::map CModelInfoSA::ms_RestreamTxdIDMap; +std::map CModelInfoSA::ms_ModelDefaultLodDistanceMap; +std::map CModelInfoSA::ms_ModelDefaultAlphaTransparencyMap; +std::unordered_map> CModelInfoSA::ms_ModelDefaultDummiesPosition; +std::map CModelInfoSA::ms_ModelDefaultModelTimeInfo; +std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; + +CModelInfoSA::CModelInfoSA() +{ + m_pInterface = NULL; + this->m_dwModelID = 0xFFFFFFFF; + m_dwReferences = 0; + m_dwPendingInterfaceRef = 0; + m_pOriginalColModelInterface = NULL; + m_pCustomClump = NULL; + m_pCustomColModel = NULL; + m_bAddedRefForCollision = false; +} + +CModelInfoSA::CModelInfoSA(DWORD dwModelID) +{ + this->m_dwModelID = dwModelID; + m_pInterface = ppModelInfo[m_dwModelID]; + m_dwReferences = 0; + m_dwPendingInterfaceRef = 0; + m_pOriginalColModelInterface = NULL; + m_pCustomClump = NULL; + m_pCustomColModel = NULL; + m_bAddedRefForCollision = false; +} + +CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() +{ + return m_pInterface = ppModelInfo[m_dwModelID]; +} + +BOOL CModelInfoSA::IsBoat() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBoat ( )"); + DWORD dwFunction = FUNC_IsBoatModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsCar() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsCar ( )"); + DWORD dwFunction = FUNC_IsCarModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsTrain() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsTrain ( )"); + DWORD dwFunction = FUNC_IsTrainModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsHeli() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsHeli ( )"); + DWORD dwFunction = FUNC_IsHeliModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsPlane() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsPlane ( )"); + DWORD dwFunction = FUNC_IsPlaneModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsBike() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBike ( )"); + DWORD dwFunction = FUNC_IsBikeModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsFakePlane() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsFakePlane ( )"); + DWORD dwFunction = FUNC_IsFakePlaneModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsMonsterTruck() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsMonsterTruck ( )"); + DWORD dwFunction = FUNC_IsMonsterTruckModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsQuadBike() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsQuadBike ( )"); + DWORD dwFunction = FUNC_IsQuadBikeModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsBmx() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsBmx ( )"); + DWORD dwFunction = FUNC_IsBmxModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsTrailer() +{ + DEBUG_TRACE("BOOL CModelInfoSA::IsTrailer ( )"); + DWORD dwFunction = FUNC_IsTrailerModel; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; +} + +BOOL CModelInfoSA::IsVehicle() +{ + /* + DEBUG_TRACE("BOOL CModelInfoSA::IsVehicle ( )"); + DWORD dwFunction = FUNC_IsVehicleModelType; + DWORD ModelID = m_dwModelID; + BYTE bReturn = 0; + _asm + { + push ModelID + call dwFunction + mov bReturn, al + add esp, 4 + } + return (BOOL)bReturn; + */ + + // Above doesn't seem to work + return m_dwModelID >= 400 && m_dwModelID <= 611; +} + +bool CModelInfoSA::IsPlayerModel() +{ + return (GetInterface() && GetInterface()->pColModel && GetInterface()->pColModel == (CColModelSAInterface*)VAR_CTempColModels_ModelPed1); +} + +BOOL CModelInfoSA::IsUpgrade() +{ + return m_dwModelID >= 1000 && m_dwModelID <= 1193; +} + +char* CModelInfoSA::GetNameIfVehicle() +{ + DEBUG_TRACE("char * CModelInfoSA::GetNameIfVehicle ( )"); + // if(this->IsVehicle()) + // { + DWORD dwModelInfo = ARRAY_ModelInfo; + DWORD dwFunc = FUNC_CText_Get; + DWORD ModelID = m_dwModelID; + DWORD dwReturn = 0; + + _asm + { + push eax + push ebx + push ecx + + mov ebx, ModelID + lea ebx, [ebx*4] + add ebx, ARRAY_ModelInfo + mov eax, [ebx] + add eax, 50 + + push eax + mov ecx, CLASS_CText + call dwFunc + + mov dwReturn, eax + + pop ecx + pop ebx + pop eax + } + return (char*)dwReturn; + // } + // return NULL; +} + +uint CModelInfoSA::GetAnimFileIndex() +{ + DWORD dwFunc = m_pInterface->VFTBL->GetAnimFileIndex; + DWORD dwThis = (DWORD)m_pInterface; + uint uiReturn = 0; + if (dwFunc) + { + _asm + { + mov ecx, dwThis + call dwFunc + mov uiReturn, eax + } + } + return uiReturn; +} + +VOID CModelInfoSA::Request(EModelRequestType requestType, const char* szTag) +{ + DEBUG_TRACE("VOID CModelInfoSA::Request( BOOL bAndLoad, BOOL bWaitForLoad )"); + // don't bother loading it if it already is + if (IsLoaded()) + return; + + if (m_dwModelID <= 288 && m_dwModelID != 7 && !pGame->GetModelInfo(7)->IsLoaded()) + { + // Skin 7 must be loaded in order for other skins to work. No, really. (#4010) + pGame->GetModelInfo(7)->Request(requestType, "Model 7"); + } + + // Bikes can sometimes get stuck when loading unless the anim file is handled like what is does here + // Don't change the code below unless you can test it (by recreating the problem it solves) + if (IsVehicle()) + { + uint uiAnimFileIndex = GetAnimFileIndex(); + if (uiAnimFileIndex != 0xffffffff) + { + uint uiAnimId = uiAnimFileIndex + 25575; + CModelInfoSA* pAnim = static_cast(pGame->GetModelInfo(uiAnimId)); + if (!pAnim) + { + if (uiAnimId != 25714) + LogEvent(505, "Model no anim", "", SString("%d (%d)", m_dwModelID, uiAnimId)); + } + else if (!pAnim->IsLoaded()) + { + OutputDebugLine(SString("[Models] Requesting anim file %d for model %d", uiAnimId, m_dwModelID)); + pAnim->Request(requestType, szTag); + } + } + } + + if (requestType == BLOCKING) + { + pGame->GetStreaming()->RequestModel(m_dwModelID, 0x16); + pGame->GetStreaming()->LoadAllRequestedModels(true, szTag); + if (!IsLoaded()) + { + // Try 3 more times, final time without high priority flag + int iCount = 0; + while (iCount++ < 10 && !IsLoaded()) + { + bool bOnlyPriorityModels = (iCount < 3 || iCount & 1); + pGame->GetStreaming()->LoadAllRequestedModels(bOnlyPriorityModels, szTag); + } + if (!IsLoaded()) + { + AddReportLog(6641, SString("Blocking load fail: %d (%s)", m_dwModelID, szTag)); + LogEvent(641, "Blocking load fail", "", SString("%d (%s)", m_dwModelID, szTag)); + dassert(0); + } + else + { + AddReportLog(6642, SString("Blocking load: %d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); + LogEvent(642, "Blocking load", "", SString("%d (%s) (Took %d attempts)", m_dwModelID, szTag, iCount)); + } + } + } + else + { + pGame->GetStreaming()->RequestModel(m_dwModelID, 0x06); + } +} + +VOID CModelInfoSA::Remove() +{ + DEBUG_TRACE("VOID CModelInfoSA::Remove ( )"); + + // Don't remove if GTA refers to it somehow. + // Or we'll screw up SA's map for example. + + m_pInterface = ppModelInfo[m_dwModelID]; + + // Remove ref added for collision + if (m_bAddedRefForCollision) + { + m_bAddedRefForCollision = false; + if (m_pInterface->usNumberOfRefs > 0) + m_pInterface->usNumberOfRefs--; + } + + // Remove our reference + if (m_pInterface->usNumberOfRefs > 0) + m_pInterface->usNumberOfRefs--; + + // No references left? + if (m_pInterface->usNumberOfRefs == 0) + { + // We have a custom model? + if (m_pCustomClump) + { + // Mark us as unloaded. We manage the clump unloading. + // BYTE *ModelLoaded = (BYTE*)ARRAY_ModelLoaded; + // ModelLoaded[(m_dwModelID+m_dwModelID*4)<<2] = 0; + } + else + { + // Make our collision model original again before we unload. + RestoreColModel(); + + // Remove the model. + DWORD dwFunction = FUNC_RemoveModel; + DWORD ModelID = m_dwModelID; + _asm + { + push ModelID + call dwFunction + add esp, 4 + } + } + } +} + +BYTE CModelInfoSA::GetLevelFromPosition(CVector* vecPosition) +{ + DEBUG_TRACE("BYTE CModelInfoSA::GetLevelFromPosition ( CVector * vecPosition )"); + DWORD dwFunction = FUNC_GetLevelFromPosition; + BYTE bReturn = 0; + _asm + { + push vecPosition + call dwFunction + add esp, 4 + mov bReturn, al + } + return bReturn; +} + +BOOL CModelInfoSA::IsLoaded() +{ + if (DoIsLoaded()) + { + if (m_dwPendingInterfaceRef) + { + assert(m_dwReferences > 0); + m_pInterface = ppModelInfo[m_dwModelID]; + m_pInterface->usNumberOfRefs++; + m_dwPendingInterfaceRef = 0; + } + return true; + } + return false; +} + +BOOL CModelInfoSA::DoIsLoaded() +{ + DEBUG_TRACE("BOOL CModelInfoSA::DoIsLoaded ( )"); + + // return (BOOL)*(BYTE *)(ARRAY_ModelLoaded + 20*dwModelID); + BOOL bLoaded = pGame->GetStreaming()->HasModelLoaded(m_dwModelID); + + if (m_dwModelID < 20000) + { + m_pInterface = ppModelInfo[m_dwModelID]; + + if (bLoaded) + { + // Check rw object is there + if (!m_pInterface || !m_pInterface->pRwObject) + return false; + } + } + return bLoaded; +} + +BYTE CModelInfoSA::GetFlags() +{ + DWORD dwFunc = FUNC_GetModelFlags; + DWORD ModelID = m_dwModelID; + BYTE bFlags = 0; + _asm + { + push ModelID + call dwFunc + add esp, 4 + mov bFlags, al + } + return bFlags; +} + +CBoundingBox* CModelInfoSA::GetBoundingBox() +{ + DWORD dwFunc = FUNC_GetBoundingBox; + DWORD ModelID = m_dwModelID; + CBoundingBox* dwReturn = 0; + _asm + { + push ModelID + call dwFunc + add esp, 4 + mov dwReturn, eax + } + return dwReturn; +} + +bool CModelInfoSA::IsValid() +{ + if (m_dwModelID >= 20000 && m_dwModelID < MODELINFO_MAX) + return true; + return ppModelInfo[m_dwModelID] != 0; +} + +float CModelInfoSA::GetDistanceFromCentreOfMassToBaseOfModel() +{ + DWORD dwModelInfo = 0; + DWORD ModelID = m_dwModelID; + FLOAT fReturn = 0; + _asm + { + mov eax, ModelID + mov eax, ARRAY_ModelInfo[eax*4] + mov eax, [eax+20] + cmp eax, 0 + jz skip + fld [eax + 8] + fchs + fstp fReturn +skip: + } + return fReturn; +} + +unsigned short CModelInfoSA::GetTextureDictionaryID() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + return m_pInterface->usTextureDictionary; + + return 0; +} + +void CModelInfoSA::SetTextureDictionaryID(unsigned short usID) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + m_pInterface->usTextureDictionary = usID; +} + +float CModelInfoSA::GetLODDistance() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + return m_pInterface->fLodDistanceUnscaled; + + return 0.0f; +} + +bool CModelInfoSA::SetTime(char cHourOn, char cHourOff) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + + TimeInfo* pTime = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (!pTime) + return false; + + if (!MapContains(ms_ModelDefaultModelTimeInfo, pTime)) + MapSet(ms_ModelDefaultModelTimeInfo, pTime, new TimeInfo(pTime->m_nTimeOn, pTime->m_nTimeOff, pTime->m_wOtherTimeModel)); + + pTime->m_nTimeOn = cHourOn; + pTime->m_nTimeOff = cHourOff; + return true; +} + +bool CModelInfoSA::GetTime(char& cHourOn, char& cHourOff) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + + TimeInfo* time = ((TimeInfo*(*)(void))m_pInterface->VFTBL->GetTimeInfo)(); + if (!time) + return false; + + cHourOn = time->m_nTimeOn; + cHourOff = time->m_nTimeOff; + return true; +} + +void CModelInfoSA::StaticResetModelTimes() +{ + // Restore default values + for (std::map::const_iterator iter = ms_ModelDefaultModelTimeInfo.begin(); iter != ms_ModelDefaultModelTimeInfo.end(); ++iter) + { + iter->first->m_nTimeOn = iter->second->m_nTimeOn; + iter->first->m_nTimeOff = iter->second->m_nTimeOff; + } + + ms_ModelDefaultModelTimeInfo.clear(); +} + +float CModelInfoSA::GetOriginalLODDistance() +{ + // Return default LOD distance value (if doesn't exist, LOD distance hasn't been changed) + if (MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) + return MapGet(ms_ModelDefaultLodDistanceMap, m_dwModelID); + + return 0.0f; +} + +void CModelInfoSA::SetLODDistance(float fDistance, bool bOverrideMaxDistance) +{ +#if 0 + // fLodDistanceUnscaled values: + // + // With the draw distance setting in GTA SP options menu set to maximum: + // 0 - 170 roughly correlates to a LOD distance of 0 - 300 game units + // 170 - 480 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in + // 490 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely + // + // With the draw distance setting in GTA SP options menu set to minimum: + // 0 - 325 roughly correlates to a LOD distance of 0 - 300 game units + // 340 - 960 sets the LOD distance to 300 game units and has a negative effect on the alpha fade-in + // 1000 - 1e7 sets the LOD distance to 300 game units and removes the alpha fade-in completely + // + // So, to ensure the maximum draw distance with a working alpha fade-in, fLodDistanceUnscaled has to be + // no more than: 325 - (325-170) * draw_distance_setting + // + if (!bOverrideMaxDistance) { + // Change GTA draw distance value from 0.925 to 1.8 into 0 to 1 + float fDrawDistanceSetting = UnlerpClamped(0.925f, CSettingsSA().GetDrawDistance(), 1.8f); + + // Calc max setting allowed for fLodDistanceUnscaled to preserve alpha fade-in + float fMaximumValue = Lerp(325.f, fDrawDistanceSetting, 170.f); + + // Ensure fDistance is in range + fDistance = std::min(fDistance, fMaximumValue); + } +#endif + if (!bOverrideMaxDistance) { + // Limit to 325.f as it goes horrible after that + fDistance = std::min(fDistance, 325.f); + } + + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // Save default value if not done yet + if (!MapContains(ms_ModelDefaultLodDistanceMap, m_dwModelID)) + MapSet(ms_ModelDefaultLodDistanceMap, m_dwModelID, m_pInterface->fLodDistanceUnscaled); + m_pInterface->fLodDistanceUnscaled = fDistance; + } +} + +void CModelInfoSA::StaticResetLodDistances() +{ + // Restore default values + for (std::map::const_iterator iter = ms_ModelDefaultLodDistanceMap.begin(); iter != ms_ModelDefaultLodDistanceMap.end(); ++iter) + { + CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; + if (pInterface) + pInterface->fLodDistanceUnscaled = iter->second; + } + + ms_ModelDefaultLodDistanceMap.clear(); +} + +void CModelInfoSA::RestreamIPL() +{ + // IPLs should not contain peds, weapons, vehicles and vehicle upgrades + if (m_dwModelID > 611 && (m_dwModelID < 1000 || m_dwModelID > 1193)) + MapSet(ms_RestreamTxdIDMap, GetTextureDictionaryID(), 0); +} + +void CModelInfoSA::StaticFlushPendingRestreamIPL() +{ + if (ms_RestreamTxdIDMap.empty()) + return; + // This function restreams all instances of the model *that are from the default SA world (ipl)*. + // In other words, it does not affect elements created by MTA. + // It's mostly a reimplementation of SA's DeleteAllRwObjects, except that it filters by model ID. + + ((void (*)())FUNC_FlushRequestList)(); + + std::set removedModels; + + for (int i = 0; i < 2 * NUM_StreamSectorRows * NUM_StreamSectorCols; i++) + { + DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamSectors)[i]; + while (pSectorEntry) + { + CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; + + // Possible bug - pEntity seems to be invalid here occasionally + if (pEntity->vtbl->DeleteRwObject != 0x00534030) + { + // Log info + OutputDebugString(SString("Entity 0x%08x (with model %d) at ARRAY_StreamSectors[%d,%d] is invalid\n", pEntity, pEntity->m_nModelIndex, + i / 2 % NUM_StreamSectorRows, i / 2 / NUM_StreamSectorCols)); + // Assert in debug + #if MTA_DEBUG + assert(pEntity->vtbl->DeleteRwObject == 0x00534030); + #endif + pSectorEntry = (DWORD*)pSectorEntry[1]; + continue; + } + + if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) + { + if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) + { + _asm + { + mov ecx, pEntity + mov eax, [ecx] + call dword ptr [eax+20h] + } + removedModels.insert(pEntity->m_nModelIndex); + } + } + + pSectorEntry = (DWORD*)pSectorEntry[1]; + } + } + + for (int i = 0; i < NUM_StreamRepeatSectorRows * NUM_StreamRepeatSectorCols; i++) + { + DWORD* pSectorEntry = ((DWORD**)ARRAY_StreamRepeatSectors)[3 * i + 2]; + while (pSectorEntry) + { + CEntitySAInterface* pEntity = (CEntitySAInterface*)pSectorEntry[0]; + if (MapContains(ms_RestreamTxdIDMap, pGame->GetModelInfo(pEntity->m_nModelIndex)->GetTextureDictionaryID())) + { + if (!pEntity->bStreamingDontDelete && !pEntity->bImBeingRendered) + { + _asm + { + mov ecx, pEntity + mov eax, [ecx] + call dword ptr [eax+20h] + } + removedModels.insert(pEntity->m_nModelIndex); + } + } + pSectorEntry = (DWORD*)pSectorEntry[1]; + } + } + + ms_RestreamTxdIDMap.clear(); + + std::set::iterator it; + for (it = removedModels.begin(); it != removedModels.end(); it++) + { + ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(*it); + MemPut(ARRAY_ModelLoaded + 20 * (*it), 0); + } +} + +void CModelInfoSA::ModelAddRef(EModelRequestType requestType, const char* szTag) +{ + // Are we not loaded? + if (!IsLoaded()) + { + // Request it. Wait for it to load if we're asked to. + if (pGame && pGame->IsASyncLoadingEnabled()) + Request(requestType, szTag); + else + Request(BLOCKING, szTag); + } + + // Increment the references. + if (m_dwReferences == 0) + { + assert(!m_dwPendingInterfaceRef); + if (IsLoaded()) + { + m_pInterface = ppModelInfo[m_dwModelID]; + m_pInterface->usNumberOfRefs++; + } + else + m_dwPendingInterfaceRef = 1; + } + + m_dwReferences++; +} + +int CModelInfoSA::GetRefCount() +{ + return static_cast(m_dwReferences); +} + +void CModelInfoSA::RemoveRef(bool bRemoveExtraGTARef) +{ + // Decrement the references + if (m_dwReferences > 0) + m_dwReferences--; + + if (m_dwReferences == 0 && m_dwPendingInterfaceRef) + { + m_dwPendingInterfaceRef = 0; + return; + } + + // Handle extra ref if requested + if (bRemoveExtraGTARef) + { + // Remove ref added by GTA. + if (m_pInterface->usNumberOfRefs > 1) + { + DWORD dwFunction = FUNC_RemoveRef; + CBaseModelInfoSAInterface* pInterface = m_pInterface; + _asm + { + mov ecx, pInterface + call dwFunction + } + } + } + + // Unload it if 0 references left and we're not CJ model. + // And if we're loaded. + if (m_dwReferences == 0 && m_dwModelID != 0 && IsLoaded()) + { + Remove(); + } +} + +void CModelInfoSA::SetAlphaTransparencyEnabled(BOOL bEnabled) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + if (!MapContains(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID)) + { + MapSet(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID, (BYTE)(m_pInterface->bAlphaTransparency)); + } + m_pInterface->bAlphaTransparency = bEnabled; + } +} + +bool CModelInfoSA::IsAlphaTransparencyEnabled() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + return m_pInterface->bAlphaTransparency; + } + return false; +} + +void CModelInfoSA::StaticResetAlphaTransparencies() +{ + for (std::map::const_iterator iter = ms_ModelDefaultAlphaTransparencyMap.begin(); iter != ms_ModelDefaultAlphaTransparencyMap.end(); iter++) + { + CBaseModelInfoSAInterface* pInterface = ppModelInfo[iter->first]; + if (pInterface) + { + pInterface->bAlphaTransparency = iter->second; + } + } + + ms_ModelDefaultAlphaTransparencyMap.clear(); +} + +void CModelInfoSA::ResetAlphaTransparency() +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + BYTE* pbEnabled = MapFind(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); + if (pbEnabled) + { + m_pInterface->bAlphaTransparency = *pbEnabled; + MapRemove(ms_ModelDefaultAlphaTransparencyMap, m_dwModelID); + } + } +} + +short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) +{ + short sreturn = -1; + if (usUpgrade >= 1000 && usUpgrade <= 1193) + { + DWORD ModelID = m_dwModelID; + _asm + { + mov eax, ModelID + movsx edx, usUpgrade + mov eax, ARRAY_ModelInfo[eax*4] + mov ax, [eax+edx*2+0x2D6] + mov sreturn, ax + } + } + return sreturn; +} + +bool CModelInfoSA::IsUpgradeAvailable(eVehicleUpgradePosn posn) +{ + bool bRet = false; + DWORD ModelID = m_dwModelID; + _asm + { + mov eax, ModelID + lea ecx, ARRAY_ModelInfo[eax*4] + + mov eax, posn + mov ecx, [ecx+0x5C] + shl eax, 5 + push esi + mov esi, [ecx+eax+0D0h] + xor edx, edx + test esi, esi + setnl dl + mov al, dl + pop esi + + mov bRet, al + } + return bRet; +} + +void CModelInfoSA::SetCustomCarPlateText(const char* szText) +{ + char* szStoredText; + DWORD ModelID = m_dwModelID; + _asm + { + push ecx + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + add ecx, 40 + mov szStoredText, ecx + pop ecx + } + + if (szText) strncpy(szStoredText, szText, 8); + else szStoredText[0] = 0; +} + +unsigned int CModelInfoSA::GetNumRemaps() +{ + DWORD dwFunc = FUNC_CVehicleModelInfo__GetNumRemaps; + DWORD ModelID = m_dwModelID; + unsigned int uiReturn = 0; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + call dwFunc + mov uiReturn, eax + } + return uiReturn; +} + +void* CModelInfoSA::GetVehicleSuspensionData() +{ + return GetInterface()->pColModel->pColData->pSuspensionLines; +} + +void* CModelInfoSA::SetVehicleSuspensionData(void* pSuspensionLines) +{ + CColDataSA* pColData = GetInterface()->pColModel->pColData; + void* pOrigSuspensionLines = pColData->pSuspensionLines; + pColData->pSuspensionLines = pSuspensionLines; + return pOrigSuspensionLines; +} + +CVector CModelInfoSA::GetVehicleExhaustFumesPosition() +{ + return GetVehicleDummyPosition(eVehicleDummies::EXHAUST); +} + +void CModelInfoSA::SetVehicleExhaustFumesPosition(const CVector& vecPosition) +{ + return SetVehicleDummyPosition(eVehicleDummies::EXHAUST, vecPosition); +} + +CVector CModelInfoSA::GetVehicleDummyPosition(eVehicleDummies eDummy) +{ + if (!IsVehicle()) + return CVector(); + + // Request model load right now if not loaded yet (#9897) + if (!IsLoaded()) + Request(BLOCKING, "GetVehicleDummyPosition"); + + auto pVehicleModel = reinterpret_cast(m_pInterface); + return pVehicleModel->pVisualInfo->vecDummies[eDummy]; +} + +void CModelInfoSA::SetVehicleDummyPosition(eVehicleDummies eDummy, const CVector& vecPosition) +{ + if (!IsVehicle()) + return; + + // Request model load right now if not loaded yet (#9897) + if (!IsLoaded()) + Request(BLOCKING, "SetVehicleDummyPosition"); + + // Store default position in map + auto pVehicleModel = reinterpret_cast(m_pInterface); + auto iter = ms_ModelDefaultDummiesPosition.find(pVehicleModel); + if (iter == ms_ModelDefaultDummiesPosition.end()) + { + ms_ModelDefaultDummiesPosition.insert({pVehicleModel, std::map()}); + // Increment this model references count, so we don't unload it before we have a chance to reset the positions + m_pInterface->usNumberOfRefs++; + } + + if (ms_ModelDefaultDummiesPosition[pVehicleModel].find(eDummy) == ms_ModelDefaultDummiesPosition[pVehicleModel].end()) + { + ms_ModelDefaultDummiesPosition[pVehicleModel][eDummy] = pVehicleModel->pVisualInfo->vecDummies[eDummy]; + } + + // Set dummy position + pVehicleModel->pVisualInfo->vecDummies[eDummy] = vecPosition; +} + +void CModelInfoSA::ResetAllVehicleDummies() +{ + for (auto& info : ms_ModelDefaultDummiesPosition) + { + CVehicleModelInfoSAInterface* pVehicleModel = info.first; + for (auto& dummy : ms_ModelDefaultDummiesPosition[pVehicleModel]) + { + // TODO: Find out why this is a nullptr, and fix underlying bug + if (pVehicleModel->pVisualInfo != nullptr) + pVehicleModel->pVisualInfo->vecDummies[dummy.first] = dummy.second; + } + ms_ModelDefaultDummiesPosition[pVehicleModel].clear(); + // Decrement reference counter, since we reverted all position changes, the model can be safely unloaded + info.first->usNumberOfRefs--; + } + ms_ModelDefaultDummiesPosition.clear(); +} + +void CModelInfoSA::SetCustomModel(RpClump* pClump) +{ + // Error + if (pClump == NULL) + return; + + // Store the custom clump + m_pCustomClump = pClump; + + // Replace the model if we're loaded. + if (IsLoaded()) + { + switch (GetModelType()) + { + case MODEL_INFO_TYPE_PED: + return pGame->GetRenderWare()->ReplacePedModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_WEAPON: + return pGame->GetRenderWare()->ReplaceWeaponModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_VEHICLE: + return pGame->GetRenderWare()->ReplaceVehicleModel(pClump, static_cast(m_dwModelID)); + case MODEL_INFO_TYPE_ATOMIC: + case MODEL_INFO_TYPE_LOD_ATOMIC: + case MODEL_INFO_TYPE_TIME: + return pGame->GetRenderWare()->ReplaceAllAtomicsInModel(pClump, static_cast(m_dwModelID)); + } + } +} + +void CModelInfoSA::RestoreOriginalModel() +{ + // Are we loaded? + if (IsLoaded()) + { + ((void(__cdecl*)(unsigned short))FUNC_RemoveModel)(static_cast(m_dwModelID)); + } + + // Reset the stored custom vehicle clump + m_pCustomClump = NULL; +} + +void CModelInfoSA::SetColModel(CColModel* pColModel) +{ + // Grab the interfaces + CColModelSAInterface* pColModelInterface = pColModel->GetInterface(); + + if (!m_bAddedRefForCollision) + { + // Prevent this model from unloading while we have custom collision + ModelAddRef(BLOCKING, "for collision"); + m_bAddedRefForCollision = true; + } + + // Should always be loaded at this point + + // Skip setting if already done + if (m_pCustomColModel == pColModel) + return; + + // Store the col model we set + m_pCustomColModel = pColModel; + + // Do the following only if we're loaded + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // If no collision model has been set before, store the original in case we want to restore it + if (!m_pOriginalColModelInterface) + m_pOriginalColModelInterface = m_pInterface->pColModel; + + // Apply some low-level hacks + pColModelInterface->level = 0xA9; + + // Call SetColModel + DWORD dwFunc = FUNC_SetColModel; + DWORD ModelID = m_dwModelID; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + push 1 + push pColModelInterface + call dwFunc + } + + // FUNC_SetColModel resets bDoWeOwnTheColModel + m_pInterface->bDoWeOwnTheColModel = false; + m_pInterface->bCollisionWasStreamedWithModel = false; + + // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) + DWORD func = 0x5B2C20; + _asm + { + push pColModelInterface + push ModelID + call func + add esp, 8 + } + + // Set some lighting for this collision if not already present + CColDataSA* pColData = pColModelInterface->pColData; + if (pColData) + { + for (uint i = 0; i < pColData->numColTriangles; i++) + { + CColTriangleSA* pTriangle = pColData->pColTriangles + i; + if (pTriangle->lighting.night == 0 && pTriangle->lighting.day == 0) + { + pTriangle->lighting.night = 1; + pTriangle->lighting.day = 12; + } + } + } + } +} + +void CModelInfoSA::RestoreColModel() +{ + // Are we loaded? + m_pInterface = ppModelInfo[m_dwModelID]; + if (m_pInterface) + { + // We only have to store if the collision model was set + // Also only if we have a col model set + if (m_pOriginalColModelInterface && m_pCustomColModel) + { + DWORD dwFunc = FUNC_SetColModel; + DWORD dwOriginalColModelInterface = (DWORD)m_pOriginalColModelInterface; + DWORD ModelID = m_dwModelID; + _asm + { + mov ecx, ModelID + mov ecx, ARRAY_ModelInfo[ecx*4] + push 1 + push dwOriginalColModelInterface + call dwFunc + } + + // public: static void __cdecl CColAccel::addCacheCol(int, class CColModel const &) + DWORD func = 0x5B2C20; + _asm + { + push dwOriginalColModelInterface + push ModelID + call func + add esp, 8 + } + // (IJs) Document this function some time + } + } + + // We currently have no custom model loaded + m_pCustomColModel = NULL; + + // Remove ref added for collision + if (m_bAddedRefForCollision) + { + m_bAddedRefForCollision = false; + RemoveRef(); + } +} + +void CModelInfoSA::MakeCustomModel() +{ + // We have a custom model? + if (m_pCustomClump) + { + SetCustomModel(m_pCustomClump); + } + + // Custom collision model is not NULL and it's different from the original? + if (m_pCustomColModel) + { + SetColModel(m_pCustomColModel); + } +} + +void CModelInfoSA::GetVoice(short* psVoiceType, short* psVoiceID) +{ + if (psVoiceType) + *psVoiceType = GetPedModelInfoInterface()->sVoiceType; + if (psVoiceID) + *psVoiceID = GetPedModelInfoInterface()->sFirstVoice; +} + +void CModelInfoSA::GetVoice(const char** pszVoiceType, const char** pszVoice) +{ + short sVoiceType, sVoiceID; + GetVoice(&sVoiceType, &sVoiceID); + if (pszVoiceType) + *pszVoiceType = CPedSoundSA::GetVoiceTypeNameFromID(sVoiceType); + if (pszVoice) + *pszVoice = CPedSoundSA::GetVoiceNameFromID(sVoiceType, sVoiceID); +} + +void CModelInfoSA::SetVoice(short sVoiceType, short sVoiceID) +{ + GetPedModelInfoInterface()->sVoiceType = sVoiceType; + GetPedModelInfoInterface()->sFirstVoice = sVoiceID; + GetPedModelInfoInterface()->sLastVoice = sVoiceID; + GetPedModelInfoInterface()->sNextVoice = sVoiceID; +} + +void CModelInfoSA::SetVoice(const char* szVoiceType, const char* szVoice) +{ + short sVoiceType = CPedSoundSA::GetVoiceTypeIDFromName(szVoiceType); + if (sVoiceType < 0) + return; + short sVoiceID = CPedSoundSA::GetVoiceIDFromName(sVoiceType, szVoice); + if (sVoiceID < 0) + return; + SetVoice(sVoiceType, sVoiceID); +} + +void CModelInfoSA::MakePedModel(char* szTexture) +{ + // Create a new CPedModelInfo + CPedModelInfoSA pedModelInfo; + ppModelInfo[m_dwModelID] = (CBaseModelInfoSAInterface*)pedModelInfo.GetPedModelInfoInterface(); + + // Load our texture + pGame->GetStreaming()->RequestSpecialModel(m_dwModelID, szTexture, 0); +} + +void CModelInfoSA::DeallocateModel(void) +{ + Remove(); + ppModelInfo[m_dwModelID] = nullptr; +} +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for NodeNameStreamRead +// +// Ignore extra characters in dff frame name +// +////////////////////////////////////////////////////////////////////////////////////////// +__declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) +{ + // Calc sizes + const uint uiMaxBufferSize = 24; + uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); + uint uiAmountToSkip = uiSize - uiAmountToRead; + + // Read good bit + RwStreamRead(stream, pDest, uiAmountToRead); + pDest[uiAmountToRead] = 0; + + // Skip bad bit (this might not be required) + if (uiAmountToSkip > 0) + RwStreamSkip(stream, uiAmountToSkip); +} + +// Hook info +#define HOOKPOS_NodeNameStreamRead 0x072FA68 +#define HOOKSIZE_NodeNameStreamRead 15 +DWORD RETURN_NodeNameStreamRead = 0x072FA77; +void _declspec(naked) HOOK_NodeNameStreamRead() +{ + _asm + { + pushad + push edi + push esi + push ebx + call OnMY_NodeNameStreamRead + add esp, 4*3 + popad + + jmp RETURN_NodeNameStreamRead + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Setup hooks +// +////////////////////////////////////////////////////////////////////////////////////////// +void CModelInfoSA::StaticSetHooks() +{ + EZHookInstall(NodeNameStreamRead); +} + +// Recursive RwFrame children searching function +void CModelInfoSA::RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel) +{ + for (RwFrame* ret = parent->child; ret != NULL; ret = ret->next) + { + // recurse into the child + if (ret->child != NULL) + { + RwSetSupportedUpgrades(ret, dwModel); + } + SString strName = ret->szName; + // Spoiler + if (strName == "ug_bonnet") + { + m_ModelSupportedUpgrades.m_bBonnet = true; + } + else if (strName == "ug_bonnet_left") + { + m_ModelSupportedUpgrades.m_bBonnet_Left = true; + } + else if (strName == "ug_bonnet_left_dam") + { + m_ModelSupportedUpgrades.m_bBonnet_Left_dam = true; + } + else if (strName == "ug_bonnet_right") + { + m_ModelSupportedUpgrades.m_bBonnet_Right = true; + } + else if (strName == "ug_bonnet_right_dam") + { + m_ModelSupportedUpgrades.m_bBonnet_Right_dam = true; + } + // Spoiler + else if (strName == "ug_spoiler") + { + m_ModelSupportedUpgrades.m_bSpoiler = true; + } + else if (strName == "ug_spoiler_dam") + { + m_ModelSupportedUpgrades.m_bSpoiler_dam = true; + } + // Bonnet + else if (strName == "ug_lights") + { + m_ModelSupportedUpgrades.m_bLamps = true; + } + else if (strName == "ug_lights_dam") + { + m_ModelSupportedUpgrades.m_bLamps_dam = true; + } + // Roof + else if (strName == "ug_roof") + { + m_ModelSupportedUpgrades.m_bRoof = true; + } + // Side Skirt + else if (strName == "ug_wing_right") + { + m_ModelSupportedUpgrades.m_bSideSkirt_Right = true; + } + // Side Skirt + else if (strName == "ug_wing_left") + { + m_ModelSupportedUpgrades.m_bSideSkirt_Left = true; + } + // Exhaust + else if (strName == "exhaust_ok") + { + m_ModelSupportedUpgrades.m_bExhaust = true; + } + // Front bullbars + else if (strName == "ug_frontbullbar") + { + m_ModelSupportedUpgrades.m_bFrontBullbars = true; + } + // rear bullbars + else if (strName == "ug_backbullbar") + { + m_ModelSupportedUpgrades.m_bRearBullbars = true; + } + // Front bumper + else if (strName == "bump_front_dummy") + { + m_ModelSupportedUpgrades.m_bFrontBumper = true; + } + // Rear bumper + else if (strName == "bump_rear_dummy") + { + m_ModelSupportedUpgrades.m_bRearBumper = true; + } + // Rear bumper + else if (strName == "misc_c") + { + m_ModelSupportedUpgrades.m_bMisc = true; + } + } +} + +void CModelInfoSA::InitialiseSupportedUpgrades(RpClump* pClump) +{ + m_ModelSupportedUpgrades.Reset(); + RwFrame* pFrame = RpGetFrame(pClump); + RwSetSupportedUpgrades(pFrame, m_dwModelID); + m_ModelSupportedUpgrades.m_bInitialised = true; +} + +void CModelInfoSA::ResetSupportedUpgrades() +{ + m_ModelSupportedUpgrades.Reset(); +} + +void CModelInfoSA::SetObjectPropertiesGroup(unsigned short usNewGroup) +{ + unsigned short usOrgGroup = GetObjectPropertiesGroup(); + if (usOrgGroup == usNewGroup) + return; + + if (!MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID)) + MapSet(ms_OriginalObjectPropertiesGroups, m_dwModelID, usOrgGroup); + + GetInterface()->usDynamicIndex = usNewGroup; +} + +unsigned short CModelInfoSA::GetObjectPropertiesGroup() +{ + unsigned short usGroup = GetInterface()->usDynamicIndex; + if (usGroup == 0xFFFF) + usGroup = 0; + + return usGroup; +} + +void CModelInfoSA::RestoreObjectPropertiesGroup() +{ + unsigned short* usGroupInMap = MapFind(ms_OriginalObjectPropertiesGroups, m_dwModelID); + if (usGroupInMap) + { + GetInterface()->usDynamicIndex = *usGroupInMap; + MapRemove(ms_OriginalObjectPropertiesGroups, m_dwModelID); + } +} + +void CModelInfoSA::RestoreAllObjectsPropertiesGroups() +{ + for (const auto& pair : ms_OriginalObjectPropertiesGroups) + { + pGame->GetModelInfo(pair.first)->GetInterface()->usDynamicIndex = pair.second; + } + ms_OriginalObjectPropertiesGroups.clear(); +} + +eModelInfoType CModelInfoSA::GetModelType() +{ + return ((eModelInfoType(*)())m_pInterface->VFTBL->GetModelType)(); +} + +bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) +{ + bool isTowable = true; + + const bool isTowTruck = towingModel->GetModel() == 525; + const bool isTractor = towingModel->GetModel() == 531; + + if (IsTrain() || towingModel->IsTrain()) + { + // A train is never towing other vehicles. Trains are linked by other means + isTowable = false; + } + else if (isTowTruck || isTractor) + { + const bool isFarmTrailer = GetModel() == 610; + + // Tow truck (525) and tractor (531) can only tow certain vehicle types without issues + if (IsBoat() || IsBike() || IsBmx()) + { + isTowable = false; + } + else if (IsTrailer() && !(isTractor && isFarmTrailer)) + { + isTowable = false; + } + } + + return isTowable; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CModelInfoSA::ForceUnload +// +// 1. Unload the dff from memory +// 2. Unload the associated txd from memory +// 3. Cross fingers +// +// Why do we do this? +// Player model adds (seemingly) unnecessary refs +// (Will crash if anything is actually using the model or txd) +// +// Won't work if there is a custom model replacement +// +// Returns true if model was unloaded +// +////////////////////////////////////////////////////////////////////////////////////////// +bool CModelInfoSA::ForceUnload() +{ + CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface(); + + // Need to have at least one ref to delete pRwObject + if (pModelInfoSAInterface->usNumberOfRefs == 0 && pModelInfoSAInterface->pRwObject != NULL) + { + pModelInfoSAInterface->usNumberOfRefs++; + } + + // Keep removing refs from the model until is it gone + uint uiLimit = 100; + while (pModelInfoSAInterface->usNumberOfRefs > 0 && uiLimit--) + { + RemoveRef(); + } + + // Did it work? + if (pModelInfoSAInterface->usNumberOfRefs > 0 || pModelInfoSAInterface->pRwObject != NULL) + return false; + + // If success, then remove txd + ushort usTxdId = GetTextureDictionaryID(); + if (usTxdId) + pGame->GetRenderWare()->TxdForceUnload(usTxdId, true); + + return true; +}