diff --git a/Client/core/DXHook/CProxyDirect3D9.cpp b/Client/core/DXHook/CProxyDirect3D9.cpp index d563d95a5d..40330e9696 100644 --- a/Client/core/DXHook/CProxyDirect3D9.cpp +++ b/Client/core/DXHook/CProxyDirect3D9.cpp @@ -10,6 +10,8 @@ *****************************************************************************/ #include "StdInc.h" +#include + HRESULT HandleCreateDeviceResult(HRESULT hResult, IDirect3D9* pDirect3D, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface); std::vector ms_CreatedDirect3D9List; @@ -166,6 +168,10 @@ HRESULT CProxyDirect3D9::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND SetWindowTextW(hFocusWindow, MbUTF8ToUTF16("MTA: San Andreas").c_str()); #endif + // Set dark titlebar if needed + BOOL darkTitleBar = GetSystemRegistryValue((uint)HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", "AppsUseLightTheme") == "\x0"; + DwmSetWindowAttribute(hFocusWindow, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkTitleBar, sizeof(darkTitleBar)); + // Detect if second call to CreateDevice if (CreateDeviceSecondCallCheck(hResult, m_pDevice, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface)) { diff --git a/Client/core/premake5.lua b/Client/core/premake5.lua index 0573dd6c05..49755743a2 100644 --- a/Client/core/premake5.lua +++ b/Client/core/premake5.lua @@ -46,7 +46,7 @@ project "Client Core" links { "ws2_32", "d3dx9", "Userenv", "DbgHelp", "xinput", "Imagehlp", "dxguid", "dinput8", - "strmiids", "odbc32", "odbccp32", "shlwapi", "winmm", "gdi32", "Imm32", "Psapi", + "strmiids", "odbc32", "odbccp32", "shlwapi", "winmm", "gdi32", "Imm32", "Psapi", "dwmapi", "pthread", "libpng", "jpeg", "zlib", "tinygettext", "discord-rpc", } diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index c3eafa78d6..77c78333f3 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -253,7 +253,7 @@ CGameSA::CGameSA() error += ": "; error += e.what(); - MessageBoxUTF8(nullptr, error.c_str(), _("Error"), MB_ICONERROR | MB_OK); + MessageBoxUTF8(nullptr, error, _("Error"), MB_ICONERROR | MB_OK); ExitProcess(EXIT_FAILURE); } catch (const std::exception& e) diff --git a/Client/game_sa/CPhysicalSA.h b/Client/game_sa/CPhysicalSA.h index eb5a2a4515..a1236f371b 100644 --- a/Client/game_sa/CPhysicalSA.h +++ b/Client/game_sa/CPhysicalSA.h @@ -104,8 +104,8 @@ class CPhysicalSAInterface : public CEntitySAInterface CVector m_vecUnk; // 280 uint32 m_pad4; // 292 CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 - float m_fLighting; // 300 - float m_fLighting2; // 304 + float m_fLighting; // 300 surface brightness + float m_fLighting2; // 304 dynamic lighting (unused, always set to 0 in the GTA code) class CShadowDataSA* m_pShadowData; // 308 CRect* GetBoundRect_(CRect* pRect); diff --git a/Client/game_sa/CRendererSA.cpp b/Client/game_sa/CRendererSA.cpp index bad607d5ba..04face6248 100644 --- a/Client/game_sa/CRendererSA.cpp +++ b/Client/game_sa/CRendererSA.cpp @@ -15,6 +15,9 @@ #include "CMatrix.h" #include "gamesa_renderware.h" +#define SetLightColoursForPedsCarsAndObjects(fMult) ((RpLight*(__cdecl*)(float))0x735D90)(fMult) +#define SetAmbientColours() ((RpLight*(__cdecl*)())0x735D30)() + CRendererSA::CRendererSA() { } @@ -23,7 +26,7 @@ CRendererSA::~CRendererSA() { } -void CRendererSA::RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix) +void CRendererSA::RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting) { CBaseModelInfoSAInterface* pModelInfoSAInterface = pModelInfo->GetInterface(); if (!pModelInfoSAInterface) @@ -40,7 +43,10 @@ void CRendererSA::RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix) rwMatrix.up = (RwV3d&)matrix.vFront; rwMatrix.at = (RwV3d&)matrix.vUp; rwMatrix.pos = (RwV3d&)matrix.vPos; - RwFrameTransform(pFrame, &rwMatrix, rwCOMBINEREPLACE); + RwFrameTransform(pFrame, &rwMatrix, rwCOMBINEREPLACE); + + // Setup ambient light multiplier + SetLightColoursForPedsCarsAndObjects(lighting); if (pRwObject->type == RP_TYPE_ATOMIC) { @@ -52,4 +58,7 @@ void CRendererSA::RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix) RpClump* pClump = reinterpret_cast(pRwObject); RpClumpRender(pClump); } + + // Restore ambient light + SetAmbientColours(); } diff --git a/Client/game_sa/CRendererSA.h b/Client/game_sa/CRendererSA.h index e71a83665e..eb09edae72 100644 --- a/Client/game_sa/CRendererSA.h +++ b/Client/game_sa/CRendererSA.h @@ -19,5 +19,5 @@ class CRendererSA : public CRenderer CRendererSA(); ~CRendererSA(); - void RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix) override; + void RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting) override; }; diff --git a/Client/mods/deathmatch/logic/CModelRenderer.cpp b/Client/mods/deathmatch/logic/CModelRenderer.cpp index 2f06c836ec..ed05fa4ae4 100644 --- a/Client/mods/deathmatch/logic/CModelRenderer.cpp +++ b/Client/mods/deathmatch/logic/CModelRenderer.cpp @@ -13,14 +13,14 @@ #include "game\CRenderer.h" #include "game\CVisibilityPlugins.h" -bool CModelRenderer::EnqueueModel(CModelInfo* pModelInfo, const CMatrix& matrix) +bool CModelRenderer::EnqueueModel(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting) { if (g_pCore->IsWindowMinimized()) return false; if (pModelInfo && pModelInfo->IsLoaded()) { - m_Queue.emplace_back(pModelInfo, matrix); + m_Queue.emplace_back(pModelInfo, matrix, lighting); return true; } @@ -54,7 +54,7 @@ void CModelRenderer::Render() for (auto& modelDesc : m_Queue) { if (modelDesc.pModelInfo->IsLoaded() && !modelDesc.pModelInfo->GetIdeFlag(eModelIdeFlag::DRAW_LAST)) - pRenderer->RenderModel(modelDesc.pModelInfo, modelDesc.matrix); + pRenderer->RenderModel(modelDesc.pModelInfo, modelDesc.matrix, modelDesc.lighting); } m_Queue.clear(); @@ -68,5 +68,5 @@ void CModelRenderer::RenderEntity(SModelToRender* modelDesc, float distance) CRenderer* pRenderer = g_pGame->GetRenderer(); assert(pRenderer); - pRenderer->RenderModel(modelDesc->pModelInfo, modelDesc->matrix); + pRenderer->RenderModel(modelDesc->pModelInfo, modelDesc->matrix, modelDesc->lighting); } diff --git a/Client/mods/deathmatch/logic/CModelRenderer.h b/Client/mods/deathmatch/logic/CModelRenderer.h index d923db6c7f..a2dba3030b 100644 --- a/Client/mods/deathmatch/logic/CModelRenderer.h +++ b/Client/mods/deathmatch/logic/CModelRenderer.h @@ -18,15 +18,17 @@ class CModelRenderer final { CModelInfo* pModelInfo; CMatrix matrix; + float lighting; - SModelToRender(CModelInfo* pModelInfo, const CMatrix& matrix) : + SModelToRender(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting = 0.0f) : pModelInfo(pModelInfo), - matrix(matrix) + matrix(matrix), + lighting(lighting) { } }; - bool EnqueueModel(CModelInfo* pModelInfo, const CMatrix& matrix); + bool EnqueueModel(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting); void Update(); diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 209bcbc1a2..6fc425fa83 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -1125,6 +1125,42 @@ void MixedReadMaterialString(CScriptArgReader& argStream, CClientMaterial*& pMat } } +// +// Check 4x4 lua table +// +bool IsValidMatrixLuaTable(lua_State* luaVM, std::uint32_t argIndex) noexcept +{ + std::uint32_t cell = 0; + + if (lua_type(luaVM, argIndex) == LUA_TTABLE) + { + lua_pushnil(luaVM); + for (std::uint32_t row = 0; lua_next(luaVM, argIndex) != 0; lua_pop(luaVM, 1), ++row) + { + if (lua_type(luaVM, -1) != LUA_TTABLE) + return false; + + std::uint32_t col = 0; + + lua_pushnil(luaVM); + for (; lua_next(luaVM, -2) != 0; lua_pop(luaVM, 1), ++col, ++cell) + { + int argumentType = lua_type(luaVM, -1); + if (argumentType != LUA_TNUMBER && argumentType != LUA_TSTRING) + return false; + } + + if (col != 4) + return false; + } + } + + if (cell != 16) + return false; + + return true; +} + // // 4x4 matrix into CMatrix // diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index d755b4e5e3..818ebeb308 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -587,6 +587,7 @@ class CScriptArgReader; void MixedReadDxFontString(CScriptArgReader& argStream, eFontType& outFontType, eFontType defaultFontType, CClientDxFont*& poutDxFontElement); void MixedReadGuiFontString(CScriptArgReader& argStream, SString& strFontName, const char* szDefaultFontName, CClientGuiFont*& poutGuiFontElement); void MixedReadMaterialString(CScriptArgReader& argStream, CClientMaterial*& pMaterialElement); +bool IsValidMatrixLuaTable(lua_State* luaVM, std::uint32_t argIndex) noexcept; bool ReadMatrix(lua_State* luaVM, uint uiArgIndex, CMatrix& outMatrix); void MinClientReqCheck(lua_State* luaVM, const char* szVersionReq, const char* szReason); bool MinClientReqCheck(CScriptArgReader& argStream, const char* szVersionReq, const char* szReason = nullptr); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp index f54378944d..adc83a1224 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp @@ -2123,7 +2123,7 @@ bool CLuaDrawingDefs::DxDrawWiredSphere(lua_State* const luaVM, const CVector po return true; } -bool CLuaDrawingDefs::DxDrawModel3D(std::uint32_t modelID, CVector position, CVector rotation, const std::optional scale) +bool CLuaDrawingDefs::DxDrawModel3D(std::uint32_t modelID, CVector position, CVector rotation, const std::optional scale, const std::optional lighting) { CModelInfo* pModelInfo = g_pGame->GetModelInfo(modelID); if (!pModelInfo) @@ -2138,5 +2138,5 @@ bool CLuaDrawingDefs::DxDrawModel3D(std::uint32_t modelID, CVector position, CVe ConvertDegreesToRadians(rotation); return g_pClientGame->GetModelRenderer()->EnqueueModel(pModelInfo, - CMatrix{position, rotation, scale.value_or(CVector{1.0f, 1.0f, 1.0f})}); + CMatrix{position, rotation, scale.value_or(CVector{1.0f, 1.0f, 1.0f})}, lighting.value_or(0.0f)); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.h index 55dc3adda1..d01d6ec214 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.h @@ -82,7 +82,7 @@ class CLuaDrawingDefs : public CLuaDefs static bool DxDrawWiredSphere(lua_State* const luaVM, const CVector position, const float radius, const std::optional color, const std::optional lineWidth, const std::optional iterations); - static bool DxDrawModel3D(std::uint32_t modelID, CVector position, CVector rotation, const std::optional scale); + static bool DxDrawModel3D(std::uint32_t modelID, CVector position, CVector rotation, const std::optional scale, const std::optional lighting); private: static void AddDxMaterialClass(lua_State* luaVM); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index e88f8f6751..ff4f9b3abb 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -99,6 +99,7 @@ void CLuaElementDefs::LoadFunctions() {"setElementFrozen", SetElementFrozen}, {"setLowLODElement", ArgumentParser}, {"setElementCallPropagationEnabled", SetElementCallPropagationEnabled}, + {"setElementLighting", ArgumentParser}, }; // Add functions @@ -191,6 +192,7 @@ void CLuaElementDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "setLowLOD", "setLowLODElement"); lua_classfunction(luaVM, "setCallPropagationEnabled", "setElementCallPropagationEnabled"); lua_classfunction(luaVM, "setStreamable", "setElementStreamable"); + lua_classfunction(luaVM, "setLighting", "setElementLighting"); lua_classvariable(luaVM, "callPropagationEnabled", "setElementCallPropagationEnabled", "isElementCallPropagationEnabled"); lua_classvariable(luaVM, "waitingForGroundToLoad", NULL, "isElementWaitingForGroundToLoad"); @@ -225,6 +227,7 @@ void CLuaElementDefs::AddClass(lua_State* luaVM) lua_classvariable(luaVM, "velocity", SetElementVelocity, OOP_GetElementVelocity); lua_classvariable(luaVM, "angularVelocity", SetElementAngularVelocity, OOP_GetElementTurnVelocity); lua_classvariable(luaVM, "isElement", NULL, "isElement"); + lua_classvariable(luaVM, "lighting", "setElementLighting", "getElementLighting"); // TODO: Support element data: player.data["age"] = 1337; <=> setElementData(player, "age", 1337) lua_registerclass(luaVM, "Element"); @@ -1340,6 +1343,7 @@ std::variant CLuaElementDefs::GetElementLighting(CClientEntity* ent break; } case CCLIENTOBJECT: + case CCLIENTWEAPON: { CObject* object = static_cast(entity)->GetGameObject(); if (object) @@ -2604,3 +2608,41 @@ int CLuaElementDefs::IsElementWaitingForGroundToLoad(lua_State* luaVM) lua_pushboolean(luaVM, false); return 1; } + +bool CLuaElementDefs::SetElementLighting(CClientEntity* entity, float lighting) +{ + switch (entity->GetType()) + { + case CCLIENTPLAYER: + case CCLIENTPED: + { + auto* ped = static_cast(entity)->GetGamePlayer(); + if (!ped) + return false; + + ped->SetLighting(lighting); + return true; + } + case CCLIENTVEHICLE: + { + auto* vehicle = static_cast(entity)->GetGameVehicle(); + if (!vehicle) + return false; + + vehicle->SetLighting(lighting); + return true; + } + case CCLIENTOBJECT: + case CCLIENTWEAPON: + { + auto* object = static_cast(entity)->GetGameObject(); + if (!object) + return false; + + object->SetLighting(lighting); + return true; + } + } + + return false; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.h index 40f93761e4..87b8c42f64 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.h @@ -99,4 +99,5 @@ class CLuaElementDefs : public CLuaDefs LUA_DECLARE(SetElementFrozen); static bool SetLowLodElement(lua_State* luaVM, CClientEntity* pEntity, std::optional pLowLodEntity); LUA_DECLARE(SetElementCallPropagationEnabled); + static bool SetElementLighting(CClientEntity* entity, float lighting); }; diff --git a/Client/multiplayer_sa/multiplayer_keysync.cpp b/Client/multiplayer_sa/multiplayer_keysync.cpp index 5807350dbb..704020a18b 100644 --- a/Client/multiplayer_sa/multiplayer_keysync.cpp +++ b/Client/multiplayer_sa/multiplayer_keysync.cpp @@ -51,7 +51,6 @@ VOID InitKeysyncHooks() HookInstallMethod(VTBL_CBoat__ProcessControl, (DWORD)HOOK_CBoat__ProcessControl); HookInstallMethod(VTBL_CBike__ProcessControl, (DWORD)HOOK_CBike__ProcessControl); HookInstallMethod(VTBL_CHeli__ProcessControl, (DWORD)HOOK_CHeli__ProcessControl); - HookInstallMethod(VTBL_CHeli__ProcessControl, (DWORD)HOOK_CHeli__ProcessControl); // not strictly for keysync, to make CPlayerPed::GetPlayerInfoForThisPlayerPed always return the local playerinfo // 00609FF2 EB 1F JMP SHORT gta_sa_u.0060A013 diff --git a/Client/sdk/game/CRenderer.h b/Client/sdk/game/CRenderer.h index 65d6a8bdca..19a9465224 100644 --- a/Client/sdk/game/CRenderer.h +++ b/Client/sdk/game/CRenderer.h @@ -19,5 +19,5 @@ class CRenderer public: virtual ~CRenderer() {} - virtual void RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix) = 0; + virtual void RenderModel(CModelInfo* pModelInfo, const CMatrix& matrix, float lighting) = 0; }; diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 76540327af..27afc8510e 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -646,6 +646,42 @@ void ReadPregFlags(CScriptArgReader& argStream, pcrecpp::RE_Options& pOptions) } } +// +// Check 4x4 lua table +// +bool IsValidMatrixLuaTable(lua_State* luaVM, std::uint32_t argIndex) noexcept +{ + std::uint32_t cell = 0; + + if (lua_type(luaVM, argIndex) == LUA_TTABLE) + { + lua_pushnil(luaVM); + for (std::uint32_t row = 0; lua_next(luaVM, argIndex) != 0; lua_pop(luaVM, 1), ++row) + { + if (lua_type(luaVM, -1) != LUA_TTABLE) + return false; + + std::uint32_t col = 0; + + lua_pushnil(luaVM); + for (; lua_next(luaVM, -2) != 0; lua_pop(luaVM, 1), ++col, ++cell) + { + int argumentType = lua_type(luaVM, -1); + if (argumentType != LUA_TNUMBER && argumentType != LUA_TSTRING) + return false; + } + + if (col != 4) + return false; + } + } + + if (cell != 16) + return false; + + return true; +} + // // 4x4 matrix into CMatrix // diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 796ca3c702..62d4312c51 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -390,6 +390,7 @@ void MixedReadResourceString(CScriptArgReader& argStream, CResource*& pOutRes bool StringToBool(const SString& strText); void MinServerReqCheck(CScriptArgReader& argStream, const char* szVersionReq, const char* szReason); void ReadPregFlags(CScriptArgReader& argStream, pcrecpp::RE_Options& pOptions); +bool IsValidMatrixLuaTable(lua_State* luaVM, std::uint32_t argIndex) noexcept; bool ReadMatrix(lua_State* luaVM, uint uiArgIndex, CMatrix& outMatrix); // diff --git a/Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h b/Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h index b31b5cba4a..d6d1cc3297 100644 --- a/Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h +++ b/Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h @@ -283,9 +283,12 @@ struct CLuaFunctionParserBase return true; return iArgument == LUA_TUSERDATA || iArgument == LUA_TLIGHTUSERDATA; } - // CMatrix may either be represented by 3 CLuaVector or by 12 numbers + // CMatrix can be represented either by 3 CLuaVectors, 12 numbers, or a 4x4 Lua table else if constexpr (std::is_same_v) { + if (IsValidMatrixLuaTable(L, index)) + return true; + for (int i = 0; i < sizeof(CMatrix) / sizeof(float); i++) { if (!lua_isnumber(L, index + i)) @@ -639,6 +642,19 @@ struct CLuaFunctionParserBase return matrix; } + if (lua_istable(L, index)) + { + CMatrix matrix; + + if (!ReadMatrix(L, index, matrix)) + { + SetBadArgumentError(L, "matrix", index, "table"); + return T{}; + } + + return matrix; + } + int iType = lua_type(L, index); bool isLightUserData = iType == LUA_TLIGHTUSERDATA; void* pValue = lua::PopPrimitive(L, index);