From df7a9178082498a29e00ee120f79000010488f22 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Thu, 9 Mar 2023 02:37:09 +0100 Subject: [PATCH 01/19] Fix cmake build (#550) --- source/game_sa/PathFind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/game_sa/PathFind.h b/source/game_sa/PathFind.h index e633dd79db..834261ab10 100644 --- a/source/game_sa/PathFind.h +++ b/source/game_sa/PathFind.h @@ -10,7 +10,7 @@ #include "Vector.h" #include "NodeAddress.h" #include "NodeRoute.h" -#include "FixedFloat.hpp" +#include #include static constexpr auto NUM_PATH_MAP_AREA_X{ 8 }; From 4a619e9763e3c97f89d91caccc3a6d355ba66596 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Thu, 9 Mar 2023 03:00:47 +0100 Subject: [PATCH 02/19] Fix asserts (#549) --- source/game_sa/Radar.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source/game_sa/Radar.cpp b/source/game_sa/Radar.cpp index 394c256ee2..0d5f1073ae 100644 --- a/source/game_sa/Radar.cpp +++ b/source/game_sa/Radar.cpp @@ -1254,19 +1254,21 @@ void CRadar::Draw3dMarkers() { }; for (auto&& [i, trace] : notsa::enumerate(ms_RadarTrace)) { - if (!trace.m_bTrackingBlip) + if (!trace.m_bTrackingBlip) { continue; + } const auto color = CRGBA{GetRadarTraceColour(trace.m_nColour, trace.m_bBright, trace.m_bFriendly)}; // TODO: make a tConeHandle or something for this. - const auto coneHandle = (uint16)i | ((uint16)trace.m_nCounter << (sizeof(uint16) * 8u)); + const auto coneHandle = (uint32)i | ((uint32)trace.m_nCounter << (sizeof(uint16) * 8)); - if (trace.m_nBlipDisplayFlag != BLIP_DISPLAY_BOTH && trace.m_nBlipDisplayFlag != BLIP_DISPLAY_MARKERONLY) + if (trace.m_nBlipDisplayFlag != BLIP_DISPLAY_BOTH && trace.m_nBlipDisplayFlag != BLIP_DISPLAY_MARKERONLY) { continue; + } switch (trace.m_nBlipType) { case BLIP_CAR: { - const auto vehicle = GetVehiclePool()->GetAt(trace.m_nEntityHandle); + const auto vehicle = GetVehiclePool()->GetAtRef(trace.m_nEntityHandle); assert(vehicle); // NOTSA const auto posn = [vehicle] { @@ -1282,7 +1284,7 @@ void CRadar::Draw3dMarkers() { break; } case BLIP_CHAR: { - const auto ped = GetPedPool()->GetAt(trace.m_nEntityHandle); + const auto ped = GetPedPool()->GetAtRef(trace.m_nEntityHandle); assert(ped); // NOTSA PutMarkerCone(coneHandle, ped->GetRealPosition() + CVector{0.0f, 0.0f, 2.7f}, 1.2f, color); @@ -1293,7 +1295,7 @@ void CRadar::Draw3dMarkers() { const auto posn = [&trace]() { CVector ret{}; if (trace.m_nBlipType == BLIP_OBJECT) { - if (const auto obj = GetObjectPool()->GetAt(trace.m_nEntityHandle)) { + if (const auto obj = GetObjectPool()->GetAtRef(trace.m_nEntityHandle)) { const auto bbMaxZ = obj->GetColModel()->GetBoundingBox().m_vecMax.z; ret = obj->GetPosition() + CVector{0.0f, 0.0f, bbMaxZ}; } else { From 5017d41818a41c46373b1b806a6d5dd6af62f4be Mon Sep 17 00:00:00 2001 From: Pirulax Date: Thu, 9 Mar 2023 05:49:23 +0100 Subject: [PATCH 03/19] `CCutsceneMgr` (#547) --- source/InjectHooksMain.cpp | 1 + source/StdInc.h | 1 + source/extensions/ci_string.hpp | 34 + source/extensions/utility.hpp | 61 +- .../game_sa/Animation/AnimBlendAssocGroup.cpp | 4 +- .../game_sa/Animation/AnimBlendAssocGroup.h | 2 +- source/game_sa/Animation/AnimBlendFrameData.h | 12 +- source/game_sa/Animation/AnimSequenceFrames.h | 6 + source/game_sa/Collision/TempColModels.h | 4 + source/game_sa/Core/Link.h | 5 +- source/game_sa/Core/Pool.h | 4 +- source/game_sa/Core/Vector.h | 11 + source/game_sa/CutsceneMgr.cpp | 1069 +++++++++++++++-- source/game_sa/CutsceneMgr.h | 140 ++- source/game_sa/Directory.cpp | 2 +- source/game_sa/Directory.h | 3 + source/game_sa/Entity/Entity.cpp | 14 +- source/game_sa/Entity/Entity.h | 8 + .../game_sa/Entity/Object/CutsceneObject.cpp | 8 +- source/game_sa/Entity/Object/CutsceneObject.h | 2 +- source/game_sa/Enums/eModelID.h | 9 + source/game_sa/FileLoader.cpp | 17 +- source/game_sa/Messages.cpp | 4 +- source/game_sa/Models/BaseModelInfo.h | 2 +- source/game_sa/Models/VehicleModelInfo.cpp | 10 +- source/game_sa/Models/VehicleModelInfo.h | 3 +- source/game_sa/Pickup.cpp | 3 +- source/game_sa/PopCycle.cpp | 2 +- source/game_sa/RealTimeShadow.cpp | 3 + source/game_sa/Shadows.cpp | 22 +- .../SeekEntity/TaskComplexSeekEntity.h | 2 +- .../Tasks/TaskTypes/TaskComplexArrestPed.cpp | 2 +- .../TaskComplexKillPedOnFootArmed.cpp | 2 +- source/game_sa/WaterLevel.cpp | 2 +- source/game_sa/World.cpp | 5 +- source/game_sa/cHandlingDataMgr.cpp | 2 +- .../Script/MissionDebugModule.cpp | 2 +- 37 files changed, 1289 insertions(+), 194 deletions(-) create mode 100644 source/extensions/ci_string.hpp diff --git a/source/InjectHooksMain.cpp b/source/InjectHooksMain.cpp index 8b958cfb0c..ceaaf310fa 100644 --- a/source/InjectHooksMain.cpp +++ b/source/InjectHooksMain.cpp @@ -412,6 +412,7 @@ void InjectHooksMain() { HookInstall(0x541DD0, CPad::UpdatePads); // [ImGui] Changes logic of the function and shouldn't be toggled on/off HookInstall(0x459F70, CVehicleRecording::Render); // [ImGui] Debug stuff rendering + CCutsceneMgr::InjectHooks(); CFileMgr::InjectHooks(); CPedGroupPlacer::InjectHooks(); CLoadedCarGroup::InjectHooks(); diff --git a/source/StdInc.h b/source/StdInc.h index a6e02b06f6..35bdfc2e7c 100644 --- a/source/StdInc.h +++ b/source/StdInc.h @@ -27,6 +27,7 @@ #include namespace rng = std::ranges; +namespace rngv = std::views; #include "Base.h" #include "config.h" diff --git a/source/extensions/ci_string.hpp b/source/extensions/ci_string.hpp new file mode 100644 index 0000000000..75f23d233d --- /dev/null +++ b/source/extensions/ci_string.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace notsa { +namespace details { +// Partially based on code from https://en.cppreference.com/w/cpp/string/basic_string +struct ci_char_traits : public std::char_traits { + static char to_upper(char ch) { return std::toupper((unsigned char)ch); } + + static bool eq(char c1, char c2) { return to_upper(c1) == to_upper(c2); } + static bool lt(char c1, char c2) { return to_upper(c1) < to_upper(c2); } + static const char* find(const char* s, std::size_t n, char a) { + const auto ua = to_upper(a); + for (; n; n--, s++) { + if (to_upper(*s) == ua) { + return s; + } + } + return nullptr; + } + static int compare(const char* s1, const char* s2, std::size_t n) { +#ifdef WIN32 + return _strnicmp(s1, s2, n); +#else /* *nix */ + return strncasecmp(s1, s2, n) +#endif + } +}; +}; // namespace details +using ci_string = std::basic_string; +using ci_string_view = std::basic_string_view; +}; // namespace notsa diff --git a/source/extensions/utility.hpp b/source/extensions/utility.hpp index ffc2b0668b..9aa7d02674 100644 --- a/source/extensions/utility.hpp +++ b/source/extensions/utility.hpp @@ -1,11 +1,66 @@ #pragma once #include +#include #include +#include "Base.h" + + namespace notsa { namespace rng = std::ranges; +/*! +* Much like std::stoi [and variants] but takes an `std::string_view` + in debug does error checking [unlike the C stuff] +* @param str The string to convert +* @param radix The radix (base) of the number +* @param end The end of the the number in the string (points to inside `sv`) +*/ +template +T ston(std::string_view str, int radix = 10, const char** end = nullptr) { + T out; + const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), out, radix); + assert(ec == std::errc{}); + if (end) { + *end = ptr; + } + return out; +} + +/*! +* Much like std::stof [and variants] but takes an `std::string_view` + in debug does error checking [unlike the C stuff] +* @param str The string to convert +* @param fmt The formatting mode +* @param end The end of the the number in the string (points to inside `sv`) +*/ +template + requires std::is_floating_point_v +T ston(std::string_view str, std::chars_format fmt = std::chars_format::general, const char** end = nullptr) { + T out; + const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), out, fmt); + assert(ec != std::errc{}); + if (end) { + *end = ptr; + } + return out; +} + +/* +* Parse a string into a 3D vector. The format is `X Y Z` (There might be multiple spaces, they're ignored) +* [On failure asserts in debug] +*/ +CVector stov3d(std::string_view str, std::chars_format fmt = std::chars_format::general) { + CVector v3d; + for (auto i = 0; i < 3; i++) { + const char* end; + v3d[i] = ston(str, fmt, &end); + if (i < 2) { + str = str.substr(end - str.data() + 1); + } + } + return v3d; +} + /* * Want to know something funny? * `std::initializer_list` is just a proxy object for a stack allocated array. @@ -18,13 +73,13 @@ namespace rng = std::ranges; * @brief Call the given function on object destruction. */ template -struct AutoCallOnDestruct { - AutoCallOnDestruct(Fn fn) : +struct ScopeGuard { + ScopeGuard(Fn fn) : m_fn{ std::move(fn) } { } - ~AutoCallOnDestruct() { + ~ScopeGuard() { std::invoke(m_fn); } diff --git a/source/game_sa/Animation/AnimBlendAssocGroup.cpp b/source/game_sa/Animation/AnimBlendAssocGroup.cpp index 268371f55e..7b5dfca110 100644 --- a/source/game_sa/Animation/AnimBlendAssocGroup.cpp +++ b/source/game_sa/Animation/AnimBlendAssocGroup.cpp @@ -34,8 +34,8 @@ void CAnimBlendAssocGroup::CreateAssociations(const char* AnimName, RpClump* clu } // 0x4CE3B0 -void CAnimBlendAssocGroup::CreateAssociations(const char* AnimName, const char* arg2, const char* arg3, int32 arg4) { - plugin::CallMethod<0x4CE3B0, CAnimBlendAssocGroup*, const char*, const char*, const char*, int32>(this, AnimName, arg2, arg3, arg4); +void CAnimBlendAssocGroup::CreateAssociations(const char* AnimName, const char* arg2, const char* arg3, uint32 strStorageSz) { + plugin::CallMethod<0x4CE3B0, CAnimBlendAssocGroup*, const char*, const char*, const char*, int32>(this, AnimName, arg2, arg3, strStorageSz); } // 0x4CDFF0 diff --git a/source/game_sa/Animation/AnimBlendAssocGroup.h b/source/game_sa/Animation/AnimBlendAssocGroup.h index 522a65317d..601ac557a4 100644 --- a/source/game_sa/Animation/AnimBlendAssocGroup.h +++ b/source/game_sa/Animation/AnimBlendAssocGroup.h @@ -26,7 +26,7 @@ class CAnimBlendAssocGroup { CAnimBlendAssociation* CopyAnimation(uint32 ID); void CreateAssociations(const char* szBlockName); void CreateAssociations(const char* AnimName, RpClump* clump, char** arg3, int32 NumAnimations); - void CreateAssociations(const char* AnimName, const char* arg2, const char* arg3, int32 arg4); + void CreateAssociations(const char* AnimName, const char* arg2, const char* arg3, uint32 arg4); void DestroyAssociations(); CAnimBlendStaticAssociation* GetAnimation(const char* AnimName); CAnimBlendStaticAssociation* GetAnimation(uint32 ID); diff --git a/source/game_sa/Animation/AnimBlendFrameData.h b/source/game_sa/Animation/AnimBlendFrameData.h index bf999afc5d..9e506b392e 100644 --- a/source/game_sa/Animation/AnimBlendFrameData.h +++ b/source/game_sa/Animation/AnimBlendFrameData.h @@ -24,8 +24,18 @@ class AnimBlendFrameData { }; uint8 m_nFlags; }; + + /* todo + union { + RwV3d_0 m_posn; + RwV3d_0 m_bonePosition; + }; + */ CVector m_vecOffset; - RpHAnimBlendInterpFrame* m_pIFrame; + union { + RpHAnimBlendInterpFrame* m_pIFrame; // TODO: Rename to `m_pStdKeyFrame` + RwFrame* m_pFrame; + }; uint32 m_nNodeId; // In case of peds it's ePedBone (NOTE: I might be wrong, see `IsPedHeadAbovePos`) // NOTSA diff --git a/source/game_sa/Animation/AnimSequenceFrames.h b/source/game_sa/Animation/AnimSequenceFrames.h index c62b916a3c..c8c6f05b93 100644 --- a/source/game_sa/Animation/AnimSequenceFrames.h +++ b/source/game_sa/Animation/AnimSequenceFrames.h @@ -77,6 +77,12 @@ struct KeyFrameTransCompressed : KeyFrameCompressed { vec->z = float(trans[2]) * scale; } + CVector GetTranslation() { + CVector out; + GetTranslation(&out); + return out; + } + void SetTranslation(const CVector& vec) { trans[0] = int16(vec.x * 1024.0f); trans[1] = int16(vec.y * 1024.0f); diff --git a/source/game_sa/Collision/TempColModels.h b/source/game_sa/Collision/TempColModels.h index 0867abd08d..0082a204ee 100644 --- a/source/game_sa/Collision/TempColModels.h +++ b/source/game_sa/Collision/TempColModels.h @@ -1,6 +1,8 @@ #pragma once #include "ColModel.h" +#include +#include "Base.h" class CTempColModels { public: @@ -16,6 +18,8 @@ class CTempColModels { static CColModel& ms_colModelBBox; static inline CColModel& ms_colModelPeds = *(CColModel*)0x968DF0; + static constexpr auto MAX_NUM_CUTSCENE_COLMODELS = MODEL_CUTOBJ20 - MODEL_CUTOBJ01; + static inline auto& ms_colModelCutObj = StaticRef, 0x968A30>(); public: static void InjectHooks(); diff --git a/source/game_sa/Core/Link.h b/source/game_sa/Core/Link.h index e107af3c42..be403d86c8 100644 --- a/source/game_sa/Core/Link.h +++ b/source/game_sa/Core/Link.h @@ -6,7 +6,8 @@ */ #pragma once -template class CLink { +template +class CLink { public: T data; CLink* prev; @@ -25,4 +26,4 @@ template class CLink { } }; -VALIDATE_SIZE(CLink, 0xC); \ No newline at end of file +VALIDATE_SIZE(CLink, 0xC); diff --git a/source/game_sa/Core/Pool.h b/source/game_sa/Core/Pool.h index 6d81470cc2..dddada9010 100644 --- a/source/game_sa/Core/Pool.h +++ b/source/game_sa/Core/Pool.h @@ -285,8 +285,8 @@ template class CPool { auto GetAllValid() { using namespace std; return span{ m_pObjects, (size_t)m_nSize } - | views::filter([this](auto&& obj) { return !IsFreeSlotAtIndex(GetIndex(&obj)); }) // Filter only slots in use - | views::transform([](auto&& obj) -> T { + | rngv::filter([this](auto&& obj) { return !IsFreeSlotAtIndex(GetIndex(&obj)); }) // Filter only slots in use + | rngv::transform([](auto&& obj) -> T { if constexpr (std::is_pointer_v) { // For pointers we also do an address-of return static_cast(&obj); } else { diff --git a/source/game_sa/Core/Vector.h b/source/game_sa/Core/Vector.h index 3d3aa05324..335be564d5 100644 --- a/source/game_sa/Core/Vector.h +++ b/source/game_sa/Core/Vector.h @@ -180,6 +180,17 @@ class CVector : public RwV3d { return { vec.x * multiplier, vec.y * multiplier, vec.z * multiplier }; } +#ifdef _DEBUG + bool HasNanOrInf() const { + for (auto i = 0; i < 3; i++) { + const auto v = (*this)[i]; + if (std::isnan(v) || std::isinf(v)) { + return true; + } + } + return false; + } +#endif }; VALIDATE_SIZE(CVector, 0xC); diff --git a/source/game_sa/CutsceneMgr.cpp b/source/game_sa/CutsceneMgr.cpp index b382c82957..0fdb6376b5 100644 --- a/source/game_sa/CutsceneMgr.cpp +++ b/source/game_sa/CutsceneMgr.cpp @@ -6,59 +6,24 @@ */ #include "StdInc.h" +#include // TODO: Remove (Included because of isForeground) + +#include + +#include "Fx.h" #include "CutsceneMgr.h" +#include "TaskSimpleCarSetPedOut.h" +#include "Rubbish.h" +#include +#include uint32 MAX_NUM_CUTSCENE_OBJECTS = 50; uint32 MAX_NUM_CUTSCENE_PARTICLE_EFFECTS = 8; uint32 MAX_NUM_CUTSCENE_ITEMS_TO_HIDE = 50; uint32 MAX_NUM_CUTSCENE_ATTACHMENTS = 50; -bool &CCutsceneMgr::ms_useCutsceneShadows = *(bool *)0x8AC158; -uint32 &CCutsceneMgr::numPlayerWeaponsToRestore = *(uint32 *)0xB5EB58; -uint32 *CCutsceneMgr::playerWeaponsToRestore_Ammo = (uint32 *)0xB5EB5C; -uint32 *CCutsceneMgr::playerWeaponsToRestore_Type = (uint32 *)0xB5EB90; -char(*CCutsceneMgr::ms_cAppendAnimName)[32] = (char(*)[32])0xB5EBC8; -char(*CCutsceneMgr::ms_cAppendObjectName)[32] = (char(*)[32])0xB5F208; -CDirectory *CCutsceneMgr::ms_pCutsceneDir = (CDirectory *)0xB5F848; -uint32 &CCutsceneMgr::ms_cutsceneLoadStatus = *(uint32 *)0xB5F84C; -bool &CCutsceneMgr::ms_animLoaded = *(bool *)0xB5F850; -bool &CCutsceneMgr::ms_running = *(bool *)0xB5F851; -bool &CCutsceneMgr::ms_cutsceneProcessing = *(bool *)0xB5F852; -bool &CCutsceneMgr::ms_useLodMultiplier = *(bool *)0xB5F853; -bool &CCutsceneMgr::ms_wasCutsceneSkipped = *(bool *)0xB5F854; -bool &CCutsceneMgr::ms_hasFileInfo = *(bool *)0xB5F855; -uint32 &CCutsceneMgr::ms_numAppendObjectNames = *(uint32 *)0xB5F858; -bool &CCutsceneMgr::restoreEverythingAfterCutscene = *(bool *)0xB5F85C; -float &CCutsceneMgr::m_fPrevCarDensity = *(float *)0xBC1D68; -float &CCutsceneMgr::m_fPrevPedDensity = *(float *)0xBC1D6C; -tCutsceneParticleEffect *CCutsceneMgr::ms_pParticleEffects = (tCutsceneParticleEffect *)0xBC1D70; -tCutsceneRemoval *CCutsceneMgr::ms_crToHideItems = (tCutsceneRemoval *)0xBC20D0; -CEntity **CCutsceneMgr::ms_pHiddenEntities = (CEntity **)0xBC2968; -uint32 &CCutsceneMgr::ms_numAttachObjectToBones = *(uint32 *)0xBC2A30; -bool *CCutsceneMgr::ms_bRepeatObject = (bool *)0xBC2A34; -tCutsceneAttachment *CCutsceneMgr::ms_iAttachObjectToBone = (tCutsceneAttachment *)0xBC2A68; -char(*CCutsceneMgr::ms_aUncompressedCutsceneAnims)[32] = (char(*)[32])0xBC2CC0; -int32 *CCutsceneMgr::ms_iTextDuration = (int32 *)0xBC2DC0; -int32 *CCutsceneMgr::ms_iTextStartTime = (int32 *)0xBC2EC0; -char(*CCutsceneMgr::ms_cTextOutput)[8] = (char(*)[8])0xBC2FC0; -int32 *CCutsceneMgr::ms_iModelIndex = (int32 *)0xBC31C0; -char(*CCutsceneMgr::ms_cLoadAnimName)[32] = (char(*)[32])0xBC3288; -char(*CCutsceneMgr::ms_cLoadObjectName)[32] = (char(*)[32])0xBC38C8; -float &CCutsceneMgr::ms_cutsceneTimer = *(float *)0xBC3F08; -char *CCutsceneMgr::ms_cutsceneName = (char *)0xBC3F0C; -uint32 &CCutsceneMgr::ms_cutscenePlayStatus = *(uint32 *)0xBC3FE0; -uint32 &CCutsceneMgr::ms_numCutsceneObjs = *(uint32 *)0xBC3FE4; -uint32 &CCutsceneMgr::ms_numLoadObjectNames = *(uint32 *)0xBC3FE8; -uint32 &CCutsceneMgr::ms_numTextOutput = *(uint32 *)0xBC3FEC; -uint32 &CCutsceneMgr::ms_currTextOutput = *(uint32 *)0xBC3FF0; -uint32 &CCutsceneMgr::ms_numUncompressedCutsceneAnims = *(uint32 *)0xBC3FF4; -uint32 &CCutsceneMgr::ms_iNumHiddenEntities = *(uint32 *)0xBC3FF8; -uint32 &CCutsceneMgr::ms_iNumParticleEffects = *(uint32 *)0xBC3FFC; -uint32 &CCutsceneMgr::m_PrevExtraColour = *(uint32 *)0xBC4000; -bool &CCutsceneMgr::m_PrevExtraColourOn = *(bool *)0xBC4004; -bool &CCutsceneMgr::dataFileLoaded = *(bool *)0xBC4006; -CAnimBlendAssocGroup &CCutsceneMgr::ms_cutsceneAssociations = *(CAnimBlendAssocGroup *)0xBC4020; -CVector &CCutsceneMgr::ms_cutsceneOffset = *(CVector *)0xBC4034; +//! Is the cutscene close to finishing (If this is set the camera is already fading in) +static inline auto& g_bCutSceneFinishing = StaticRef(); // 0x5B0380 int32 CCutsceneMgr::AddCutsceneHead(CObject* object, int32 arg1) { @@ -67,127 +32,862 @@ int32 CCutsceneMgr::AddCutsceneHead(CObject* object, int32 arg1) { // 0x4D5DB0 void CCutsceneMgr::AppendToNextCutscene(const char* objectName, const char* animName) { - plugin::Call<0x4D5DB0, const char*, const char*>(objectName, animName); + const auto CopyAndLower = [](auto&& dst, const char* src) { + strcpy_s(dst, src); + _strlwr_s(dst); + }; + + auto& num = ms_numAppendObjectNames; + CopyAndLower(ms_cAppendObjectName[num], objectName); + CopyAndLower(ms_cAppendAnimName[num], animName); + num++; } // 0x5B0450 -void CCutsceneMgr::AttachObjectToBone(CObject* attachment, CObject* object, int32 boneId) { - plugin::Call<0x5B0450, CObject*, CObject*, int32>(attachment, object, boneId); +void CCutsceneMgr::AttachObjectToBone(CCutsceneObject* attachment, CCutsceneObject* object, int32 boneId) { + attachment->m_pAttachmentObject = object; + attachment->m_nAttachBone = RpHAnimIDGetIndex(GetAnimHierarchyFromSkinClump(object->m_pRwClump), boneId); } // 0x5B0480 -void CCutsceneMgr::AttachObjectToFrame(CObject* attachment, CEntity* object, const char* frameName) { - plugin::Call<0x5B0480, CObject*, CEntity*, const char*>(attachment, object, frameName); +void CCutsceneMgr::AttachObjectToFrame(CCutsceneObject* attachment, CEntity* object, const char* frameName) { + attachment->m_pAttachmentObject = nullptr; + attachment->m_pAttachToFrame = RpAnimBlendClumpFindFrame(object->m_pRwClump, frameName)->m_pFrame; } // 0x5B04B0 -void CCutsceneMgr::AttachObjectToParent(CObject* attachment, CEntity* object) { - plugin::Call<0x5B04B0, CObject*, CEntity*>(attachment, object); +void CCutsceneMgr::AttachObjectToParent(CCutsceneObject* attachment, CEntity* object) { + attachment->m_pAttachmentObject = nullptr; + attachment->m_pAttachToFrame = RpClumpGetFrame(object->m_pRwClump); } // 0x4D5E20 void CCutsceneMgr::BuildCutscenePlayer() { - plugin::Call<0x4D5E20>(); + const auto plyr = FindPlayerPed(); + CClothes::RebuildPlayerIfNeeded(plyr); + CStreaming::RequestModel(MODEL_CSPLAY, STREAMING_PRIORITY_REQUEST | STREAMING_KEEP_IN_MEMORY | STREAMING_MISSION_REQUIRED); + CStreaming::LoadAllRequestedModels(true); + CClothes::RebuildCutscenePlayer(plyr, true); +} + +// 0x5B0130 +RpAtomic* CalculateBoundingSphereRadiusCB(RpAtomic* atomic, void* data) { + auto& maxRadius = *(float*)data; + + const auto sp = RpAtomicGetBoundingSphere(atomic); + + // Take bound sphere's center, and transform it all back to the world + auto center = sp->center; + for (RwFrame* frame = RpAtomicGetFrame(atomic); RwFrameGetParent(frame); frame = RwFrameGetParent(frame)) { + RwV3dTransformPoint(¢er, ¢er, RwFrameGetMatrix(frame)); + } + + // Not sure? I guess `center` is now just the offset from the object space center? + // Fuck knows... + maxRadius = std::max(RwV3dLength(¢er) + sp->radius, maxRadius); + + return atomic; +} + +// 0x5B01E0 +void CCutsceneMgr::UpdateCutsceneObjectBoundingBox(RpClump* clump, eModelID modelId) { + assert(IsModelIDForCutScene(modelId)); + assert(RwObjectGetType(clump) == rpCLUMP); + + // Figure out bounding sphere radius + float radius = 0.f; + RpClumpForAllAtomics(clump, CalculateBoundingSphereRadiusCB, &radius); + + // Now update the col model with it + auto& cm = CTempColModels::ms_colModelCutObj[modelId - MODEL_CUTOBJ01]; + cm.GetBoundingSphere().m_fRadius = radius; + cm.GetBoundingBox() = CBoundingBox{ // Bounding box of the sphere basically + {-radius, -radius, -radius}, + { radius, radius, radius} + }; } // 0x5B02A0 -CCutsceneObject* CCutsceneMgr::CreateCutsceneObject(int32 modelId) { - return plugin::CallAndReturn(modelId); +CCutsceneObject* CCutsceneMgr::CreateCutsceneObject(eModelID modelId) { + CStreaming::ImGonnaUseStreamingMemory(); + + // Create col model for it (If cutscene object) + if (IsModelIDForCutScene(modelId)) { + const auto mi = CModelInfo::GetModelInfo(modelId); + mi->SetColModel(&CTempColModels::ms_colModelCutObj[modelId - MODEL_CUTOBJ01]); + UpdateCutsceneObjectBoundingBox(mi->m_pRwClump, modelId); + } + + // Actually create the object now + const auto obj = ms_pCutsceneObjects[ms_numCutsceneObjs++] = new CCutsceneObject{}; + obj->SetModelIndex(modelId); + + CStreaming::IHaveUsedStreamingMemory(); + + return obj; } // 0x4D5ED0 void CCutsceneMgr::DeleteCutsceneData() { - plugin::Call<0x4D5ED0>(); + DeleteCutsceneData_overlay(); + CStreaming::SetMissionDoesntRequireModel(MODEL_CSPLAY); + if (restoreEverythingAfterCutscene) { + LoadEverythingBecauseCutsceneDeletedAllOfIt(); + } } // 0x5AFD60 void CCutsceneMgr::DeleteCutsceneData_overlay() { - plugin::Call<0x5AFD60>(); + if (ms_cutsceneLoadStatus == LoadStatus::NOT_LOADED) { + return; + } + + // We suspend the timer here (is resumed at the end) + CTimer::Suspend(); + + // Restore multipliers + CPopulation::PedDensityMultiplier = m_fPrevPedDensity; + CCarCtrl::CarDensityMultiplier = m_fPrevCarDensity; + + // Restore extra colors + if (m_PrevExtraColourOn) { + CTimeCycle::StartExtraColour(m_PrevExtraColour, false); + } else { + CTimeCycle::StopExtraColour(false); + } + + // Make hidden entities visible again + for (auto& e : ms_pHiddenEntities | rngv::take(ms_iNumHiddenEntities)) { + if (e) { + CEntity::CleanUpOldReference(e); + e->m_bIsVisible = true; + } + } + ms_iNumHiddenEntities = 0; + + // Destroy particle effects + for (auto& fx : ms_pParticleEffects | rngv::take(ms_iNumParticleEffects)) { + if (fx.m_pFxSystem) { + g_fxMan.DestroyFxSystem(fx.m_pFxSystem); + fx.m_pFxSystem = nullptr; + } + } + ms_iNumParticleEffects = 0; + + CMessages::ClearMessages(false); + CRubbish::SetVisibility(false); + + ms_cutsceneProcessing = false; + ms_useLodMultiplier = false; + + // Delete cutscene objects + for (auto& obj : ms_pCutsceneObjects | rngv::take(ms_numCutsceneObjs)) { + assert(obj); + + CWorld::Remove(obj); + obj->DeleteRwObject(); + delete obj; + } + ms_numCutsceneObjs = 0; + + // Unload anims + if (ms_animLoaded) { + CAnimManager::RemoveLastAnimFile(); + } + ms_animLoaded = false; + ms_cutsceneAssociations.DestroyAssociations(); + ms_aUncompressedCutsceneAnims[0][0] = 0; + ms_numUncompressedCutsceneAnims = 0; + + if (dataFileLoaded) { + TheCamera.RestoreWithJumpCut(); + TheCamera.SetWideScreenOff(); + TheCamera.DeleteCutSceneCamDataMemory(); + } + + CIplStore::ClearIplsNeededAtPosn(); + + ms_cutsceneLoadStatus = LoadStatus::NOT_LOADED; + ms_running = false; + + const auto plyr = FindPlayerPed(); + const auto pad = CPad::GetPad(0); + plyr->m_bIsVisible = true; + pad->bPlayerSkipsToDestination = false; + pad->Clear(false, false); // moved up here + plyr->GetPlayerInfoForThisPlayerPed()->MakePlayerSafe(0, 10000.0); + + if (!IsPlayingCSTheFinale()) { + AudioEngine.StopCutsceneTrack(true); + CAudioEngine::EnableEffectsLoading(); + CAEPedSpeechAudioEntity::EnableAllPedSpeech(); + } + + CStreaming::ms_disableStreaming = false; + CWorld::bProcessCutsceneOnly = false; + + if (dataFileLoaded) { + CGame::DrasticTidyUpMemory(TheCamera.CCamera::GetScreenFadeStatus() == NAME_FADE_IN ? true : false); + } + + // cpad clear moved up a few lines + + // there's some code to restore the player's stored weapons, but it + // doesn't seem to do anything, cause the guarding `if`'s + // condition is always false + assert(!m_bDontClearZone); + + // Tell the streamer that we don't need any (possibly) loaded special chars + for (const auto modelId : ms_iModelIndex | rngv::take(ms_numLoadObjectNames)) { + if (CTheScripts::ScriptResourceManager.HasResourceBeenRequested(modelId, RESOURCE_TYPE_MODEL_OR_SPECIAL_CHAR)) { + CStreaming::SetMissionDoesntRequireModel(modelId); + } + } + + CStreaming::SetMissionDoesntRequireModel(MODEL_CSPLAY); + CStreaming::StreamZoneModels(FindPlayerCoors()); + + CTimer::Resume(); + + CStreaming::ForceLayerToRead(true); } // 0x5B04D0 void CCutsceneMgr::FinishCutscene() { - plugin::Call<0x5B04D0>(); + if (dataFileLoaded) { + ms_cutsceneTimerS = TheCamera.GetCutSceneFinishTime() / 1000.f; + TheCamera.FinishCutscene(); + } + FindPlayerPed()->m_bIsVisible = true; + FindPlayerInfo().MakePlayerSafe(false, 10000.f); } // 0x5B0550 -long long CCutsceneMgr::GetCutsceneTimeInMilleseconds() { - return plugin::CallAndReturn(); +uint64 CCutsceneMgr::GetCutsceneTimeInMilleseconds() { + return (uint64)ms_cutsceneTimerS * 1000; } // 0x5B0570 bool CCutsceneMgr::HasCutsceneFinished() { - return plugin::CallAndReturn(); + return !dataFileLoaded || TheCamera.GetPositionAlongSpline() == 1.0;; } // 0x5AFAD0 void CCutsceneMgr::HideRequestedObjects() { - plugin::Call<0x5AFAD0>(); + if (ms_iNumHiddenEntities == 0) { + return; + } + + for (const auto& cr : ms_crToHideItems | rngv::take(ms_iNumHiddenEntities)) { + int32 modelId; + if (!CModelInfo::GetModelInfo(cr.m_szObjectName, &modelId)) { + DEV_LOG("Invalid model name(\"{}\")", cr.m_szObjectName); + continue; + } + + int16 nObjInRng; + CEntity* objInRng[32]; + CWorld::FindObjectsOfTypeInRange(modelId, cr.m_vecPosn, 1.5f, true, &nObjInRng, (int16)std::size(objInRng), objInRng, true, false, false, true, true); + for (auto e : objInRng | rngv::take((size_t)nObjInRng)) { + if (!e->m_bIsVisible) { + continue; + } + e->m_bIsVisible = false; + CEntity::SetEntityReference(ms_pHiddenEntities[ms_iNumHiddenEntities++], e); + } + } } // 0x4D5A20 void CCutsceneMgr::Initialise() { - plugin::Call<0x4D5A20>(); + ms_cutsceneLoadStatus = LoadStatus::NOT_LOADED; + ms_running = false; + ms_animLoaded = false; + ms_cutsceneProcessing = false; + ms_useLodMultiplier = false; + ms_wasCutsceneSkipped = false; + ms_hasFileInfo = false; + ms_pCutsceneDir = new CDirectory{512}; } // 0x4D5D10 bool CCutsceneMgr::IsCutsceneSkipButtonBeingPressed() { - return plugin::CallAndReturn(); + const auto pad = CPad::GetPad(0); + return pad->IsCrossPressed() + || pad->IsMouseLButtonPressed() + || pad->IsEnterJustPressed() + || pad->IsStandardKeyJustDown(' ') + || !isForeground; // ???? } // 0x4D5AB0 void CCutsceneMgr::LoadAnimationUncompressed(const char* animName) { - plugin::Call<0x4D5AB0, const char*>(animName); + DEV_LOG("Loading uncompressed anim (\"{}\")", animName); + + strcpy_s(ms_aUncompressedCutsceneAnims[ms_numUncompressedCutsceneAnims++], animName); + ms_aUncompressedCutsceneAnims[ms_numUncompressedCutsceneAnims][0] = 0; // Null terminate next } // 0x4D5E80 void CCutsceneMgr::LoadCutsceneData(const char* cutsceneName) { - plugin::Call<0x4D5E80, const char*>(cutsceneName); + DEV_LOG("Loading cutscene data (\"{}\")", cutsceneName); + + const auto plyr = FindPlayerPed(-1); + + CClothes::RebuildPlayerIfNeeded(plyr); + + CStreaming::RequestModel(MODEL_CSPLAY, STREAMING_PRIORITY_REQUEST | STREAMING_KEEP_IN_MEMORY | STREAMING_MISSION_REQUIRED); + CStreaming::LoadAllRequestedModels(true); + + CClothes::RebuildCutscenePlayer(plyr, MODEL_CSPLAY); + + if (!ms_pCutsceneDir->HasLoaded()) { + ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.IMG"); + } + + LoadCutsceneData_overlay(cutsceneName); } // 0x5B11C0 void CCutsceneMgr::LoadCutsceneData_loading() { - plugin::Call<0x5B11C0>(); + // Make sure all neceesary models are streamed in + for (const auto model : ms_iModelIndex | rngv::take(ms_numLoadObjectNames)) { + if (!CStreaming::IsModelLoaded(model)) { + return; + } + } + + // All models are streamed in now + LoadCutsceneData_postload(); + + // Load custcene anims and create objects + CCutsceneObject* obj = nullptr; + for (auto i = 0; i < ms_numLoadObjectNames; i++) { + if (!ms_bRepeatObject[i]) { + obj = CreateCutsceneObject(ms_iModelIndex[i]); + } + if (const auto& animName = ms_cLoadAnimName[i]; animName[0]) { + SetCutsceneAnim(animName, obj); + } + } + + // Create FXs that are attached to objects + for (auto& csfx : ms_pParticleEffects | rngv::take(ms_iNumParticleEffects)) { + // Create the effect's transform matrix + RwMatrix fxTransform; + g_fx.CreateMatFromVec(&fxTransform, &csfx.m_vecPosn, &csfx.m_vecDirection); + + // Find object's matrix + const auto objMat = [&]() -> RwMatrix* { + const auto objIdx = csfx.m_nObjectId; + + // If not a cutscene object we don't have an object matrix - i'm not quite sure how this works, but okay. + if (objIdx < 0 || objIdx >= ms_numCutsceneObjs + 1) { // TODO: Bug? Pretty sure the +1 is erronous... + return nullptr; + } + + // If it's a skinned object, we use bone indencies + const auto csobj = ms_pCutsceneObjects[objIdx]; + if (const auto atomic = GetFirstAtomic(csobj->m_pRwClump); atomic && RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))) { + const auto nodeIdx = notsa::ston({ csfx.m_szObjectPart }); // Obj Part is just an node index in this case + const auto hier = GetAnimHierarchyFromSkinClump(csobj->m_pRwClump); + return &RpHAnimHierarchyGetMatrixArray(hier)[RpHAnimIDGetIndex(hier, nodeIdx)]; + } + + // If not skinned, it's the name of a frame + if (const auto frame = CClumpModelInfo::GetFrameFromName(csobj->m_pRwClump, csfx.m_szObjectPart)) { + return RwFrameGetMatrix(frame); + } else { + DEV_LOG("Part(\"{}\") of object not found", csfx.m_szObjectPart); + } + + // Otherwise we're fucked + return nullptr; + }(); + + // Finally, create the fx + csfx.m_pFxSystem = g_fxMan.CreateFxSystem(csfx.m_szEffectName, &fxTransform, objMat, true); + } + + // Finally, process attachments + for (const auto& v : ms_iAttachObjectToBone | rngv::take(ms_numAttachObjectToBones)) { + AttachObjectToBone(ms_pCutsceneObjects[v.m_nCutscenePedObjectId], ms_pCutsceneObjects[v.m_nCutscenePedObjectId], v.m_nBoneId); + } } // 0x5B13F0 void CCutsceneMgr::LoadCutsceneData_overlay(const char* cutsceneName) { - plugin::Call<0x5B13F0, const char*>(cutsceneName); + DEV_LOG("LoadCutsceneData_overlay(\"{}\")", cutsceneName); + + CTimer::Suspend(); + + ms_cutsceneProcessing = true; + ms_wasCutsceneSkipped = false; + + if (!m_bDontClearZone) { + CStreaming::RemoveCurrentZonesModels(); + } + + ms_pCutsceneDir->Clear(); + ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.IMG"); + + CStreaming::RemoveUnusedModelsInLoadedList(); + CGame::DrasticTidyUpMemory(true); + strcpy_s(ms_cutsceneName, cutsceneName); + CCutsceneMgr::LoadCutsceneData_preload(); + + CTimer::Resume(); } // 0x5AFBC0 void CCutsceneMgr::LoadCutsceneData_postload() { - plugin::Call<0x5AFBC0>(); + CMessages::ClearThisPrintBigNow(STYLE_MIDDLE); + + CPopulation::PedDensityMultiplier = 0.0; + CCarCtrl::CarDensityMultiplier = 0.0; + CStreaming::ms_disableStreaming = 0; + + // Load animations for this cutscene + { + const auto stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "ANIM\\CUTS.IMG"); + const auto raii = notsa::ScopeGuard{ [&] { RwStreamClose(stream, nullptr); } }; + + char csIFPFile[1024]; + *std::format_to(csIFPFile, "{}.IFP", ms_cutsceneName) = 0; + + if (uint32 streamOffset, streamSz; ms_animLoaded = ms_pCutsceneDir->FindItem(csIFPFile, streamOffset, streamSz)) { + CStreaming::MakeSpaceFor(streamSz * STREAMING_SECTOR_SIZE / 2); // Not sure why it's only half, but okay + + CStreaming::ImGonnaUseStreamingMemory(); + + // Load ifp file + RwStreamSkip(stream, streamOffset * STREAMING_SECTOR_SIZE); + CAnimManager::LoadAnimFile(stream, 1, ms_aUncompressedCutsceneAnims.data()); + + // Now create the anims in memory + assert(ms_cLoadAnimName.size() == ms_cLoadAnimName.size()); + assert(std::size(ms_cLoadAnimName[0]) == std::size(ms_cLoadAnimName[0])); + ms_cutsceneAssociations.CreateAssociations( + ms_cutsceneName, + ms_cLoadAnimName[0], + ms_cLoadObjectName[0], + std::size(ms_cLoadAnimName[0]) + ); + + CStreaming::IHaveUsedStreamingMemory(); + } + } + + // Load camera path splines for this cutscene + { + const auto img = CFileMgr::OpenFile("ANIM\\CUTS.IMG", "rb"); + const auto raii = notsa::ScopeGuard{ [&] { CFileMgr::CloseFile(img); } }; + + char csSplinesFile[1024]; + *std::format_to(csSplinesFile, "{}.DAT", ms_cutsceneName) = 0; + + if (uint32 streamOffset, streamSz; dataFileLoaded = ms_pCutsceneDir->FindItem(csSplinesFile, streamOffset, streamSz)) { + CStreaming::ImGonnaUseStreamingMemory(); + + CFileMgr::Seek(img, streamOffset * STREAMING_SECTOR_SIZE, SEEK_SET); + TheCamera.LoadPathSplines(img); + + CStreaming::IHaveUsedStreamingMemory(); + } + } + + ms_cutsceneLoadStatus = LoadStatus::LOADED; + + ms_cutsceneTimerS = 0.f; + + FindPlayerWanted()->ClearQdCrimes(); +} + +//! NOTSA: Code from at 0x5B0794 +//! @param csFileName cutscene file name in the CUTS.IMG archive +bool CCutsceneMgr::LoadCutSceneFile(const char* csFileName) { + DEV_LOG("LoadCutSceneFile(\"{}\")", csFileName); + + uint32 csFileOffsetBytes, csFileSzBytes; + if (!ms_pCutsceneDir->FindItem(csFileName, csFileOffsetBytes, csFileSzBytes)) { + return false; + } + csFileOffsetBytes *= STREAMING_SECTOR_SIZE; + csFileSzBytes *= STREAMING_SECTOR_SIZE; + + // Allocate data for the file + auto csFileData{ std::make_unique(csFileSzBytes * STREAMING_SECTOR_SIZE) }; + + // Load cutscene data + { + const auto s = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "ANIM\\CUTS.IMG"); + RwStreamSkip(s, csFileOffsetBytes); // Skip to beginning of it + RwStreamRead(s, csFileData.get(), csFileSzBytes); + RwStreamClose(s, nullptr); + } + + // Def sections + enum Section { + NONE, + INFO, + MODEL, + TEXT, + UNCOMPRESS, + ATTACH, + REMOVE, + PEFFECT, + EXTRACOL, + MOTION // Unused section, but has to be present in order to avoid asserts + }; + + using namespace std::string_view_literals; + + // Parse this line as a section beginning + const auto StringToSection = [&](std::string_view str) { + constexpr struct { std::string_view name; Section sec; } mapping[]{ + { "info"sv, INFO }, + { "model"sv, MODEL }, + { "text"sv, TEXT }, + { "uncompress"sv, UNCOMPRESS }, + { "attach"sv, ATTACH }, + { "remove"sv, REMOVE }, + { "peffect"sv, PEFFECT }, + { "extracol"sv, EXTRACOL }, + { "motion"sv, MOTION } + }; + for (const auto& [name, sec] : mapping) { + if (str == name) { + return sec; + } + } + return NONE; + }; + + // We're cheating here, and using `CFileLoader::LoadLine`. + // They originally didn't do that. + // Now, LoadLine originally didn't handle \r\n properly, + // but we've fixed it do so. + // Oh, also, LoadLine changes `,` to ` ` (space), so + // I had to change the `,`'s in the sscanf' below to be spaces. + auto bufRemSz = (int32)csFileSzBytes; + auto bufIt = csFileData.get(); + auto curSec = NONE; + + auto hasSetExtraColors = false; + + for (auto lineno = 0; ; lineno++) { + const auto ln = CFileLoader::LoadLine(bufIt, bufRemSz); + if (!ln) { + break; // No more lines found + } + const auto lnsv = std::string_view{ ln }; + if (lnsv.empty()) { + continue; + } + + if (lnsv == "end"sv) { // End of section + curSec = NONE; + continue; + } + + switch (curSec) { + case NONE: { + curSec = StringToSection(lnsv); + assert(curSec != NONE); + break; + } + case MOTION: + break; // Unused section, so skip parsing + case INFO: { // 0x5B09F2 + if (lnsv.starts_with("offset"sv)) { + auto& o = ms_cutsceneOffset; + VERIFY(sscanf_s(ln + strlen("offset"), "%f %f %f", &o.x, &o.y, &o.z) == 3); + + // Warp ped out of the vehicle + if (const auto plyr = FindPlayerPed(); plyr->IsInVehicle()) { + CTaskSimpleCarSetPedOut{ plyr->m_pVehicle, TARGET_DOOR_FRONT_LEFT, true }.ProcessPed(plyr); + } + + // Load IPLs + CIplStore::AddIplsNeededAtPosn(ms_cutsceneOffset); + + // Load the scene now + CStreaming::SetLoadVehiclesInLoadScene(false); + CStreaming::LoadScene(ms_cutsceneOffset); + CStreaming::SetLoadVehiclesInLoadScene(true); + } else { + assert(0 && "incorrect operand type in info section"); + } + break; + } + case MODEL: { // 0x5B0B11 + char* stctx; // strok ctx + + (void)strtok_s(ln, ", ", &stctx); // ignore first value (it was originally a number, but it's unused - maybe model id?) + + // Find out model name + char modelName[1024]; + strcpy_s(modelName, strtok_s(NULL, ", ", &stctx)); // This will hard crash if no model name is set + _strlwr_s(modelName); + + // Start loading anims + bool first = true; + for(auto& numObj = ms_numLoadObjectNames; ;numObj++, first = false) { + auto pAnimName = strtok_s(NULL, ", ", &stctx); + if (!pAnimName) { + assert(!first); + break; + } + + // Store model name + strcpy_s(ms_cLoadObjectName[numObj], modelName); + + // Store anim to use + strcpy_s(ms_cLoadAnimName[numObj], pAnimName); + _strlwr_s(ms_cLoadAnimName[numObj]); + + ms_iModelIndex[numObj] = first ? MODEL_LOAD_THIS : MODEL_USE_PREV; + ms_bRepeatObject[numObj] = !first; + } + break; + } + case TEXT: { // 0x5B0C62 + auto& numTxt = ms_numTextOutput; + + int32 startTime, duration; + auto& gxtEntry = ms_cTextOutput[numTxt]; + VERIFY(sscanf_s(ln, "%d %d %s", &startTime, &duration, gxtEntry, std::size(gxtEntry)) == 3); + _strupr_s(gxtEntry); + + ms_iTextStartTime[numTxt] = startTime; + ms_iTextDuration[numTxt] = duration; + + ms_numTextOutput++; + break; + } + case UNCOMPRESS: { // 0x5B0D09 + char* ctx; + LoadAnimationUncompressed(strtok_s(ln, ", ", &ctx)); // I'm not quite sure what purpose `strtok` serves here, but okay + break; + } + case ATTACH: { // 0x5B0D40 + auto& a = ms_iAttachObjectToBone[ms_numAttachObjectToBones++]; + VERIFY(sscanf_s(ln, "%d %d %d", &a.m_nCutscenePedObjectId, &a.m_nCutsceneAttachmentObjectId, &a.m_nBoneId) == 3); + break; + } + case REMOVE: { // 0x5B0DBA + auto& cr = ms_crToHideItems[ms_iNumHiddenEntities++]; + auto& p = cr.m_vecPosn; + VERIFY(sscanf_s(ln, "%f %f %f %s", &p.x, &p.y, &p.z, cr.m_szObjectName, std::size(cr.m_szObjectName)) == 4); + break; + } + case PEFFECT: { // 0x5B0E1D + // Originally used strtok, not sure why + auto& csfx = ms_pParticleEffects[ms_iNumParticleEffects++]; + VERIFY(sscanf_s( + ln, "%s %d %d %d %s %f %f %f %f %f %f", + csfx.m_szEffectName, std::size(csfx.m_szEffectName), + &csfx.m_nStartTime, + &csfx.m_nEndTime, + &csfx.m_nObjectId, + csfx.m_szObjectPart, std::size(csfx.m_szObjectPart), + &csfx.m_vecPosn.x, &csfx.m_vecPosn.y, &csfx.m_vecPosn.z, + &csfx.m_vecDirection.x, &csfx.m_vecDirection.y, &csfx.m_vecDirection.z + ) == 11); + break; + } + case EXTRACOL: { // 0x5B099E + if (hasSetExtraColors) { // Pretty sure they fucked up something, see note below VVVV + continue; + } + int32 extraColIdx; + VERIFY(sscanf_s(ln, "%d", &extraColIdx) == 1); + if (extraColIdx) { + CTimeCycle::StartExtraColour(extraColIdx - 1, false); + hasSetExtraColors = true; + } else { + hasSetExtraColors = false; // unreachable, see note above ^^^^^^ + } + break; + } + } + } + + if (!hasSetExtraColors) { + CTimeCycle::StopExtraColour(false); + } + + return true; } // 0x5B05A0 void CCutsceneMgr::LoadCutsceneData_preload() { - plugin::Call<0x5B05A0>(); + // Preload cutscene track (Except for the "finale" cutscene) + if (!IsPlayingCSTheFinale()) { + if (const auto trkId = FindCutsceneAudioTrackId(ms_cutsceneName); trkId != -1) { + AudioEngine.PreloadCutsceneTrack(trkId, true); + } + } + + // Unload cutscene models + for (int32 i = MODEL_CUTOBJ01; i < MODEL_CUTOBJ20; i++) { + const auto mi = CModelInfo::GetModelInfo(i); + if (mi->m_nRefCount) { + continue; + } + if (!CStreaming::IsModelLoaded(i)) { + continue; + } + CStreaming::RemoveModel(i); + mi->m_nKey = CKeyGen::GetUppercaseKey("&*%"); // Set some invalid key I guess? I don't think anything like this is used anywhere :D + } + + // Save states + m_PrevExtraColour = CTimeCycle::m_ExtraColour; + m_PrevExtraColourOn = CTimeCycle::m_bExtraColourOn; + + m_fPrevCarDensity = CCarCtrl::CarDensityMultiplier; + m_fPrevPedDensity = CPopulation::PedDensityMultiplier; + + // Reset internal states + ms_cutsceneOffset = CVector{}; + ms_numTextOutput = 0; + ms_currTextOutput = 0; + ms_numLoadObjectNames = 0; + ms_numUncompressedCutsceneAnims = 0; + ms_numAttachObjectToBones = 0; + ms_iNumHiddenEntities = 0; + ms_iNumParticleEffects = 0; + g_bCutSceneFinishing = false; + + for (auto&& name : ms_aUncompressedCutsceneAnims) { // Clear all names + name[0] = 0; + } + + rng::fill(ms_iModelIndex, MODEL_PLAYER); // TODO: Change to MODEL_INVALID + + TheCamera.SetNearClipScript(0.1f); + + CRubbish::SetVisibility(false); + + FindPlayerWanted()->ClearQdCrimes(); + FindPlayerPed()->m_bIsVisible = false; + CPad::GetPad()->bPlayerSkipsToDestination = false; + FindPlayerInfo().MakePlayerSafe(true, 10000.f); + + // Load cutscene data file from `CUTS.IMG` + char csFileName[1024]; + *std::format_to(csFileName, "{}.CUT", ms_cutsceneName) = 0; + if (!LoadCutSceneFile(csFileName)) { + DEV_LOG("Failed loading cutscene(\"{}\") def", ms_cutsceneName); + return; + } + + // 0x5B1011 + // Append objects added by script to the ones added by `LoadCutSceneFile` + rng::fill(ms_iModelIndex | rngv::drop(ms_numLoadObjectNames) | rngv::take(ms_numAppendObjectNames), MODEL_LOAD_THIS); // Appended objects are loaded directly + for (auto i = 0; i < ms_numAppendObjectNames; i++) { + const auto objId = ms_numLoadObjectNames++; + strcpy_s(ms_cLoadObjectName[objId], ms_cAppendObjectName[objId]); + strcpy_s(ms_cLoadAnimName[objId], ms_cAppendAnimName[objId]); + } + ms_numAppendObjectNames = 0; + + // Request all models to be loaded [Added by `LoadCutSceneFile` and the scripts] + size_t specialModelOffset = 0; + for (auto i = 0; i < ms_numLoadObjectNames; i++) { + const auto& modelName = ms_cLoadObjectName[i]; + + if (_stricmp(modelName, "csplay") == 0) { + ms_iModelIndex[i] = MODEL_CSPLAY; + continue; + } + + switch (ms_iModelIndex[i]) { + case MODEL_LOAD_THIS: { // 0x5B10F0 - Load a new model + auto& modelId = ms_iModelIndex[i]; + + if (CModelInfo::GetModelInfo(modelName, (int32*)&modelId)) { // Regular model + CStreaming::RequestModel(modelId, STREAMING_PRIORITY_REQUEST | STREAMING_KEEP_IN_MEMORY | STREAMING_MISSION_REQUIRED); + } else { // Special model + // Figure out cut-scene model ID + modelId = (eModelID)(MODEL_CUTOBJ01 + specialModelOffset++); + + // Request the model to be loaded + CStreaming::RequestSpecialModel(modelId, modelName, STREAMING_PRIORITY_REQUEST | STREAMING_KEEP_IN_MEMORY | STREAMING_MISSION_REQUIRED); + + // Find next not-yet loaded cutscene model + while (CStreaming::IsModelLoaded((eModelID)(MODEL_CUTOBJ01 + specialModelOffset))) { // TODO/BUG: Missing check (should only try cutscene models (eg.: up to MODEL_CUTOBJ20), nothing more) + specialModelOffset++; + } + assert(specialModelOffset <= (MODEL_CUTOBJ20 - MODEL_CUTOBJ01)); + } + break; + } + case MODEL_USE_PREV: { // Just use the previous model + assert(i > 0); // The model index array should always start with `MODEL_LOAD_THIS` + ms_iModelIndex[i] = ms_iModelIndex[i - 1]; + break; + } + } + } + CStreaming::LoadAllRequestedModels(true); + + ms_cutsceneLoadStatus = LoadStatus::LOADING; } // 0x4D5C10 void CCutsceneMgr::LoadEverythingBecauseCutsceneDeletedAllOfIt() { - plugin::Call<0x4D5C10>(); + restoreEverythingAfterCutscene = false; + CStreaming::LoadInitialPeds(); + CStreaming::LoadInitialWeapons(); + + //There's some code below that is unreachable + //reason being that `numPlayerWeaponsToRestore` is only increased in a function + //that's never called (`RemoveEverythingBecauseCutsceneDoesntFitInMemory`) + assert(numPlayerWeaponsToRestore <= 0); } // 0x4D5E50 void CCutsceneMgr::RemoveCutscenePlayer() { - plugin::Call<0x4D5E50>(); + CStreaming::SetMissionDoesntRequireModel(MODEL_CSPLAY); } // 0x4D5AF0 void CCutsceneMgr::RemoveEverythingBecauseCutsceneDoesntFitInMemory() { - plugin::Call<0x4D5AF0>(); + NOTSA_UNREACHABLE(); // Unused function } // 0x5B0390 void CCutsceneMgr::SetCutsceneAnim(const char* animName, CObject* object) { - plugin::Call<0x5B0390, const char*, CObject*>(animName, object); + const auto theAnim = ms_cutsceneAssociations.GetAnimation(animName); + if (!theAnim) { + DEV_LOG("Animation (\"{}\") not found!", animName); + return; + } + + if (theAnim->m_pHierarchy->m_bRunningCompressed) { + theAnim->m_pHierarchy->m_bKeepCompressed = true; + } + + CStreaming::ImGonnaUseStreamingMemory(); + const auto cpyOfTheAnim = ms_cutsceneAssociations.CopyAnimation(animName); + CStreaming::IHaveUsedStreamingMemory(); + + cpyOfTheAnim->SetFlag(ANIMATION_TRANSLATE_Y, true); + cpyOfTheAnim->Start(0.f); + + const auto blendData = RpClumpGetAnimBlendClumpData(object->m_pRwClump); + blendData->m_Associations.Prepend(&cpyOfTheAnim->m_Link); + + if (cpyOfTheAnim->m_pHierarchy->m_bKeepCompressed) { + blendData->m_Frames->m_bIsCompressed = true; + } } // 0x5B0420 void CCutsceneMgr::SetCutsceneAnimToLoop(const char* animName) { - plugin::Call<0x5B0420, const char*>(animName); + ms_cutsceneAssociations.GetAnimation(animName)->m_nFlags |= ANIMATION_LOOPED; } // 0x5B0440 @@ -197,40 +897,237 @@ void CCutsceneMgr::SetHeadAnim(const char* animName, CObject* headObject) { // 0x5B14D0 void CCutsceneMgr::SetupCutsceneToStart() { - plugin::Call<0x5B14D0>(); + // Calculate cutscene object world positions + ms_cutsceneOffset.z += 1.f; + for (auto i = ms_numCutsceneObjs; i-- > 0;) { + const auto csobj = ms_pCutsceneObjects[i]; + assert(csobj); + + const auto SetObjPos = [csobj](const CVector& pos) { + csobj->m_vWorldPosition = pos; + csobj->SetPosn(pos); + }; + + if (const auto anim = RpAnimBlendClumpGetFirstAssociation(csobj->m_pRwClump)) { + if (csobj->m_pAttachToFrame) { + anim->SetFlag(ANIMATION_TRANSLATE_Y, false); + anim->SetFlag(ANIMATION_STARTED, true); + } else { + // Get anim translation and offset the object's position by it + const auto animTrans = anim->m_pHierarchy->m_bRunningCompressed + ? anim->m_pHierarchy->m_pSequences->GetCompressedFrame(1)->GetTranslation() + : anim->m_pHierarchy->m_pSequences->GetUncompressedFrame(1)->translation; + SetObjPos(ms_cutsceneOffset + animTrans); + + // Start the anim + anim->SetFlag(ANIMATION_STARTED, true); + } + } else { // Object has no animation applied + SetObjPos(ms_cutsceneOffset); + } + + // Add it to the world and update skinning + CWorld::Add(csobj); + if (RwObjectGetType(csobj->m_pRwObject) == rpCLUMP) { + csobj->UpdateRpHAnim(); + } + } + + CTimer::Update(); + CTimer::Update(); // unnecessary + + ms_cutsceneTimerS = 0.f; + ms_running = true; } // 0x4D5E60 void CCutsceneMgr::Shutdown() { - plugin::Call<0x4D5E60>(); + delete ms_pCutsceneDir; } // 0x5B1700 void CCutsceneMgr::SkipCutscene() { - plugin::Call<0x5B1700>(); + CHud::m_BigMessage[STYLE_BOTTOM_RIGHT][0] = 0; // todo: add CHud::ClearBigMessage + ms_wasCutsceneSkipped = true; + FinishCutscene(); } // 0x5B1460 void CCutsceneMgr::StartCutscene() { - plugin::Call<0x5B1460>(); + CCutsceneMgr::ms_cutscenePlayStatus = PlayStatus::STARTING; + if (dataFileLoaded) { + TheCamera.SetCamCutSceneOffSet(ms_cutsceneOffset); + TheCamera.TakeControlWithSpline(eSwitchType::JUMPCUT); + TheCamera.SetWideScreenOn(); + + CHud::SetHelpMessage(nullptr, true, false, false); + CPlantMgr::PreUpdateOnceForNewCameraPos(TheCamera.GetPosition()); + } } // 0x4D5D00 void CCutsceneMgr::Update() { - plugin::Call<0x4D5D00>(); + if (ms_cutsceneLoadStatus != LoadStatus::NOT_LOADED) { + Update_overlay(); + } } // 0x5B1720 void CCutsceneMgr::Update_overlay() { - plugin::Call<0x5B1720>(); + CIplStore::AddIplsNeededAtPosn(ms_cutsceneOffset); + switch (ms_cutsceneLoadStatus) { + case LoadStatus::LOADING: { + CTimer::Suspend(); + LoadCutsceneData_loading(); + CTimer::Resume(); + break; + } + case LoadStatus::LOADED: { + ms_cutscenePlayStatus = [&]{ + switch (ms_cutscenePlayStatus) { + case PlayStatus::S0: + return ms_cutscenePlayStatus; // no change + case PlayStatus::STARTING: { + SetupCutsceneToStart(); + HideRequestedObjects(); + if (!IsPlayingCSTheFinale()) { + CAudioEngine::DisableEffectsLoading(); + CAEPedSpeechAudioEntity::DisableAllPedSpeech(); + CAudioEngine::PlayPreloadedCutsceneTrack(); + } + return PlayStatus::S2; + } + case PlayStatus::S2: return PlayStatus::S3; + case PlayStatus::S3: return PlayStatus::S4; + case PlayStatus::S4: return PlayStatus::S0; + default: NOTSA_UNREACHABLE(); + } + }(); + + if (!ms_running) { + break; + } + + ms_cutsceneTimerS += CTimer::GetTimeStepNonClippedInSeconds(); + const auto csTimeMS = (int32)(ms_cutsceneTimerS * 1000.f); + + // Deal with fx [Start and stop them] + for (auto& csfx : ms_pParticleEffects | rngv::take(ms_iNumParticleEffects)) { + const auto fxsys = csfx.m_pFxSystem; + if (!fxsys) { + continue; // I'm not sure how this could happen, but okay. + } + if (csfx.m_bPlaying) { + if (csfx.m_nEndTime != -1) { // Time to end? + if (csTimeMS >= csfx.m_nEndTime + csfx.m_nStartTime) { + fxsys->Stop(); + csfx.m_bPlaying = false; + csfx.m_bStopped = true; + } + } + } else if (!csfx.m_bStopped && csTimeMS >= csfx.m_nStartTime) { // Time to start? (Only if not stopped once already) + fxsys->Play(); + csfx.m_bPlaying = true; + } + } + + // Deal with text upcoming texts + if (ms_currTextOutput < ms_numTextOutput && csTimeMS >= ms_iTextStartTime[ms_currTextOutput]) { + CMessages::AddMessageJump( + TheText.Get(ms_cTextOutput[ms_currTextOutput]), + ms_iTextDuration[ms_currTextOutput], + 1, + true + ); + ms_currTextOutput++; + } + + // Update cutscene specific model bounding boxes + for (const auto csobj : ms_pCutsceneObjects | rngv::take(ms_numCutsceneObjs)) { + if (IsModelIDForCutScene(csobj->GetModelID())) { + UpdateCutsceneObjectBoundingBox(csobj->m_pRwClump, csobj->GetModelID()); + } + } + + // Deal with cutscene skip user input + fading at the end + if (dataFileLoaded && !IsPlayingCSTheFinale()) { + if (TheCamera.GetActiveCam().m_nMode == MODE_FLYBY) { // load status check is redudant here + if (csTimeMS + 1000 > (int32)TheCamera.GetCutSceneFinishTime() && !g_bCutSceneFinishing) { + g_bCutSceneFinishing = true; + TheCamera.Fade(1.f, eFadeFlag::FADE_IN); + } + if (IsCutsceneSkipButtonBeingPressed()) { + SkipCutscene(); + } + } + } + break; + } + default: + NOTSA_UNREACHABLE(); + } +} + +bool CCutsceneMgr::IsPlayingCSTheFinale() { + return notsa::ci_string_view{ ms_cutsceneName } == "finale"; } // 0x5AFA50 int16 FindCutsceneAudioTrackId(const char* cutsceneName) { - return plugin::CallAndReturn(cutsceneName); + // from 0x8D0AA8 + constexpr struct { + notsa::ci_string_view name; + int32 id; + } mapping[]{ + { "BCESAR2", 626 }, { "BCESAR4", 627 }, { "BCESA4W", 628 }, { "BCESAR5", 629 }, { "BCESA5W", 630 }, { "BCRAS1", 631 }, { "BCRAS2", 632 }, { "BHILL1", 633 }, { "BHILL2", 634 }, { "BHILL3a", 635 }, { "BHILL3b", 636 }, { "BHILL3c", 637 }, { "BHILL5a", 638 }, { "BHILL5b", 639 }, { "CAS_1a", 640 }, { "CAS_1b", 642 }, { "CAS_2", 643 }, { "CAS_3", 644 }, { "CAS_4a", 645 }, { "CAS_4b", 646 }, { "CAS_4c", 647 }, { "CAS_5a", 648 }, { "CAS_6a", 649 }, { "CAS6b_1", 650 }, { "CAS6b_2", 651 }, { "CAS_7b", 652 }, { "CAS_9a1", 653 }, { "CAS_9a2", 654 }, { "CAS_11a", 641 }, { "CAT_1", 655 }, { "CAT_2", 656 }, { "CAT_3", 657 }, { "CAT_4", 658 }, { "CESAR1A", 659 }, { "CESAR2A", 660 }, { "CRASH1A", 661 }, { "CRASH2A", 662 }, { "CRASH3A", 663 }, { "CRASHV1", 664 }, { "CRASv2a", 665 }, { "CRASv2b", 666 }, { "D10_ALT", 667 }, { "D8_ALT", 668 }, { "DESERT1", 671 }, { "DESERT2", 672 }, { "DESERT3", 673 }, { "DESERT4", 674 }, { "DESERT6", 675 }, { "DESERT8", 676 }, { "DESERT9", 677 }, { "DES_10A", 678 }, { "DES_10B", 679 }, { "DOC_2", 680 }, { "EPILOG", 681 }, { "FARL_2A", 682 }, { "FARL_3A", 683 }, { "FARL_3B", 684 }, { "FARL_4A", 685 }, { "FARL_5A", 686 }, { "FINAL1A", 687 }, { "FINAL2A", 688 }, { "FINAL2B", 689 }, { "GARAG1B", 690 }, { "GARAG1C", 691 }, { "GARAG3A", 692 }, { "GROVE1a", 693 }, { "GROVE1b", 694 }, { "GROVE1c", 695 }, { "GROVE2", 696 }, { "HEIST1a", 697 }, { "HEIST2a", 698 }, { "HEIST4a", 699 }, { "HEIST5a", 700 }, { "HEIST6a", 701 }, { "HEIST8a", 702 }, { "INTRO1A", 703 }, { "INTRO1B", 704 }, { "INTRO2A", 705 }, { "PROLOG1", 706 }, { "PROLOG2", 707 }, { "PROLOG3", 708 }, { "RIOT_1a", 709 }, { "RIOT_1b", 710 }, { "RIOT_2", 711 }, { "RIOT_4a", 712 }, { "RIOT_4b", 713 }, { "RIOT_4c", 714 }, { "RIOT_4d", 715 }, { "RIOT4e1", 716 }, { "RIOT4e2", 717 }, { "RYDER1A", 718 }, { "RYDER2A", 719 }, { "RYDER3A", 720 }, { "SCRASH1", 721 }, { "SCRASH2", 722 }, { "SMOKE1A", 723 }, { "SMOKE1B", 724 }, { "SMOKE2A", 725 }, { "SMOKE2B", 726 }, { "SMOKE3A", 727 }, { "SMOKE4A", 728 }, { "STEAL_1", 729 }, { "STEAL_2", 730 }, { "STEAL_4", 731 }, { "STEAL_5", 732 }, { "STRAP1A", 733 }, { "STRAP2A", 734 }, { "STRAP3A", 735 }, { "STRAP4A", 736 }, { "STRP4B1", 737 }, { "STRP4B2", 738 }, { "SWEET1A", 739 }, { "SWEET1B", 740 }, { "SWEET1C", 741 }, { "SWEET2A", 742 }, { "SWEET2B", 743 }, { "SWEET3A", 744 }, { "SWEET3B", 745 }, { "SWEET4A", 746 }, { "SWEET5A", 747 }, { "SWEET6A", 748 }, { "SWEET6B", 749 }, { "SWEET7A", 750 }, { "SYND_2A", 751 }, { "SYND_2B", 752 }, { "SYND_3A", 753 }, { "SYND_4A", 754 }, { "SYND_4B", 755 }, { "SYND_7", 756 }, { "TRUTH_1", 758 }, { "TRUTH_2", 757 }, { "W2_ALT", 759 }, { "WOOZI1A", 761 }, { "WOOZI1B", 762 }, { "WOOZIE2", 760 }, { "WOOZIE4", 763 }, { "ZERO_1", 764 }, { "ZERO_2", 765 }, { "ZERO_4", 766 }, { "DATE1a", 670 }, { "DATE1b", 669 }, { "DATE2a", 670 }, { "DATE2b", 669 }, { "DATE3a", 670 }, { "DATE3b", 669 }, { "DATE4a", 670 }, { "DATE4b", 669 }, { "DATE5a", 670 }, { "DATE5b", 669 }, { "DATE6a", 670 }, { "DATE6b", 669 } + }; + const auto csName = notsa::ci_string_view{ cutsceneName }; + for (const auto& [name, id] : mapping) { + if (csName == name) { + return id; + } + } + return -1; } -// 0x5B01E0 -void UpdateCutsceneObjectBoundingBox(RpClump* clump, int32 modelId) { - plugin::Call<0x5B01E0, RpClump*, int32>(clump, modelId); +void CCutsceneMgr::InjectHooks() { + RH_ScopedClass(CCutsceneMgr); + RH_ScopedCategoryGlobal(); + + //RH_ScopedGlobalInstall(FindCutsceneAudioTrackId, 0x8D0AA8, {.locked = true}); // Calling the original function from our code crashes, and vice versa + RH_ScopedGlobalInstall(SetCutsceneAnim, 0x5B0390); + RH_ScopedGlobalInstall(SetCutsceneAnimToLoop, 0x5B0420); + RH_ScopedGlobalInstall(SetHeadAnim, 0x5B0440); + RH_ScopedGlobalInstall(AttachObjectToBone, 0x5B0450); + RH_ScopedGlobalInstall(AttachObjectToFrame, 0x5B0480); + RH_ScopedGlobalInstall(AttachObjectToParent, 0x5B04B0); + RH_ScopedGlobalInstall(AddCutsceneHead, 0x5B0380); + RH_ScopedGlobalInstall(FinishCutscene, 0x5B04D0); + RH_ScopedGlobalInstall(HasCutsceneFinished, 0x5B0570); + RH_ScopedGlobalInstall(LoadCutsceneData_preload, 0x5B05A0); + RH_ScopedGlobalInstall(LoadCutsceneData_loading, 0x5B11C0); + RH_ScopedGlobalInstall(LoadCutsceneData_overlay, 0x5B13F0); + RH_ScopedGlobalInstall(StartCutscene, 0x5B1460); + RH_ScopedGlobalInstall(SetupCutsceneToStart, 0x5B14D0); + RH_ScopedGlobalInstall(GetCutsceneTimeInMilleseconds, 0x5B0550); + RH_ScopedGlobalInstall(CreateCutsceneObject, 0x5B02A0); + RH_ScopedGlobalInstall(DeleteCutsceneData_overlay, 0x5AFD60); + RH_ScopedGlobalInstall(LoadCutsceneData_postload, 0x5AFBC0); + RH_ScopedGlobalInstall(Initialise, 0x4D5A20); + RH_ScopedGlobalInstall(LoadAnimationUncompressed, 0x4D5AB0); + RH_ScopedGlobalInstall(RemoveEverythingBecauseCutsceneDoesntFitInMemory, 0x4D5AF0); + RH_ScopedGlobalInstall(LoadEverythingBecauseCutsceneDeletedAllOfIt, 0x4D5C10); + RH_ScopedGlobalInstall(Update, 0x4D5D00); + RH_ScopedGlobalInstall(IsCutsceneSkipButtonBeingPressed, 0x4D5D10); + RH_ScopedGlobalInstall(AppendToNextCutscene, 0x4D5DB0); + RH_ScopedGlobalInstall(BuildCutscenePlayer, 0x4D5E20); + RH_ScopedGlobalInstall(RemoveCutscenePlayer, 0x4D5E50); + RH_ScopedGlobalInstall(Shutdown, 0x4D5E60); + RH_ScopedGlobalInstall(LoadCutsceneData, 0x4D5E80); + RH_ScopedGlobalInstall(DeleteCutsceneData, 0x4D5ED0); + RH_ScopedGlobalInstall(HideRequestedObjects, 0x5AFAD0); + RH_ScopedGlobalInstall(UpdateCutsceneObjectBoundingBox, 0x5B01E0); + RH_ScopedGlobalInstall(CalculateBoundingSphereRadiusCB, 0x5B0130); + RH_ScopedGlobalInstall(SkipCutscene, 0x5B1700); + RH_ScopedGlobalInstall(Update_overlay, 0x5B1720); } diff --git a/source/game_sa/CutsceneMgr.h b/source/game_sa/CutsceneMgr.h index 87e9334e84..f13a2a87ae 100644 --- a/source/game_sa/CutsceneMgr.h +++ b/source/game_sa/CutsceneMgr.h @@ -7,6 +7,7 @@ #pragma once #include "RenderWare.h" +#include class CEntity; class CObject; @@ -26,6 +27,7 @@ struct tCutsceneParticleEffect { bool m_bPlaying; bool m_bStopped; }; +VALIDATE_SIZE(tCutsceneParticleEffect, 0x6C); struct tCutsceneAttachment { int32 m_nCutscenePedObjectId; @@ -43,67 +45,96 @@ extern uint32 MAX_NUM_CUTSCENE_PARTICLE_EFFECTS; // default: 8 extern uint32 MAX_NUM_CUTSCENE_ITEMS_TO_HIDE; // default: 50 extern uint32 MAX_NUM_CUTSCENE_ATTACHMENTS; // default: 50 + class CCutsceneMgr { public: - static bool &ms_useCutsceneShadows; // always 'true', doesn't change anything - static uint32 &numPlayerWeaponsToRestore; - static uint32 *playerWeaponsToRestore_Ammo; // static uint32 playerWeaponsToRestore_Ammo[13] - static uint32 *playerWeaponsToRestore_Type; // static uint32 playerWeaponsToRestore_Type[13] - static char (*ms_cAppendAnimName)[32]; // static char ms_cAppendAnimName[50][32] - static char (*ms_cAppendObjectName)[32]; // static char ms_cAppendObjectName[50][32] - static CDirectory *ms_pCutsceneDir; - static uint32 &ms_cutsceneLoadStatus; - static bool &ms_animLoaded; - static bool &ms_running; - static bool &ms_cutsceneProcessing; - static bool &ms_useLodMultiplier; - static bool &ms_wasCutsceneSkipped; - static bool &ms_hasFileInfo; - static uint32 &ms_numAppendObjectNames; - static bool &restoreEverythingAfterCutscene; - static float &m_fPrevCarDensity; - static float &m_fPrevPedDensity; - static tCutsceneParticleEffect *ms_pParticleEffects; // static tCutsceneParticleEffect ms_pParticleEffects[8] - static tCutsceneRemoval *ms_crToHideItems; // static tCutsceneRemoval ms_crToHideItems[50] - static CEntity **ms_pHiddenEntities; // static CEntity *ms_pHiddenEntities[50] - static uint32 &ms_numAttachObjectToBones; - static bool *ms_bRepeatObject; // static bool ms_bRepeatObject[50] - static tCutsceneAttachment *ms_iAttachObjectToBone; // static tCutsceneAttachment ms_iAttachObjectToBone[] - static char (*ms_aUncompressedCutsceneAnims)[32]; // static char ms_aUncompressedCutsceneAnims[8][32] - static int32 *ms_iTextDuration; // static int32 ms_iTextDuration[64] - static int32 *ms_iTextStartTime; // static int32 ms_iTextStartTime[64] - static char (*ms_cTextOutput)[8]; // static char ms_cTextOutput[40][8] - static int32 *ms_iModelIndex; // static int32 ms_iModelIndex[50] - static char (*ms_cLoadAnimName)[32]; // static char ms_cLoadAnimName[50][32] - static char (*ms_cLoadObjectName)[32]; // static char ms_cLoadObjectName[50][32] - static float &ms_cutsceneTimer; - static char *ms_cutsceneName; // static char ms_cutsceneName[8] - static inline CCutsceneObject* (&ms_pCutsceneObjects)[50] = *(CCutsceneObject * (*)[50])0xBC3F18; - static uint32 &ms_cutscenePlayStatus; - static uint32 &ms_numCutsceneObjs; - static uint32 &ms_numLoadObjectNames; - static uint32 &ms_numTextOutput; - static uint32 &ms_currTextOutput; - static uint32 &ms_numUncompressedCutsceneAnims; - static uint32 &ms_iNumHiddenEntities; - static uint32 &ms_iNumParticleEffects; - static uint32 &m_PrevExtraColour; - static bool &m_PrevExtraColourOn; - static bool &dataFileLoaded; - static CAnimBlendAssocGroup &ms_cutsceneAssociations; - static CVector &ms_cutsceneOffset; + enum class LoadStatus { + NOT_LOADED, + LOADING, + LOADED, + }; + + enum class PlayStatus { + S0, + STARTING = 1, + S2, + S3, + S4 + }; + +public: + static inline auto& ms_useCutsceneShadows = StaticRef(); + static inline auto& numPlayerWeaponsToRestore = StaticRef(); + + static inline auto& playerWeaponsToRestore_Ammo = StaticRef, 0xB5EB5C>(); // TODO: Where does the 13 number come from? + static inline auto& playerWeaponsToRestore_Type = StaticRef, 0xB5EB90>(); // TODO: Where does the 13 number come from? + + static inline auto& ms_cAppendAnimName = StaticRef, 0xB5EBC8>(); + static inline auto& ms_cAppendObjectName = StaticRef, 0xB5F208>(); + + static inline auto& ms_pCutsceneDir = StaticRef(); + static inline auto& ms_cutsceneLoadStatus = StaticRef(); + static inline auto& ms_running = StaticRef(); + static inline auto& ms_cutsceneProcessing = StaticRef(); + static inline auto& ms_useLodMultiplier = StaticRef(); + static inline auto& ms_wasCutsceneSkipped = StaticRef(); + static inline auto& ms_hasFileInfo = StaticRef(); + static inline auto& ms_numAppendObjectNames = StaticRef(); + static inline auto& restoreEverythingAfterCutscene = StaticRef(); + static inline auto& m_fPrevCarDensity = StaticRef(); + static inline auto& m_fPrevPedDensity = StaticRef(); + static inline auto& ms_pParticleEffects = StaticRef, 0xBC1D70>(); + static inline auto& ms_crToHideItems = StaticRef, 0xBC20D0>(); + static inline auto& ms_pHiddenEntities = StaticRef, 0xBC2968>(); + static inline auto& ms_numAttachObjectToBones = StaticRef(); + static inline auto& ms_bRepeatObject = StaticRef, 0xBC2A34>(); + static inline auto& ms_iAttachObjectToBone = StaticRef, 0xBC2A68>(); + static inline auto& ms_aUncompressedCutsceneAnims = StaticRef, 0xBC2CC0>(); + static inline auto& ms_iTextDuration = StaticRef, 0xBC2DC0>(); + static inline auto& ms_iTextStartTime = StaticRef, 0xBC2EC0>(); + static inline auto& ms_cTextOutput = StaticRef, 0xBC2FC0>(); + static inline auto& ms_iModelIndex = StaticRef, 0xBC31C0>(); + static inline auto& ms_cLoadAnimName = StaticRef, 0xBC3288>(); + static inline auto& ms_cLoadObjectName = StaticRef, 0xBC38C8>(); + static inline auto& ms_cutsceneTimerS = StaticRef(); // In seconds + static inline auto& ms_cutsceneName = StaticRef(); + static inline auto& ms_pCutsceneObjects = StaticRef, 0xBC3F18>(); + static inline auto& ms_cutscenePlayStatus = StaticRef(); + static inline auto& ms_numCutsceneObjs = StaticRef(); + static inline auto& ms_numLoadObjectNames = StaticRef(); + static inline auto& ms_numTextOutput = StaticRef(); + static inline auto& ms_currTextOutput = StaticRef(); + static inline auto& ms_numUncompressedCutsceneAnims = StaticRef(); + static inline auto& ms_iNumHiddenEntities = StaticRef(); + static inline auto& ms_iNumParticleEffects = StaticRef(); + static inline auto& m_PrevExtraColour = StaticRef(); + static inline auto& m_PrevExtraColourOn = StaticRef(); + static inline auto& m_bDontClearZone = StaticRef(); + + //! If the camera splines were loaded (See `LoadCutsceneData_postload`) + //! from the .DAT file of the cutscene found in CUTS.IMG + static inline auto& dataFileLoaded = StaticRef(); + + //! If the anims were loaded (From the cutscene's .IFP file found in CUTS.IMG) + static inline auto& ms_animLoaded = StaticRef(); + + static inline auto& ms_cutsceneAssociations = StaticRef(); + static inline auto& ms_cutsceneOffset = StaticRef(); + + static void InjectHooks(); static int32 AddCutsceneHead(CObject* object, int32 arg1); static void AppendToNextCutscene(const char* objectName, const char* animName); - static void AttachObjectToBone(CObject* attachment, CObject* object, int32 boneId); - static void AttachObjectToFrame(CObject* attachment, CEntity* object, const char* frameName); - static void AttachObjectToParent(CObject* attachment, CEntity* object); + static void AttachObjectToBone(CCutsceneObject* attachment, CCutsceneObject* object, int32 boneId); + static void AttachObjectToFrame(CCutsceneObject* attachment, CEntity* object, const char* frameName); + static void AttachObjectToParent(CCutsceneObject* attachment, CEntity* object); static void BuildCutscenePlayer(); - static CCutsceneObject* CreateCutsceneObject(int32 modelId); + static void UpdateCutsceneObjectBoundingBox(RpClump* clump, eModelID modellId); + static CCutsceneObject* CreateCutsceneObject(eModelID modelId); static void DeleteCutsceneData(); static void DeleteCutsceneData_overlay(); static void FinishCutscene(); - static long long GetCutsceneTimeInMilleseconds(); + static uint64 GetCutsceneTimeInMilleseconds(); static bool HasCutsceneFinished(); static void HideRequestedObjects(); static void Initialise(); @@ -113,6 +144,7 @@ class CCutsceneMgr { static void LoadCutsceneData_loading(); static void LoadCutsceneData_overlay(const char* cutsceneName); static void LoadCutsceneData_postload(); + static bool LoadCutSceneFile(const char* csFileName); static void LoadCutsceneData_preload(); static void LoadEverythingBecauseCutsceneDeletedAllOfIt(); static void RemoveCutscenePlayer(); @@ -127,8 +159,12 @@ class CCutsceneMgr { static void Update(); static void Update_overlay(); + static bool IsRunning() { return ms_running; } static bool IsCutsceneProcessing() { return ms_cutsceneProcessing; } + static bool HasLoaded() { return ms_cutsceneLoadStatus == LoadStatus::LOADED; } + static bool IsLoading() { return ms_cutsceneLoadStatus == LoadStatus::LOADING; } + static bool IsPlayingCSTheFinale(); }; int16 FindCutsceneAudioTrackId(const char* cutsceneName); diff --git a/source/game_sa/Directory.cpp b/source/game_sa/Directory.cpp index e88d8eec0b..4e2511db87 100644 --- a/source/game_sa/Directory.cpp +++ b/source/game_sa/Directory.cpp @@ -114,7 +114,7 @@ CDirectory::DirectoryInfo* CDirectory::FindItem(const char* itemName) const { // 0x5324A0 bool CDirectory::FindItem(const char* name, uint32& outOffset, uint32& outStreamingSize) const { if (DirectoryInfo* info = FindItem(name)) { - outOffset = info->m_nOffset; + outOffset = info->m_nOffset; outStreamingSize = info->m_nStreamingSize; return true; } diff --git a/source/game_sa/Directory.h b/source/game_sa/Directory.h index b3b5ca35fb..4b74d06fdf 100644 --- a/source/game_sa/Directory.h +++ b/source/game_sa/Directory.h @@ -38,6 +38,9 @@ class CDirectory { // Also referred to as an img file bool FindItem(const char* name, uint32& outOffset, uint32& outStreamingSize) const; bool FindItem(uint32 hashKey, uint32& outOffset, uint32& outStreamingSize) const; + // notsa + bool HasLoaded() const { return m_nNumEntries != 0; } + void Clear() { m_nNumEntries = 0; } private: friend void InjectHooksMain(); static void InjectHooks(); diff --git a/source/game_sa/Entity/Entity.cpp b/source/game_sa/Entity/Entity.cpp index dc611a8249..186bd7a667 100644 --- a/source/game_sa/Entity/Entity.cpp +++ b/source/game_sa/Entity/Entity.cpp @@ -930,15 +930,11 @@ void CEntity::UpdateRwFrame() } // 0x532B20 -void CEntity::UpdateRpHAnim() -{ - auto* firstAtomic = GetFirstAtomic(m_pRwClump); - if (!firstAtomic) - return; - - if (RpSkinGeometryGetSkin(RpAtomicGetGeometry(firstAtomic)) && !m_bDontUpdateHierarchy) { - auto* animHierarchy = GetAnimHierarchyFromSkinClump(m_pRwClump); - RpHAnimHierarchyUpdateMatrices(animHierarchy); +void CEntity::UpdateRpHAnim() { + if (const auto atomic = GetFirstAtomic(m_pRwClump)) { + if (RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)) && !m_bDontUpdateHierarchy) { + RpHAnimHierarchyUpdateMatrices(GetAnimHierarchyFromSkinClump(m_pRwClump)); + } } } diff --git a/source/game_sa/Entity/Entity.h b/source/game_sa/Entity/Entity.h index 82c016aa7c..a7119319ff 100644 --- a/source/game_sa/Entity/Entity.h +++ b/source/game_sa/Entity/Entity.h @@ -222,6 +222,14 @@ class NOTSA_EXPORT_VTABLE CEntity : public CPlaceable { } } + // Similar to `ChangeEntityReference`, but doesn't clear the old reference + template + requires std::is_base_of_v && std::is_base_of_v + static void SetEntityReference(T*& inOutRef, Y* entity) { + inOutRef = entity; + inOutRef->RegisterReference(reinterpret_cast(&inOutRef)); + } + // Register a reference to the entity that is stored in that given reference template static void RegisterReference(T*& ref) requires std::is_base_of_v { diff --git a/source/game_sa/Entity/Object/CutsceneObject.cpp b/source/game_sa/Entity/Object/CutsceneObject.cpp index 99ea3455ab..4962f14f65 100644 --- a/source/game_sa/Entity/Object/CutsceneObject.cpp +++ b/source/game_sa/Entity/Object/CutsceneObject.cpp @@ -64,14 +64,14 @@ void CCutsceneObject::ProcessControl_Reversed() { } CPhysical::ProcessControl(); // exactly CPhysical - if (m_pAttachTo) { + if (m_pAttachToFrame) { if (m_pAttachmentObject) { auto* hierarchy = GetAnimHierarchyFromClump(m_pAttachmentObject->m_pRwClump); auto* matArr = RpHAnimHierarchyGetMatrixArray(hierarchy); const auto boneMat = CMatrix(&matArr[m_nAttachBone], false); *static_cast(m_matrix) = boneMat; } else { - auto* pLtm = RwFrameGetLTM(m_pAttachTo); + auto* pLtm = RwFrameGetLTM(m_pAttachToFrame); const auto attachMat = CMatrix(pLtm, false); *static_cast(m_matrix) = attachMat; } @@ -92,14 +92,14 @@ void CCutsceneObject::ProcessControl_Reversed() { // 0x5B1E00 void CCutsceneObject::PreRender_Reversed() { - if (m_pAttachTo) { + if (m_pAttachToFrame) { if (m_pAttachmentObject) { auto* hierarchy = GetAnimHierarchyFromClump(m_pAttachmentObject->m_pRwClump); auto* matArr = RpHAnimHierarchyGetMatrixArray(hierarchy); const auto boneMat = CMatrix(&matArr[m_nAttachBone], false); *static_cast(m_matrix) = boneMat; } else { - auto* pLtm = RwFrameGetLTM(m_pAttachTo); + auto* pLtm = RwFrameGetLTM(m_pAttachToFrame); const auto attachMat = CMatrix(pLtm, false); *static_cast(m_matrix) = attachMat; } diff --git a/source/game_sa/Entity/Object/CutsceneObject.h b/source/game_sa/Entity/Object/CutsceneObject.h index b06bb81049..dc48c7131a 100644 --- a/source/game_sa/Entity/Object/CutsceneObject.h +++ b/source/game_sa/Entity/Object/CutsceneObject.h @@ -11,7 +11,7 @@ class NOTSA_EXPORT_VTABLE CCutsceneObject : public CObject { public: union { - RwFrame* m_pAttachTo; + RwFrame* m_pAttachToFrame; uint32 m_nAttachBone; // this one if m_pAttachmentObject != 0 }; CObject* m_pAttachmentObject; diff --git a/source/game_sa/Enums/eModelID.h b/source/game_sa/Enums/eModelID.h index fa0e66cc77..394dbc3270 100644 --- a/source/game_sa/Enums/eModelID.h +++ b/source/game_sa/Enums/eModelID.h @@ -16,6 +16,11 @@ enum eModelID : int32 { // Number of entries: 14832 MODEL_PLAYER = 0, MODEL_CSPLAY = 1, + + // Special values used in cutscene loading + MODEL_LOAD_THIS = 2, + MODEL_USE_PREV = 3, + MODEL_MALE01 = 7, MODEL_BFORI = 9, MODEL_BFOST = 10, @@ -14855,3 +14860,7 @@ enum eModelID : int32 { // Number of entries: 14832 MODEL_CS_LANDBIT_06_A = 18629, MODEL_CS_LANDBIT_20_A = 18630, }; + +inline bool IsModelIDForCutScene(eModelID modelId) { + return modelId >= MODEL_CUTOBJ01 && modelId <= MODEL_CUTOBJ20; +} diff --git a/source/game_sa/FileLoader.cpp b/source/game_sa/FileLoader.cpp index ca880cdf24..52590a3413 100644 --- a/source/game_sa/FileLoader.cpp +++ b/source/game_sa/FileLoader.cpp @@ -224,10 +224,13 @@ char* CFileLoader::LoadLine(auto file) { return FindFirstNonNullOrWS(ms_line); } -// 0x536FE0 -// Load line from a text buffer -// bufferIt - Iterator into buffer. It is modified by this function to point after the last character of this line -// buffSize - Size of buffer. It is modified to represent the size of the buffer remaining after the end of this line +/*! +* Load line from a text buffer with sanitization (replaces chars < 32 (space) with a space) +* @param bufferIt Iterator into buffer. It is modified by this function to point after the last character of this line +* @param buffSize Size of buffer. It is modified to represent the size of the buffer remaining after the end of this line +* @returns The beginning of the line - Note, this isn't a pointer into the passed in buffer! +* @addr 0x536FE0 +*/ char* CFileLoader::LoadLine(char*& bufferIt, int32& buffSize) { if (buffSize <= 0 || !*bufferIt) return nullptr; @@ -235,6 +238,12 @@ char* CFileLoader::LoadLine(char*& bufferIt, int32& buffSize) { // Copy with sanitization (Otherwise random crashes appear) char* copyIt = s_MemoryHeapBuffer; for (; *bufferIt && *bufferIt != '\n' && buffSize != 0; bufferIt++, buffSize--) { + // Handle EOL (\r\n) correctly + // Technically a bugfix, but can't place it under the macro + // cause code(See `LoadCutSceneFile`) relies on it filtering `\r` even in vanilla mode + if (*bufferIt == '\r') { + continue; + } // Have to cast to uint8, because signed ASCII is retarded *copyIt++ = ((uint8)*bufferIt < (uint8)' ' || *bufferIt == ',') ? ' ' : *bufferIt; // Replace chars before space and ',' (comma) by space, otherwise copy } diff --git a/source/game_sa/Messages.cpp b/source/game_sa/Messages.cpp index aee1d9294b..828d9e2756 100644 --- a/source/game_sa/Messages.cpp +++ b/source/game_sa/Messages.cpp @@ -277,6 +277,7 @@ void CMessages::ClearPreviousBriefArray() { template void ClearThisPrint_Impl(std::array& messages, const char* text, auto&& OnFirstMessageCleared) { + assert(text); for (;;) { size_t i = 0; for (;;) { @@ -335,6 +336,7 @@ void CMessages::ClearThisPrint(const char* text) { // Removes big message with this text // 0x69EBE0 void CMessages::ClearThisBigPrint(const char* text) { + assert(text); for (size_t b = 0; b < NUM_MESSAGE_STYLES; b++) { ClearThisPrint_Impl(BIGMessages[b].Stack, text, [](tMessage&) { /*nop*/ }); } @@ -343,7 +345,7 @@ void CMessages::ClearThisBigPrint(const char* text) { // Removes first big message in messages stack // 0x69ED80 void CMessages::ClearThisPrintBigNow(eMessageStyle style) { - for (auto& msg : BIGMessages[style].Stack) { + if (auto& msg = BIGMessages[style].Stack[0]; msg.Text) { ClearThisBigPrint(msg.Text); } CHud::BigMessageX[style] = 0.f; diff --git a/source/game_sa/Models/BaseModelInfo.h b/source/game_sa/Models/BaseModelInfo.h index 024dc9321c..78522d02d0 100644 --- a/source/game_sa/Models/BaseModelInfo.h +++ b/source/game_sa/Models/BaseModelInfo.h @@ -135,7 +135,7 @@ class CBaseModelInfo { void AddRef(); void RemoveRef(); // initPairedModel defines if we need to set col model for time model - void SetColModel(CColModel* colModel, bool bIsLodModel); + void SetColModel(CColModel* colModel, bool bIsLodModel = false); void Init2dEffects(); void DeleteCollisionModel(); // index is a number of effect (max number is (m_n2dfxCount - 1)) diff --git a/source/game_sa/Models/VehicleModelInfo.cpp b/source/game_sa/Models/VehicleModelInfo.cpp index 7ff88956ba..6b987134d2 100644 --- a/source/game_sa/Models/VehicleModelInfo.cpp +++ b/source/game_sa/Models/VehicleModelInfo.cpp @@ -520,14 +520,12 @@ char* CVehicleModelInfo::GetCustomCarPlateText() return m_szPlateText; } -void CVehicleModelInfo::SetCustomCarPlateText(char* text) -{ - if (!text) { +void CVehicleModelInfo::SetCustomCarPlateText(char* text) { + if (text) { + strcpy_s(m_szPlateText, text); // OG code truncated to 8 chars, but we're not gonna do that (As we want to get errors instead of quietly truncating) + } else { m_szPlateText[0] = '\0'; - return; } - - strncpy_s(m_szPlateText, text, 8); } void CVehicleModelInfo::ReduceMaterialsInVehicle() diff --git a/source/game_sa/Models/VehicleModelInfo.h b/source/game_sa/Models/VehicleModelInfo.h index 3070dc3be1..7c1b0df92e 100644 --- a/source/game_sa/Models/VehicleModelInfo.h +++ b/source/game_sa/Models/VehicleModelInfo.h @@ -130,8 +130,7 @@ VALIDATE_SIZE(UpgradePosnDesc, 0x20); class NOTSA_EXPORT_VTABLE CVehicleModelInfo : public CClumpModelInfo { public: RpMaterial* m_pPlateMaterial; - char m_szPlateText[8]; - char field_30; + char m_szPlateText[9]; uint8 m_nPlateType; char m_szGameName[8]; eVehicleType m_nVehicleType; diff --git a/source/game_sa/Pickup.cpp b/source/game_sa/Pickup.cpp index 0eb44e4eb7..4e31db98ba 100644 --- a/source/game_sa/Pickup.cpp +++ b/source/game_sa/Pickup.cpp @@ -105,8 +105,9 @@ void CPickup::GiveUsAPickUpObject(CObject** obj, int32 slotIndex) { auto& object = *obj; object = nullptr; - if (CCutsceneMgr::ms_cutsceneLoadStatus == 2) + if (CCutsceneMgr::HasLoaded()) { return; + } if (mi->GetModelType() == MODEL_INFO_WEAPON) { CWeaponInfo::GetWeaponInfo(mi->AsWeaponModelInfoPtr()->m_weaponInfo); diff --git a/source/game_sa/PopCycle.cpp b/source/game_sa/PopCycle.cpp index b0852a8be1..6e0775b8af 100644 --- a/source/game_sa/PopCycle.cpp +++ b/source/game_sa/PopCycle.cpp @@ -36,7 +36,7 @@ void CPopCycle::Initialise() { const auto file = CFileMgr::OpenFile("POPCYCLE.DAT", "r"); CFileMgr::SetDir(""); - const notsa::AutoCallOnDestruct autoCloser{ [&] { CFileMgr::CloseFile(file); } }; + const notsa::ScopeGuard autoCloser{ [&] { CFileMgr::CloseFile(file); } }; auto nline{ 1u }; for (auto zone = 0; zone < (uint32)ZoneType::COUNT; zone++) { diff --git a/source/game_sa/RealTimeShadow.cpp b/source/game_sa/RealTimeShadow.cpp index 2f0c3ac47a..afb9e1b759 100644 --- a/source/game_sa/RealTimeShadow.cpp +++ b/source/game_sa/RealTimeShadow.cpp @@ -122,13 +122,16 @@ RwTexture* CRealTimeShadow::Update() { // Do blur auto raster = m_camera.GetRwRenderRaster(); + assert(raster); if (m_bBlurred) { raster = m_blurCamera.RasterResample(raster); + assert(raster); } if (m_nBlurPasses) { raster = g_realTimeShadowMan.m_BlurCamera.RasterBlur(raster, m_nBlurPasses); + assert(raster); } if (m_bDrawMoreBlur) { diff --git a/source/game_sa/Shadows.cpp b/source/game_sa/Shadows.cpp index f368e6b6d8..c29b0058ed 100644 --- a/source/game_sa/Shadows.cpp +++ b/source/game_sa/Shadows.cpp @@ -351,14 +351,22 @@ uint16 CalculateShadowStrength(float currDist, float maxDist, uint16 maxStrength // 0x707B40 void CShadows::StoreShadowForPedObject(CPed* ped, float displacementX, float displacementY, float frontX, float frontY, float sideX, float sideY) { - assert(ped->IsPed()); - - const auto bonePos = ped->GetBonePosition(BONE_NORMAL); - const auto& camPos = TheCamera.GetPosition(); - const auto pedToCamDist2DSq = (bonePos - camPos).SquaredMagnitude2D(); + // Okay, so. + // This function is called from `CCutsceneObject::PreRender_Reversed` + // And you might ask "what the fuck, an object is not a ped!!" + // well, in R* world it is. + // it has bones (as anything can have bones actually, that's a known fact) + // the `GetBonePosition` below *just works* because it doesn't access any + // `CPed` specific member variables. + // But we really should fix this in a sensible way in the future. + assert(ped->IsPed() || ped->IsObject()); + + const auto bonePos = ped->GetBonePosition(BONE_NORMAL); + const auto& camPos = TheCamera.GetPosition(); + const auto boneToCamDist2DSq = (bonePos - camPos).SquaredMagnitude2D(); // Check if ped is close enough - if (pedToCamDist2DSq >= MAX_DISTANCE_PED_SHADOWS_SQR) { + if (boneToCamDist2DSq >= MAX_DISTANCE_PED_SHADOWS_SQR) { return; } @@ -372,7 +380,7 @@ void CShadows::StoreShadowForPedObject(CPed* ped, float displacementX, float dis } // Now store a shadow to be rendered - const auto strength = (uint8)CalculateShadowStrength(std::sqrt(pedToCamDist2DSq), MAX_DISTANCE_PED_SHADOWS, CTimeCycle::m_CurrentColours.m_nShadowStrength); + const auto strength = (uint8)CalculateShadowStrength(std::sqrt(boneToCamDist2DSq), MAX_DISTANCE_PED_SHADOWS, CTimeCycle::m_CurrentColours.m_nShadowStrength); StoreShadowToBeRendered( SHADOW_DEFAULT, gpShadowPedTex, diff --git a/source/game_sa/Tasks/TaskTypes/SeekEntity/TaskComplexSeekEntity.h b/source/game_sa/Tasks/TaskTypes/SeekEntity/TaskComplexSeekEntity.h index f332954019..8ecf370e9e 100644 --- a/source/game_sa/Tasks/TaskTypes/SeekEntity/TaskComplexSeekEntity.h +++ b/source/game_sa/Tasks/TaskTypes/SeekEntity/TaskComplexSeekEntity.h @@ -250,7 +250,7 @@ class NOTSA_EXPORT_VTABLE CTaskComplexSeekEntity : public CTaskComplex { || subTaskType == TASK_SIMPLE_STAND_STILL || m_pSubTask->MakeAbortable(ped, ABORT_PRIORITY_URGENT, nullptr) ) { - notsa::AutoCallOnDestruct makePedTalkOnReturn{ [this, ped] { + notsa::ScopeGuard makePedTalkOnReturn{ [this, ped] { if (m_entityToSeek && m_entityToSeek->IsPed()) { if (m_entityToSeek->AsPed()->IsPlayer()) { // Entity to seek is a player if (FindPlayerPed()->GetPlayerGroup().GetMembership().IsFollower(ped)) { // And ped is part of the player's group diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexArrestPed.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexArrestPed.cpp index acf41217ca..ac458c41cf 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexArrestPed.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexArrestPed.cpp @@ -104,7 +104,7 @@ CTask* CTaskComplexArrestPed::ControlSubTask_Reversed(CPed* ped) { return plugin::CallMethodAndReturn(this, ped); // Automatically make ped say something on function return - const notsa::AutoCallOnDestruct Have_A_Nice_Day_Sir{ + const notsa::ScopeGuard Have_A_Nice_Day_Sir{ [this, ped] { if (m_PedToArrest && m_PedToArrest->IsPlayer()) { if (FindPlayerWanted()->m_nCopsInPursuit == 1) { diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexKillPedOnFootArmed.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexKillPedOnFootArmed.cpp index b80c63c959..5bf461727f 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexKillPedOnFootArmed.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexKillPedOnFootArmed.cpp @@ -89,7 +89,7 @@ bool CTaskComplexKillPedOnFootArmed::LineOfSightClearForAttack(CPed* ped) { // p // Temporarily disable the target ped vehicle's collision (To ignore it) // Perhaps, `CWorld::pIgnoreEntity` could be used? const auto targetVeh = m_target->GetVehicleIfInOne(); - const notsa::AutoCallOnDestruct restore{[targetVeh, had = targetVeh && targetVeh->m_bUsesCollision] { + const notsa::ScopeGuard restore{[targetVeh, had = targetVeh && targetVeh->m_bUsesCollision] { if (targetVeh) { targetVeh->m_bUsesCollision = had; } diff --git a/source/game_sa/WaterLevel.cpp b/source/game_sa/WaterLevel.cpp index f4843d9c74..2936d0f7fe 100644 --- a/source/game_sa/WaterLevel.cpp +++ b/source/game_sa/WaterLevel.cpp @@ -55,7 +55,7 @@ void CWaterLevel::InjectHooks() { bool CWaterLevel::LoadDataFile() { const auto file = CFileMgr::OpenFile(m_nWaterConfiguration == 1 ? "DATA//water1.dat" : "DATA//water.dat", "r"); - const notsa::AutoCallOnDestruct autoCloser{ [&] { CFileMgr::CloseFile(file); } }; + const notsa::ScopeGuard autoCloser{ [&] { CFileMgr::CloseFile(file); } }; uint32 nline{}, ntri{}, nquad{}; for (;; nline++) { diff --git a/source/game_sa/World.cpp b/source/game_sa/World.cpp index 9ac15925a5..5b21e65f12 100644 --- a/source/game_sa/World.cpp +++ b/source/game_sa/World.cpp @@ -651,8 +651,9 @@ void CWorld::ShutDown() { // 0x564360 void CWorld::ClearForRestart() { - if (CCutsceneMgr::ms_cutsceneLoadStatus == 2) + if (CCutsceneMgr::HasLoaded()) { CCutsceneMgr::DeleteCutsceneData(); + } CProjectileInfo::RemoveAllProjectiles(); CObject::DeleteAllTempObjects(); @@ -2834,6 +2835,8 @@ void CWorld::RepositionCertainDynamicObjects() { // 0x56BA00 bool CWorld::ProcessLineOfSight(const CVector& origin, const CVector& target, CColPoint& outColPoint, CEntity*& outEntity, bool buildings, bool vehicles, bool peds, bool objects, bool dummies, bool doSeeThroughCheck, bool doCameraIgnoreCheck, bool doShootThroughCheck) { + assert(!origin.HasNanOrInf() && !target.HasNanOrInf()); // We're getting random nan/inf's from somewhere, so let's try to root cause it... + const int32 originSectorX = GetSectorX(origin.x); const int32 originSectorY = GetSectorY(origin.y); const int32 targetSectorX = GetSectorX(target.x); diff --git a/source/game_sa/cHandlingDataMgr.cpp b/source/game_sa/cHandlingDataMgr.cpp index 7f16630643..966c8d35d1 100644 --- a/source/game_sa/cHandlingDataMgr.cpp +++ b/source/game_sa/cHandlingDataMgr.cpp @@ -180,7 +180,7 @@ void cHandlingDataMgr::LoadHandlingData() { CFileMgr::SetDir(""); // Automatically closes file on function return - const notsa::AutoCallOnDestruct closeFile{ [&] { CFileMgr::CloseFile(file); } }; + const notsa::ScopeGuard closeFile{ [&] { CFileMgr::CloseFile(file); } }; auto nLoadedHandlings{ 0u }; diff --git a/source/toolsmenu/DebugModules/Script/MissionDebugModule.cpp b/source/toolsmenu/DebugModules/Script/MissionDebugModule.cpp index 0ad06999ce..4ff5c1de00 100644 --- a/source/toolsmenu/DebugModules/Script/MissionDebugModule.cpp +++ b/source/toolsmenu/DebugModules/Script/MissionDebugModule.cpp @@ -165,7 +165,7 @@ void InitializeAndStartNewScript() { bool MissionDebugModule::StartMission(int32 missionId, bool bDoMissionCleanUp = true) { if (!m_bStartMission && CTheScripts::IsPlayerOnAMission()) { - if (CCutsceneMgr::ms_cutsceneLoadStatus == 2) { + if (CCutsceneMgr::HasLoaded()) { CCutsceneMgr::DeleteCutsceneData(); } CTheScripts::FailCurrentMission = 2; From 778ada4c5f10df5db88e548a72e212b4e565c4e7 Mon Sep 17 00:00:00 2001 From: yukani Date: Thu, 9 Mar 2023 07:51:24 +0300 Subject: [PATCH 04/19] Script Commands (#506) Co-authored-by: Pirulax --- source/Base.h | 5 + source/app/app_debug.h | 3 +- source/app/app_game.cpp | 10 +- source/config.h | 3 + source/extensions/ScriptCommands.h | 2 +- source/extensions/Shapes/AngledRect.cpp | 55 + source/extensions/Shapes/AngledRect.hpp | 62 + source/extensions/utility.hpp | 12 +- .../Animation/AnimBlendAssociation.cpp | 4 +- .../Attractors/PedAttractorPedPlacer.cpp | 9 +- .../Attractors/PedAttractorPedPlacer.h | 4 +- .../game_sa/Audio/hardware/AEAudioChannel.cpp | 2 +- source/game_sa/Collision/Box.cpp | 6 + source/game_sa/Collision/Box.h | 3 + source/game_sa/Conversations.cpp | 11 + source/game_sa/Conversations.h | 2 +- source/game_sa/Core/Matrix.h | 2 +- source/game_sa/Core/Pool.h | 6 +- source/game_sa/Core/Rect.h | 3 + source/game_sa/Core/Vector.h | 2 +- source/game_sa/Core/Vector2D.cpp | 11 +- source/game_sa/Core/Vector2D.h | 18 +- source/game_sa/Cover.cpp | 4 +- source/game_sa/DecisionMakerTypes.cpp | 12 +- source/game_sa/DecisionMakerTypes.h | 4 +- source/game_sa/Entity/Entity.cpp | 47 +- source/game_sa/Entity/Entity.h | 70 +- source/game_sa/Entity/Object/Object.h | 6 +- source/game_sa/Entity/Ped/Ped.cpp | 11 + source/game_sa/Entity/Ped/Ped.h | 265 +-- source/game_sa/Entity/Physical.h | 5 +- source/game_sa/Entity/Placeable.cpp | 2 +- source/game_sa/Entity/Placeable.h | 1 + source/game_sa/Entity/Vehicle/Automobile.h | 2 + source/game_sa/Entity/Vehicle/Bike.h | 2 + source/game_sa/Entity/Vehicle/Bmx.h | 2 + source/game_sa/Entity/Vehicle/Boat.h | 2 + source/game_sa/Entity/Vehicle/Heli.h | 2 + source/game_sa/Entity/Vehicle/MonsterTruck.h | 2 + source/game_sa/Entity/Vehicle/Plane.h | 2 + source/game_sa/Entity/Vehicle/QuadBike.h | 2 + source/game_sa/Entity/Vehicle/Trailer.h | 2 + source/game_sa/Entity/Vehicle/Train.h | 2 + source/game_sa/Entity/Vehicle/Vehicle.h | 2 + source/game_sa/Enums/ePedType.h | 9 + source/game_sa/Enums/eScriptCommands.h | 31 +- source/game_sa/Events/EventAttractor.cpp | 4 +- source/game_sa/Events/EventDamage.cpp | 9 + source/game_sa/Events/EventDamage.h | 4 + source/game_sa/Events/EventGroup.h | 6 +- source/game_sa/FileLoader.cpp | 4 +- source/game_sa/Fire.h | 3 + source/game_sa/Fx/Fx.cpp | 4 +- source/game_sa/Fx/Fx.h | 2 +- source/game_sa/GangWars.cpp | 2 +- source/game_sa/Glass.cpp | 26 +- source/game_sa/Messages.cpp | 4 +- source/game_sa/Messages.h | 4 +- source/game_sa/MissionCleanup.cpp | 28 + source/game_sa/MissionCleanup.h | 10 + source/game_sa/NodeAddress.h | 5 +- source/game_sa/PathFind.h | 4 +- source/game_sa/PedAttractorManager.cpp | 6 +- source/game_sa/PedAttractorManager.h | 2 +- source/game_sa/PedDamageResponse.h | 3 + .../game_sa/PedDamageResponseCalculator.cpp | 1 + source/game_sa/PedGeometryAnalyser.h | 5 +- source/game_sa/PedGroupMembership.cpp | 5 + source/game_sa/PedGroupMembership.h | 1 + source/game_sa/PlaceName.cpp | 6 +- .../Plugins/TwoDEffectPlugin/2dEffect.cpp | 4 +- .../Plugins/TwoDEffectPlugin/2dEffect.h | 41 +- source/game_sa/PopCycle.cpp | 2 +- source/game_sa/Population.cpp | 8 +- source/game_sa/Population.h | 2 +- source/game_sa/Rope.cpp | 8 +- source/game_sa/Ropes.cpp | 4 +- source/game_sa/Ropes.h | 11 +- source/game_sa/ScriptResourceManager.cpp | 8 +- .../Scripts/CommandParser/LUTGenerator.hpp | 13 +- .../game_sa/Scripts/CommandParser/Parser.hpp | 111 +- .../game_sa/Scripts/CommandParser/ReadArg.hpp | 384 ++-- .../Scripts/CommandParser/StoreArg.hpp | 50 +- .../game_sa/Scripts/CommandParser/Utility.hpp | 86 +- source/game_sa/Scripts/Commands/Basic.cpp | 394 ++++ source/game_sa/Scripts/Commands/Basic.hpp | 1244 ----------- .../CLEO/{AudioStream.hpp => AudioStream.cpp} | 11 +- source/game_sa/Scripts/Commands/CLEO/Car.hpp | 38 - source/game_sa/Scripts/Commands/CLEO/Char.hpp | 12 - .../Scripts/Commands/CLEO/Character.cpp | 7 + .../Scripts/Commands/CLEO/Commands.hpp | 21 + ...{DynamicLibrary.hpp => DynamicLibrary.cpp} | 9 +- .../Commands/CLEO/Extensions/CleoPlus.cpp | 7 + .../Commands/CLEO/Extensions/Clipboard.cpp | 7 + .../Commands/CLEO/Extensions/Commands.hpp | 17 + .../Scripts/Commands/CLEO/Extensions/Fs.cpp | 7 + .../Commands/CLEO/Extensions/Imgui.cpp | 7 + .../CLEO/Extensions/IntOperations.cpp | 72 + .../Scripts/Commands/CLEO/{Fs.hpp => Fs.cpp} | 12 +- source/game_sa/Scripts/Commands/CLEO/Game.cpp | 17 + source/game_sa/Scripts/Commands/CLEO/Game.hpp | 12 - .../CLEO/{Generic.hpp => Generic.cpp} | 11 +- .../game_sa/Scripts/Commands/CLEO/Memory.cpp | 94 + .../game_sa/Scripts/Commands/CLEO/Memory.hpp | 73 - source/game_sa/Scripts/Commands/CLEO/Pad.cpp | 17 + .../Commands/CLEO/{Script.hpp => Script.cpp} | 13 +- .../game_sa/Scripts/Commands/CLEO/Vehicle.cpp | 42 + .../game_sa/Scripts/Commands/CLEO/World.cpp | 33 + .../game_sa/Scripts/Commands/CLEO/World.hpp | 12 - source/game_sa/Scripts/Commands/Camera.cpp | 45 + source/game_sa/Scripts/Commands/Camera.hpp | 34 - source/game_sa/Scripts/Commands/Car.hpp | 40 - source/game_sa/Scripts/Commands/Char.hpp | 29 - source/game_sa/Scripts/Commands/Character.cpp | 1870 +++++++++++++++++ .../Scripts/Commands/{Clock.hpp => Clock.cpp} | 18 +- source/game_sa/Scripts/Commands/Commands.hpp | 27 + .../game_sa/Scripts/Commands/Comparasion.cpp | 11 + .../game_sa/Scripts/Commands/Comparasion.hpp | 5 - source/game_sa/Scripts/Commands/Game.cpp | 376 ++++ source/game_sa/Scripts/Commands/Game.hpp | 13 - .../Commands/{Generic.hpp => Generic.cpp} | 8 +- source/game_sa/Scripts/Commands/Math.cpp | 47 + source/game_sa/Scripts/Commands/Math.hpp | 10 - .../Commands/{Mission.hpp => Mission.cpp} | 22 +- source/game_sa/Scripts/Commands/Object.cpp | 296 +++ source/game_sa/Scripts/Commands/Pad.cpp | 40 + source/game_sa/Scripts/Commands/Pad.hpp | 20 - source/game_sa/Scripts/Commands/Ped.cpp | 32 + source/game_sa/Scripts/Commands/Player.cpp | 285 +++ source/game_sa/Scripts/Commands/Player.hpp | 83 - source/game_sa/Scripts/Commands/Script.cpp | 124 ++ source/game_sa/Scripts/Commands/Script.hpp | 26 - source/game_sa/Scripts/Commands/Sequence.cpp | 21 + source/game_sa/Scripts/Commands/Sequence.hpp | 17 - source/game_sa/Scripts/Commands/Text.cpp | 62 + source/game_sa/Scripts/Commands/Text.hpp | 46 - source/game_sa/Scripts/Commands/Unused.cpp | 46 + source/game_sa/Scripts/Commands/Utility.cpp | 10 + source/game_sa/Scripts/Commands/Utility.hpp | 6 - source/game_sa/Scripts/Commands/Vehicle.cpp | 124 ++ source/game_sa/Scripts/RunningScript.cpp | 338 +-- source/game_sa/Scripts/RunningScript.h | 109 +- source/game_sa/Scripts/Scripted2dEffects.cpp | 6 +- source/game_sa/Scripts/Scripted2dEffects.h | 2 +- source/game_sa/Scripts/TheScripts.cpp | 23 + source/game_sa/Scripts/TheScripts.h | 56 +- source/game_sa/SearchLight.cpp | 29 + source/game_sa/SearchLight.h | 15 + source/game_sa/Tasks/TaskManager.h | 11 + .../TaskTypes/TaskComplexUseAttractor.cpp | 2 +- source/game_sa/TheZones.cpp | 21 + source/game_sa/TheZones.h | 7 + source/game_sa/TrafficLights.cpp | 6 +- source/game_sa/World.h | 6 + source/game_sa/Zone.cpp | 2 +- source/game_sa/Zone.h | 21 +- .../ReversibleHook/ScriptCommand.h | 26 +- .../toolsmenu/DebugModules/DebugModules.cpp | 2 + .../DebugModules/ScriptDebugModule.cpp | 94 + .../DebugModules/ScriptDebugModule.hpp | 18 + source/toolsmenu/UIRenderer.h | 5 +- 161 files changed, 5901 insertions(+), 2484 deletions(-) create mode 100644 source/extensions/Shapes/AngledRect.cpp create mode 100644 source/extensions/Shapes/AngledRect.hpp create mode 100644 source/game_sa/Scripts/Commands/Basic.cpp delete mode 100644 source/game_sa/Scripts/Commands/Basic.hpp rename source/game_sa/Scripts/Commands/CLEO/{AudioStream.hpp => AudioStream.cpp} (93%) delete mode 100644 source/game_sa/Scripts/Commands/CLEO/Car.hpp delete mode 100644 source/game_sa/Scripts/Commands/CLEO/Char.hpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Character.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Commands.hpp rename source/game_sa/Scripts/Commands/CLEO/{DynamicLibrary.hpp => DynamicLibrary.cpp} (78%) create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/CleoPlus.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/Clipboard.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/Commands.hpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/Fs.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/Imgui.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Extensions/IntOperations.cpp rename source/game_sa/Scripts/Commands/CLEO/{Fs.hpp => Fs.cpp} (88%) create mode 100644 source/game_sa/Scripts/Commands/CLEO/Game.cpp delete mode 100644 source/game_sa/Scripts/Commands/CLEO/Game.hpp rename source/game_sa/Scripts/Commands/CLEO/{Generic.hpp => Generic.cpp} (84%) create mode 100644 source/game_sa/Scripts/Commands/CLEO/Memory.cpp delete mode 100644 source/game_sa/Scripts/Commands/CLEO/Memory.hpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/Pad.cpp rename source/game_sa/Scripts/Commands/CLEO/{Script.hpp => Script.cpp} (81%) create mode 100644 source/game_sa/Scripts/Commands/CLEO/Vehicle.cpp create mode 100644 source/game_sa/Scripts/Commands/CLEO/World.cpp delete mode 100644 source/game_sa/Scripts/Commands/CLEO/World.hpp create mode 100644 source/game_sa/Scripts/Commands/Camera.cpp delete mode 100644 source/game_sa/Scripts/Commands/Camera.hpp delete mode 100644 source/game_sa/Scripts/Commands/Car.hpp delete mode 100644 source/game_sa/Scripts/Commands/Char.hpp create mode 100644 source/game_sa/Scripts/Commands/Character.cpp rename source/game_sa/Scripts/Commands/{Clock.hpp => Clock.cpp} (53%) create mode 100644 source/game_sa/Scripts/Commands/Commands.hpp create mode 100644 source/game_sa/Scripts/Commands/Comparasion.cpp delete mode 100644 source/game_sa/Scripts/Commands/Comparasion.hpp create mode 100644 source/game_sa/Scripts/Commands/Game.cpp delete mode 100644 source/game_sa/Scripts/Commands/Game.hpp rename source/game_sa/Scripts/Commands/{Generic.hpp => Generic.cpp} (65%) create mode 100644 source/game_sa/Scripts/Commands/Math.cpp delete mode 100644 source/game_sa/Scripts/Commands/Math.hpp rename source/game_sa/Scripts/Commands/{Mission.hpp => Mission.cpp} (78%) create mode 100644 source/game_sa/Scripts/Commands/Object.cpp create mode 100644 source/game_sa/Scripts/Commands/Pad.cpp delete mode 100644 source/game_sa/Scripts/Commands/Pad.hpp create mode 100644 source/game_sa/Scripts/Commands/Ped.cpp create mode 100644 source/game_sa/Scripts/Commands/Player.cpp delete mode 100644 source/game_sa/Scripts/Commands/Player.hpp create mode 100644 source/game_sa/Scripts/Commands/Script.cpp delete mode 100644 source/game_sa/Scripts/Commands/Script.hpp create mode 100644 source/game_sa/Scripts/Commands/Sequence.cpp delete mode 100644 source/game_sa/Scripts/Commands/Sequence.hpp create mode 100644 source/game_sa/Scripts/Commands/Text.cpp delete mode 100644 source/game_sa/Scripts/Commands/Text.hpp create mode 100644 source/game_sa/Scripts/Commands/Unused.cpp create mode 100644 source/game_sa/Scripts/Commands/Utility.cpp delete mode 100644 source/game_sa/Scripts/Commands/Utility.hpp create mode 100644 source/game_sa/Scripts/Commands/Vehicle.cpp create mode 100644 source/game_sa/SearchLight.cpp create mode 100644 source/game_sa/SearchLight.h create mode 100644 source/toolsmenu/DebugModules/ScriptDebugModule.cpp create mode 100644 source/toolsmenu/DebugModules/ScriptDebugModule.hpp diff --git a/source/Base.h b/source/Base.h index dc017631e8..a3b99e5f7e 100644 --- a/source/Base.h +++ b/source/Base.h @@ -138,6 +138,11 @@ T& StaticRef() { #define _IGNORED_ #define _CAN_BE_NULL_ +// TODO: Use premake/cmake for this instead of relaying on `_DEBUG` +#ifdef _DEBUG +#define NOTSA_DEBUG 1 +#endif + #if (defined(__GNUC__) || defined(__GNUG__) || defined(__clang__)) #define PLUGIN_SOURCE_FILE #define PLUGIN_VARIABLE diff --git a/source/app/app_debug.h b/source/app/app_debug.h index 8351f5c11d..9784bc4c74 100644 --- a/source/app/app_debug.h +++ b/source/app/app_debug.h @@ -1,5 +1,5 @@ #pragma once -#include "common.h" +//#include "common.h" namespace notsa { namespace detail { @@ -10,6 +10,7 @@ static void VerifyMacroImpl(bool result) { }; #define VERIFY notsa::detail::VerifyMacroImpl +#define VERIFY_TODO_FIX(_expr) (_expr) // Macro used to mark shit that uses `VERIFY and sometimes fails #ifdef _DEBUG namespace notsa { diff --git a/source/app/app_game.cpp b/source/app/app_game.cpp index c972ea9447..c57f3c19cc 100644 --- a/source/app/app_game.cpp +++ b/source/app/app_game.cpp @@ -258,9 +258,6 @@ void Render2dStuff() { CDarkel::DrawMessages(); CGarages::PrintMessages(); CFont::DrawFonts(); - - // NOTSA: ImGui menu draw loop - notsa::ui::UIRenderer::GetSingleton().DrawLoop(); } // 0x53E160 @@ -357,6 +354,10 @@ void Idle(void* param) { CCredits::Render(); CDebug::DebugDisplayTextBuffer(); FlushObrsPrintfs(); + + // NOTSA: ImGui menu draw loop + notsa::ui::UIRenderer::GetSingleton().DrawLoop(); + RwCameraEndUpdate(Scene.m_pRwCamera); RsCameraShowRaster(Scene.m_pRwCamera); } @@ -409,6 +410,9 @@ void FrontendIdle() { CDebug::DebugDisplayTextBuffer(); FlushObrsPrintfs(); + // NOTSA: ImGui menu draw loop + notsa::ui::UIRenderer::GetSingleton().DrawLoop(); + RwCameraEndUpdate(Scene.m_pRwCamera); RsCameraShowRaster(Scene.m_pRwCamera); } diff --git a/source/config.h b/source/config.h index 6b2e55e588..386e2e7102 100644 --- a/source/config.h +++ b/source/config.h @@ -9,3 +9,6 @@ #ifdef BUILD_PC //#define USE_EU_STUFF #endif + +// Enables opcodes from III/VC that are not implemented in SA +#define IMPLEMENT_UNSUPPORTED_OPCODES diff --git a/source/extensions/ScriptCommands.h b/source/extensions/ScriptCommands.h index 00eb6c3190..6dc96037f7 100644 --- a/source/extensions/ScriptCommands.h +++ b/source/extensions/ScriptCommands.h @@ -82,7 +82,7 @@ class ScriptCode { // for all arguments: add them to script code code.Pack(arguments...); - script.m_pBaseIP = script.m_pCurrentIP = code.GetData(); + script.m_pBaseIP = script.m_IP = code.GetData(); script.ProcessOneCommand(); code.SaveResultVariables(&script); diff --git a/source/extensions/Shapes/AngledRect.cpp b/source/extensions/Shapes/AngledRect.cpp new file mode 100644 index 0000000000..750994f7c5 --- /dev/null +++ b/source/extensions/Shapes/AngledRect.cpp @@ -0,0 +1,55 @@ +#include + +#include "AngledRect.hpp" + + +namespace notsa { +namespace shapes { +bool AngledRect::IsPointWithin(const CVector2D& pos) const { + // Check if `pos` lies on the segment defined by `A` and `dir * mag` + const auto PointLiesOnSegment = [ + &, + posRelativeToA = pos - m_a + ](const CVector2D& dir, float mag) { + const auto dot = dir.Dot(posRelativeToA); + return dot >= 0.f && dot <= mag; + }; + + return PointLiesOnSegment(m_dirBtoA, m_height) && PointLiesOnSegment(m_dirDtoA, m_width); +} + +void AngledRect::DrawWireFrame(CRGBA color, float z, const CMatrix& transform) const { + const auto colorARGB = color.ToInt(); + + // Calculate corners + CVector corners[4]; + rng::transform(GetCorners(), corners, [&](auto& c) { + return transform * CVector{ c.x, c.y, z }; + }); + + // Draw lines going from one corner to another + for (auto i = 0u; i < 4; i++) { + CLines::RenderLineNoClipping(corners[i], corners[(i + 1) % 4], colorARGB, colorARGB); + } + + // Draw diagonal line + CLines::RenderLineNoClipping(corners[0], corners[2], colorARGB, colorARGB); +} + +void AngledRect::HighlightWithMarkers(const CMatrix& transform) const { + for (const auto& corner : GetCorners()) { + auto corner3D = transform * CVector{ corner, CWorld::FindGroundZForCoord(corner.x, corner.y) }; + C3dMarkers::PlaceMarkerSet( + reinterpret_cast(this) + rand(), + MARKER3D_CYLINDER, + corner3D, + 0.2f, + 255, 0, 255, 150, + 2048, + 0.2f, + 0 + ); + } +} +}; // namespace shapes +}; // namespace notsa diff --git a/source/extensions/Shapes/AngledRect.hpp b/source/extensions/Shapes/AngledRect.hpp new file mode 100644 index 0000000000..eb65cbe326 --- /dev/null +++ b/source/extensions/Shapes/AngledRect.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include +#include +#include +#include + + +namespace notsa { +namespace shapes { +/*! +* Represents an angled rectangle (basically a quad with all right angles): +* /[B]-----------[C]\ +* | | +* | | +* | | +* | | +* \[A]----[D]/ +* `Width` may be negative (in which case `C` on the __above__ rectangle would be on the left instead of the right) +* just a note: `A` might be on the bottom too (so `B` may be in the top). +* But `C` is always calculated based on `A`'s position, and it's always either on the left or right of it. +*/ +class AngledRect { +public: + /*! + * @param a The A corner + * @param b The B corner + * @param widthAndDir Width and direction of the C point relative to A. + * If positive the C point will be to the left of the AB segment, otherwise to the right. + */ + AngledRect(CVector2D a, CVector2D b, float widthAndDir) : + m_a{ a }, + m_b{ b }, + m_dirBtoA{ (b - a).Normalized(&m_height) }, + m_dirDtoA{ widthAndDir >= 0 ? m_dirBtoA.GetPerpLeft() : m_dirBtoA.GetPerpRight() }, + m_width{ std::abs(widthAndDir) } + { + } + + auto GetCornerA() const { return m_a; } + auto GetCornerB() const { return m_b; } + auto GetCornerC() const { return m_b + m_dirDtoA * m_width; } + auto GetCornerD() const { return m_a + m_dirDtoA * m_width; } + auto GetCorners() const { return std::to_array({ GetCornerA(), GetCornerB(), GetCornerC(), GetCornerD() }); } + + //! Check if a point is within this quad + bool IsPointWithin(const CVector2D& pos) const; + + //! Draw wireframe of this quad (Must set-up render states beforehands!) + void DrawWireFrame(CRGBA color, float z, const CMatrix& transform = CMatrix::Unity()) const; + + //! Highlight this rect with markers in each corner + void HighlightWithMarkers(const CMatrix& transform = CMatrix::Unity()) const; +private: + CVector2D m_a{}, m_b{}; //< Corner A and B + CVector2D m_dirBtoA{}, m_dirDtoA{}; //< Directions to/from corners `C -> A` is the same as `B -> D` + float m_width{}, m_height{}; //< Width and height (both always positive) +}; +}; +}; // namespace notsa diff --git a/source/extensions/utility.hpp b/source/extensions/utility.hpp index 9aa7d02674..947aeacf99 100644 --- a/source/extensions/utility.hpp +++ b/source/extensions/utility.hpp @@ -8,6 +8,16 @@ namespace notsa { +//template +//struct basic_static_string { +// template +// friend std::strong_ordering operator<=>(const basic_static_string& self, std::basic_string_view sv) { +// sv.compare(std::basic_string_view{m_chars}); +// } +// +//private: +// TChar m_chars[N]{}; +//}; namespace rng = std::ranges; /*! @@ -255,7 +265,7 @@ static constexpr void IterateFunction(auto&& functor) { // Continue recursing if there's anything left if constexpr (Stop - Start > ChunkSize) { - IterateFunction(functor); + IterateFunction(functor); } } diff --git a/source/game_sa/Animation/AnimBlendAssociation.cpp b/source/game_sa/Animation/AnimBlendAssociation.cpp index 544b7566ae..e605eeed62 100644 --- a/source/game_sa/Animation/AnimBlendAssociation.cpp +++ b/source/game_sa/Animation/AnimBlendAssociation.cpp @@ -200,9 +200,7 @@ void CAnimBlendAssociation::SetCurrentTime(float currentTime) { m_pNodeArray[i].SetupKeyFrameCompressed(); } } - } else - // - { + } else { for (auto i = 0; i < m_nNumBlendNodes; i++) { if (m_pNodeArray[i].m_pAnimSequence) { m_pNodeArray[i].FindKeyFrame(m_fCurrentTime); diff --git a/source/game_sa/Attractors/PedAttractorPedPlacer.cpp b/source/game_sa/Attractors/PedAttractorPedPlacer.cpp index 8a1bc8de76..06641e07cf 100644 --- a/source/game_sa/Attractors/PedAttractorPedPlacer.cpp +++ b/source/game_sa/Attractors/PedAttractorPedPlacer.cpp @@ -1,6 +1,9 @@ -#include "StdInc.h" +#include +#include + #include "PedAttractorPedPlacer.h" + void CPedAttractorPedPlacer::InjectHooks() { RH_ScopedClass(CPedAttractorPedPlacer); RH_ScopedCategory("Attractors"); @@ -9,6 +12,6 @@ void CPedAttractorPedPlacer::InjectHooks() { } // 0x5EA390 -void CPedAttractorPedPlacer::PlacePedAtEffect(C2dEffect const& effect, CEntity* entity, CPed* ped, float forwardOffsetMultiplier) { - return plugin::CallAndReturn(effect, entity, ped, forwardOffsetMultiplier); +void CPedAttractorPedPlacer::PlacePedAtEffect(C2dEffectPedAttractor const& effect, CEntity* entity, CPed* ped, float forwardOffsetMultiplier) { + return plugin::CallAndReturn(effect, entity, ped, forwardOffsetMultiplier); } diff --git a/source/game_sa/Attractors/PedAttractorPedPlacer.h b/source/game_sa/Attractors/PedAttractorPedPlacer.h index a598d775db..ebde6f8248 100644 --- a/source/game_sa/Attractors/PedAttractorPedPlacer.h +++ b/source/game_sa/Attractors/PedAttractorPedPlacer.h @@ -2,11 +2,11 @@ class CPed; class CEntity; -class C2dEffect; +class C2dEffectPedAttractor; class CPedAttractorPedPlacer { public: static void InjectHooks(); - static void PlacePedAtEffect(C2dEffect const& effect, CEntity* entity, CPed* ped, float forwardOffsetMultiplier); + static void PlacePedAtEffect(C2dEffectPedAttractor const& effect, CEntity* entity, CPed* ped, float forwardOffsetMultiplier); }; diff --git a/source/game_sa/Audio/hardware/AEAudioChannel.cpp b/source/game_sa/Audio/hardware/AEAudioChannel.cpp index 23bdf85eb5..75a9622115 100644 --- a/source/game_sa/Audio/hardware/AEAudioChannel.cpp +++ b/source/game_sa/Audio/hardware/AEAudioChannel.cpp @@ -162,7 +162,7 @@ void CAEAudioChannel::SetFrequency(uint32 freq) { #ifdef USE_DSOUND if (m_pDirectSoundBuffer) { - VERIFY(SUCCEEDED(m_pDirectSoundBuffer->SetFrequency(freq))); + VERIFY_TODO_FIX(SUCCEEDED(m_pDirectSoundBuffer->SetFrequency(freq))); } #endif } diff --git a/source/game_sa/Collision/Box.cpp b/source/game_sa/Collision/Box.cpp index 055e124709..6af88d7ff3 100644 --- a/source/game_sa/Collision/Box.cpp +++ b/source/game_sa/Collision/Box.cpp @@ -77,3 +77,9 @@ void CBox::DrawWireFrame(CRGBA color, const CMatrix& transform) const { CLines::RenderLineNoClipping(v7, v3, colorARGB, colorARGB); CLines::RenderLineNoClipping(v7, v4, colorARGB, colorARGB); } + +bool CBox::IsPointInside(const CVector& point) const { + return point.x >= m_vecMin.x && point.x <= m_vecMax.x + && point.y >= m_vecMin.y && point.y <= m_vecMax.y + && point.z >= m_vecMin.z && point.z <= m_vecMax.z; +} diff --git a/source/game_sa/Collision/Box.h b/source/game_sa/Collision/Box.h index 44ca184108..0171a67886 100644 --- a/source/game_sa/Collision/Box.h +++ b/source/game_sa/Collision/Box.h @@ -27,6 +27,9 @@ class CBox { float GetHeight() const { return m_vecMax.z - m_vecMin.z; } CVector GetCenter() const { return (m_vecMax + m_vecMin) / 2.f; } + //! Check is point within the box + bool IsPointInside(const CVector& point) const; + /*! * @addr notsa * @brief Render the box in the 3D world (Be sure to call from a place where 3D stuff is rendered, if called from elsewhere you won't see the lines!) diff --git a/source/game_sa/Conversations.cpp b/source/game_sa/Conversations.cpp index c3663f5de4..befffc6349 100644 --- a/source/game_sa/Conversations.cpp +++ b/source/game_sa/Conversations.cpp @@ -18,6 +18,17 @@ void CConversations::Update() { plugin::Call<0x43C590>(); } +// 0x43A870 +void CConversations::SetUpConversationNode( + const char* questionKey, + const char* answerYesKey, + const char* answerNoKey, + int32 questionWAV, + int32 answerYesWAV, + int32 answerNoWAV) { + plugin::Call<0x43A870, const char*, const char*, const char*, int32, int32, int32>(questionKey, answerYesKey, answerNoKey, questionWAV, answerYesWAV, answerNoWAV); +} + // 0x43A960 void CConversations::RemoveConversationForPed(CPed* ped) { plugin::Call<0x43A960, CPed*>(ped); diff --git a/source/game_sa/Conversations.h b/source/game_sa/Conversations.h index ebf869868d..3958bd8782 100644 --- a/source/game_sa/Conversations.h +++ b/source/game_sa/Conversations.h @@ -16,6 +16,7 @@ class CConversations { static void RemoveConversationForPed(CPed* ped); static void Update(); + static void SetUpConversationNode(const char*, const char*, const char*, int32, int32, int32); /* Check the signatures before starting work static bool IsPlayerInPositionForConversation(CPed* ped, bool); static bool IsConversationGoingOn(); @@ -26,7 +27,6 @@ class CConversations { static void FindFreeNodeSlot(); static void FindConversationForPed(CPed* ped); static void FindFreeConversationSlot(); - static void SetUpConversationNode(char*, char*, char*, int32, int32, int32); static void StartSettingUpConversation(CPed* ped); static void AwkwardSay(int32, CPed* ped); */ diff --git a/source/game_sa/Core/Matrix.h b/source/game_sa/Core/Matrix.h index 89f4e8c5db..2b46e39dba 100644 --- a/source/game_sa/Core/Matrix.h +++ b/source/game_sa/Core/Matrix.h @@ -116,7 +116,7 @@ class CMatrix { void SetRotateX(float angle); void SetRotateY(float angle); void SetRotateZ(float angle); - void SetRotate(float x, float y, float z); // set rotate on 3 axes + void SetRotate(float x, float y, float z); // set rotate on 3 axes (Values are in radians) void RotateX(float angle); void RotateY(float angle); void RotateZ(float angle); diff --git a/source/game_sa/Core/Pool.h b/source/game_sa/Core/Pool.h index dddada9010..7e0b9c7254 100644 --- a/source/game_sa/Core/Pool.h +++ b/source/game_sa/Core/Pool.h @@ -127,9 +127,9 @@ template class CPool { } // Returns slot index for this object - int32 GetIndex(A* obj) { + int32 GetIndex(const A* obj) { assert(IsFromObjectArray(obj)); - return reinterpret_cast(obj) - m_pObjects; + return reinterpret_cast(obj) - m_pObjects; } // Returns pointer to object by slot index @@ -212,7 +212,7 @@ template class CPool { } // Returns SCM handle (ref) for object (0x424160) - int32 GetRef(A* obj) { + int32 GetRef(const A* obj) { const auto idx = GetIndex(obj); return (idx << 8) + m_byteMap[idx].IntValue(); } diff --git a/source/game_sa/Core/Rect.h b/source/game_sa/Core/Rect.h index 544e0366b8..7745d64231 100644 --- a/source/game_sa/Core/Rect.h +++ b/source/game_sa/Core/Rect.h @@ -65,6 +65,9 @@ class CRect { [[nodiscard]] inline CVector2D GetCenter() const { return { (right + left) * 0.5F, (bottom + top) * 0.5F }; } void StretchToPoint(float x, float y); + CVector2D GetTopLeft() const { return { left, top }; } + CVector2D GetBottomRight() const { return { right, bottom }; } + /*! * @addr notsa * @brief Constrain a point into the rectangle. diff --git a/source/game_sa/Core/Vector.h b/source/game_sa/Core/Vector.h index 335be564d5..2748e41ba2 100644 --- a/source/game_sa/Core/Vector.h +++ b/source/game_sa/Core/Vector.h @@ -160,7 +160,7 @@ class CVector : public RwV3d { /*! * @param reMapRangeTo0To2Pi Return value will be in interval [0, 2pi] instead of [-pi, pi] - * @returning The heading of the vector in radians. + * @return The heading of the vector in radians. */ [[nodiscard]] float Heading(bool reMapRangeTo0To2Pi = false) const; diff --git a/source/game_sa/Core/Vector2D.cpp b/source/game_sa/Core/Vector2D.cpp index 9fb1a1f05d..85d7976c5d 100644 --- a/source/game_sa/Core/Vector2D.cpp +++ b/source/game_sa/Core/Vector2D.cpp @@ -23,15 +23,18 @@ CVector2D::CVector2D(const CVector& v3) : { } -void CVector2D::Normalise() { - auto len = Magnitude(); - if (len > 0.0f) { - auto recip = 1.0F / len; +void CVector2D::Normalise(float* outMag) { + auto mag = Magnitude(); + if (mag > 0.0f) { + auto recip = 1.0F / mag; x *= recip; y *= recip; } else { x = 1.0f; } + if (outMag) { + *outMag = mag; + } } uint32 CVector2D::NodeHeading() const { diff --git a/source/game_sa/Core/Vector2D.h b/source/game_sa/Core/Vector2D.h index 27dda95ab9..a5cc4a239a 100644 --- a/source/game_sa/Core/Vector2D.h +++ b/source/game_sa/Core/Vector2D.h @@ -29,13 +29,21 @@ class CVector2D : public RwV2d { static void InjectHooks(); - /// Normalize this vector in-place - void Normalise(); + /*! + * @brief Normalize this vector in-place + * + * @param [opt, out, notsa] outMag The magnitude of the vector + */ + void Normalise(float* outMag = nullptr); - /// Get a normalized copy of this vector - auto Normalized() const { + /*! + * @brief Get a normalized copy of this vector + * + * @param [opt, out] mag The magnitude of the vector + */ + auto Normalized(float* outMag = nullptr) const { CVector2D cpy = *this; - cpy.Normalise(); + cpy.Normalise(outMag); return cpy; } diff --git a/source/game_sa/Cover.cpp b/source/game_sa/Cover.cpp index 403f954bf3..21195c6a15 100644 --- a/source/game_sa/Cover.cpp +++ b/source/game_sa/Cover.cpp @@ -99,7 +99,7 @@ void CCover::FindCoverPointsForThisBuilding(CBuilding* building) { for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto* effect = mi->Get2dEffect(iFxInd); - if (effect->m_nType != e2dEffectType::EFFECT_COVER_POINT) + if (effect->m_type != e2dEffectType::EFFECT_COVER_POINT) continue; auto vecDir = CVector(effect->coverPoint.m_vecDirection.x, effect->coverPoint.m_vecDirection.y, 0.0F); @@ -107,7 +107,7 @@ void CCover::FindCoverPointsForThisBuilding(CBuilding* building) { const auto fTwoPiToChar = 256.0F / TWO_PI; const auto ucAngle = static_cast(atan2(vedTransformed.x, vedTransformed.y) * fTwoPiToChar); - auto vecPoint = building->GetMatrix() * effect->m_vecPosn; + auto vecPoint = building->GetMatrix() * effect->m_pos; CCover::AddCoverPoint(3, building, &vecPoint, effect->coverPoint.m_nType, ucAngle); } } diff --git a/source/game_sa/DecisionMakerTypes.cpp b/source/game_sa/DecisionMakerTypes.cpp index d08e19864b..e4db71459a 100644 --- a/source/game_sa/DecisionMakerTypes.cpp +++ b/source/game_sa/DecisionMakerTypes.cpp @@ -10,7 +10,7 @@ void CDecisionMakerTypesFileLoader::InjectHooks() { RH_ScopedInstall(GetPedDMName, 0x600860, {.reversed = false}); RH_ScopedInstall(GetGrpDMName, 0x600880, {.reversed = false}); RH_ScopedInstall(LoadDefaultDecisionMaker, 0x5BF400, {.reversed = false}); - RH_ScopedOverloadedInstall(LoadDecisionMaker, "enum", 0x607D30, void(*)(const char*, eDecisionTypes, bool)); + RH_ScopedOverloadedInstall(LoadDecisionMaker, "enum", 0x607D30, int32(*)(const char*, eDecisionTypes, bool)); RH_ScopedOverloadedInstall(LoadDecisionMaker, "ptr", 0x6076B0, void (*)(const char*, CDecisionMaker*), {.reversed = false}); } @@ -34,11 +34,11 @@ void CDecisionMakerTypesFileLoader::LoadDefaultDecisionMaker() { plugin::Call<0x5BF400>(); } -// 0x607D30 -void CDecisionMakerTypesFileLoader::LoadDecisionMaker(const char* filepath, eDecisionTypes decisionMakerType, bool bUseMissionCleanup) { +// 0x607D30 - Returns script handle for the DM +int32 CDecisionMakerTypesFileLoader::LoadDecisionMaker(const char* filepath, eDecisionTypes decisionMakerType, bool bUseMissionCleanup) { CDecisionMaker decisionMaker; LoadDecisionMaker(filepath, &decisionMaker); - CDecisionMakerTypes::GetInstance()->AddDecisionMaker(&decisionMaker, decisionMakerType, bUseMissionCleanup); + return CDecisionMakerTypes::GetInstance()->AddDecisionMaker(&decisionMaker, decisionMakerType, bUseMissionCleanup); } // 0x6076B0 @@ -52,8 +52,8 @@ void CDecisionMakerTypes::InjectHooks() { } // 0x607050 -void CDecisionMakerTypes::AddDecisionMaker(CDecisionMaker* decisionMaker, eDecisionTypes decisionMakerType, bool bUseMissionCleanup) { - plugin::CallMethod<0x607050, CDecisionMakerTypes*, CDecisionMaker*, eDecisionTypes, bool>(this, decisionMaker, decisionMakerType, bUseMissionCleanup); +int32 CDecisionMakerTypes::AddDecisionMaker(CDecisionMaker* decisionMaker, eDecisionTypes decisionMakerType, bool bUseMissionCleanup) { + return plugin::CallMethodAndReturn(this, decisionMaker, decisionMakerType, bUseMissionCleanup); } // 0x4684F0 diff --git a/source/game_sa/DecisionMakerTypes.h b/source/game_sa/DecisionMakerTypes.h index da2c11d6c1..bffe2a0693 100644 --- a/source/game_sa/DecisionMakerTypes.h +++ b/source/game_sa/DecisionMakerTypes.h @@ -14,7 +14,7 @@ class CDecisionMakerTypesFileLoader { static void GetPedDMName(int32 index, char* name); static void GetGrpDMName(int32 index, char* name); static void LoadDefaultDecisionMaker(); - static void LoadDecisionMaker(const char* filepath, eDecisionTypes decisionMakerType, bool bUseMissionCleanup); + static int32 LoadDecisionMaker(const char* filepath, eDecisionTypes decisionMakerType, bool bUseMissionCleanup); static void LoadDecisionMaker(const char* filepath, CDecisionMaker* decisionMaker); }; @@ -26,7 +26,7 @@ class CDecisionMakerTypes { static CDecisionMakerTypes* GetInstance(); - void AddDecisionMaker(CDecisionMaker* decisionMaker, eDecisionTypes decisionMakerType, bool bUseMissionCleanup); + int32 AddDecisionMaker(CDecisionMaker* decisionMaker, eDecisionTypes decisionMakerType, bool bUseMissionCleanup); void MakeDecision(CPed* ped, int32 eventType, int32 eventSourceType, bool bIsPedInVehicle, int32 taskId1, int32 taskId2, int32 taskId3, int32 taskId4, bool bInGroup, int16& taskId, int16& field_10); int32 MakeDecision(CPedGroup* pedGroup, int32 eventType, int32 eventSourceType, bool bIsPedInVehicle, int32 taskId1, int32 taskId2, int32 taskId3, int32 taskId4); void AddEventResponse(int32 decisionMakerIndex, int32 eventType, int32 taskId, float* responseChances, int32* flags); diff --git a/source/game_sa/Entity/Entity.cpp b/source/game_sa/Entity/Entity.cpp index 186bd7a667..fd475fec8b 100644 --- a/source/game_sa/Entity/Entity.cpp +++ b/source/game_sa/Entity/Entity.cpp @@ -972,7 +972,7 @@ bool CEntity::HasPreRenderEffects() return false; for (int32 i = 0; i < mi->m_n2dfxCount; ++i) { - if (mi->Get2dEffect(i)->m_nType == e2dEffectType::EFFECT_LIGHT) + if (mi->Get2dEffect(i)->m_type == e2dEffectType::EFFECT_LIGHT) return true; } @@ -1119,8 +1119,8 @@ CVector* CEntity::FindTriggerPointCoors(CVector* outVec, int32 triggerIndex) auto mi = CModelInfo::GetModelInfo(m_nModelIndex); for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - if (effect->m_nType == e2dEffectType::EFFECT_TRIGGER_POINT && effect->slotMachineIndex.m_nId == triggerIndex) { - *outVec = GetMatrix() * effect->m_vecPosn; + if (effect->m_type == e2dEffectType::EFFECT_TRIGGER_POINT && effect->slotMachineIndex.m_nId == triggerIndex) { + *outVec = GetMatrix() * effect->m_pos; return outVec; } } @@ -1146,7 +1146,7 @@ C2dEffect* CEntity::GetRandom2dEffect(int32 effectType, bool bCheckForEmptySlot) size_t iFoundCount = 0; for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - if (effect->m_nType != effectType) + if (effect->m_type != effectType) continue; if (bCheckForEmptySlot && !GetPedAttractorManager()->HasEmptySlot(effect, this)) @@ -1196,13 +1196,13 @@ void CEntity::CreateEffects() for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - switch (effect->m_nType) { + switch (effect->m_type) { case e2dEffectType::EFFECT_LIGHT: { m_bHasPreRenderEffects = true; break; } case e2dEffectType::EFFECT_PARTICLE: { - g_fx.CreateEntityFx(this, effect->particle.m_szName, &effect->m_vecPosn, GetModellingMatrix()); + g_fx.CreateEntityFx(this, effect->particle.m_szName, &effect->m_pos, GetModellingMatrix()); break; } case e2dEffectType::EFFECT_ATTRACTOR: { @@ -1211,8 +1211,8 @@ void CEntity::CreateEffects() break; } case e2dEffectType::EFFECT_ENEX: { - auto vecExit = effect->m_vecPosn + effect->enEx.m_vecExitPosn; - auto vecWorldEffect = TransformFromObjectSpace(effect->m_vecPosn); + auto vecExit = effect->m_pos + effect->enEx.m_vecExitPosn; + auto vecWorldEffect = TransformFromObjectSpace(effect->m_pos); auto vecWorldExit = TransformFromObjectSpace(vecExit); if (effect->enEx.bTimedEffect) { @@ -1277,13 +1277,13 @@ void CEntity::CreateEffects() RwFrameRotate(frame, &axis2, effect->roadsign.m_vecRotation.z, RwOpCombineType::rwCOMBINEREPLACE); RwFrameRotate(frame, &axis0, effect->roadsign.m_vecRotation.x, RwOpCombineType::rwCOMBINEPOSTCONCAT); RwFrameRotate(frame, &axis1, effect->roadsign.m_vecRotation.y, RwOpCombineType::rwCOMBINEPOSTCONCAT); - RwFrameTranslate(frame, &effect->m_vecPosn, RwOpCombineType::rwCOMBINEPOSTCONCAT); + RwFrameTranslate(frame, &effect->m_pos, RwOpCombineType::rwCOMBINEPOSTCONCAT); RwFrameUpdateObjects(frame); effect->roadsign.m_pAtomic = signAtomic; break; } case e2dEffectType::EFFECT_ESCALATOR: { - auto vecStart = TransformFromObjectSpace(effect->m_vecPosn); + auto vecStart = TransformFromObjectSpace(effect->m_pos); auto vecBottom = TransformFromObjectSpace(effect->escalator.m_vecBottom); auto vecTop = TransformFromObjectSpace(effect->escalator.m_vecTop); auto vecEnd = TransformFromObjectSpace(effect->escalator.m_vecEnd); @@ -1305,7 +1305,7 @@ void CEntity::DestroyEffects() for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - switch (effect->m_nType) { + switch (effect->m_type) { case e2dEffectType::EFFECT_ATTRACTOR: { if (effect->pedAttractor.m_nAttractorType == ePedAttractorType::PED_ATTRACTOR_TRIGGER_SCRIPT) CTheScripts::ScriptsForBrains.MarkAttractorScriptBrainWithThisNameAsNoLongerNeeded(effect->pedAttractor.m_szScriptName); @@ -1321,7 +1321,7 @@ void CEntity::DestroyEffects() break; } case e2dEffectType::EFFECT_ENEX: { - auto vecWorld = TransformFromObjectSpace(effect->m_vecPosn); + auto vecWorld = TransformFromObjectSpace(effect->m_pos); auto iNearestEnex = CEntryExitManager::FindNearestEntryExit(vecWorld, 1.5F, -1); if (iNearestEnex != -1) { auto enex = CEntryExitManager::mp_poolEntryExits->GetAt(iNearestEnex); @@ -1423,7 +1423,7 @@ void CEntity::RenderEffects() for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - if (effect->m_nType != e2dEffectType::EFFECT_ROADSIGN) + if (effect->m_type != e2dEffectType::EFFECT_ROADSIGN) continue; CCustomRoadsignMgr::RenderRoadsignAtomic(effect->roadsign.m_pAtomic, TheCamera.GetPosition()); @@ -1456,19 +1456,10 @@ bool CEntity::GetIsTouching(const CVector& centre, float radius) } // 0x534540 -bool CEntity::GetIsOnScreen() -{ +bool CEntity::GetIsOnScreen() { CVector thisVec; GetBoundCentre(thisVec); - auto fThisRadius = CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->GetBoundRadius(); - - if (TheCamera.IsSphereVisible(thisVec, fThisRadius, reinterpret_cast(&TheCamera.m_mMatInverse))) - return true; - - if (TheCamera.m_bMirrorActive) - return TheCamera.IsSphereVisible(thisVec, fThisRadius, reinterpret_cast(&TheCamera.m_mMatMirrorInverse)); - - return false; + return TheCamera.IsSphereVisible(thisVec, CModelInfo::GetModelInfo(m_nModelIndex)->GetColModel()->GetBoundRadius()); } // 0x5345D0 @@ -1926,8 +1917,8 @@ void CEntity::ProcessLightsForEntity() auto fIntensity = 1.0F; auto uiRand = m_nRandomSeed ^ CCoronas::ms_aEntityLightsOffsets[iFxInd & 0x7]; - if (effect->m_nType == e2dEffectType::EFFECT_SUN_GLARE && CWeather::SunGlare >= 0.0F) { - auto vecEffPos = TransformFromObjectSpace(effect->m_vecPosn); + if (effect->m_type == e2dEffectType::EFFECT_SUN_GLARE && CWeather::SunGlare >= 0.0F) { + auto vecEffPos = TransformFromObjectSpace(effect->m_pos); auto vecDir = vecEffPos - GetPosition(); vecDir.Normalise(); @@ -1977,10 +1968,10 @@ void CEntity::ProcessLightsForEntity() continue; } - if (effect->m_nType != e2dEffectType::EFFECT_LIGHT) + if (effect->m_type != e2dEffectType::EFFECT_LIGHT) continue; - auto vecEffPos = TransformFromObjectSpace(effect->m_vecPosn); + auto vecEffPos = TransformFromObjectSpace(effect->m_pos); auto bDoColorLight = false; auto bDoNoColorLight = false; auto bCoronaVisible = false; diff --git a/source/game_sa/Entity/Entity.h b/source/game_sa/Entity/Entity.h index a7119319ff..636a5ebfcf 100644 --- a/source/game_sa/Entity/Entity.h +++ b/source/game_sa/Entity/Entity.h @@ -45,41 +45,41 @@ class NOTSA_EXPORT_VTABLE CEntity : public CPlaceable { union { struct { /* https://github.com/multitheftauto/mtasa-blue/blob/master/Client/game_sa/CEntitySA.h */ - uint32 m_bUsesCollision : 1; // does entity use collision - uint32 m_bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function - uint32 m_bIsStatic : 1; // is entity static - uint32 m_bHasContacted : 1; // has entity processed some contact forces - uint32 m_bIsStuck : 1; // is entity stuck - uint32 m_bIsInSafePosition : 1; // is entity in a collision free safe position - uint32 m_bWasPostponed : 1; // was entity control processing postponed - uint32 m_bIsVisible : 1; // is the entity visible - - uint32 m_bIsBIGBuilding : 1; // Set if this entity is a big building - uint32 m_bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage - uint32 m_bStreamingDontDelete : 1; // Don't let the streaming remove this - uint32 m_bRemoveFromWorld : 1; // remove this entity next time it should be processed - uint32 m_bHasHitWall : 1; // has collided with a building (changes subsequent collisions) - uint32 m_bImBeingRendered : 1; // don't delete me because I'm being rendered - uint32 m_bDrawLast : 1; // draw object last - uint32 m_bDistanceFade : 1; // Fade entity because it is far away - - uint32 m_bDontCastShadowsOn : 1; // Don't cast shadows on this object - uint32 m_bOffscreen : 1; // offscreen flag. This can only be trusted when it is set to true - uint32 m_bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them - uint32 m_bDontStream : 1; // tell the streaming not to stream me - uint32 m_bUnderwater : 1; // this object is underwater change drawing order - uint32 m_bHasPreRenderEffects : 1; // Object has a prerender effects attached to it - uint32 m_bIsTempBuilding : 1; // whether the building is temporary (i.e. can be created and deleted more than once) - uint32 m_bDontUpdateHierarchy : 1; // Don't update the animation hierarchy this frame - - uint32 m_bHasRoadsignText : 1; // entity is roadsign and has some 2dEffect text stuff to be rendered - uint32 m_bDisplayedSuperLowLOD : 1; - uint32 m_bIsProcObject : 1; // set object has been generated by procedural object generator - uint32 m_bBackfaceCulled : 1; // has backface culling on - uint32 m_bLightObject : 1; // light object with directional lights - uint32 m_bUnimportantStream : 1; // set that this object is unimportant, if streaming is having problems - uint32 m_bTunnel : 1; // Is this model part of a tunnel - uint32 m_bTunnelTransition : 1; // This model should be rendered from within and outside the tunnel + bool m_bUsesCollision : 1; // does entity use collision + bool m_bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function + bool m_bIsStatic : 1; // is entity static + bool m_bHasContacted : 1; // has entity processed some contact forces + bool m_bIsStuck : 1; // is entity stuck + bool m_bIsInSafePosition : 1; // is entity in a collision free safe position + bool m_bWasPostponed : 1; // was entity control processing postponed + bool m_bIsVisible : 1; // is the entity visible + + bool m_bIsBIGBuilding : 1; // Set if this entity is a big building + bool m_bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage + bool m_bStreamingDontDelete : 1; // Don't let the streaming remove this + bool m_bRemoveFromWorld : 1; // remove this entity next time it should be processed + bool m_bHasHitWall : 1; // has collided with a building (changes subsequent collisions) + bool m_bImBeingRendered : 1; // don't delete me because I'm being rendered + bool m_bDrawLast : 1; // draw object last + bool m_bDistanceFade : 1; // Fade entity because it is far away + + bool m_bDontCastShadowsOn : 1; // Don't cast shadows on this object + bool m_bOffscreen : 1; // offscreen flag. This can only be trusted when it is set to true + bool m_bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them + bool m_bDontStream : 1; // tell the streaming not to stream me + bool m_bUnderwater : 1; // this object is underwater change drawing order + bool m_bHasPreRenderEffects : 1; // Object has a prerender effects attached to it + bool m_bIsTempBuilding : 1; // whether the building is temporary (i.e. can be created and deleted more than once) + bool m_bDontUpdateHierarchy : 1; // Don't update the animation hierarchy this frame + + bool m_bHasRoadsignText : 1; // entity is roadsign and has some 2dEffect text stuff to be rendered + bool m_bDisplayedSuperLowLOD : 1; + bool m_bIsProcObject : 1; // set object has been generated by procedural object generator + bool m_bBackfaceCulled : 1; // has backface culling on + bool m_bLightObject : 1; // light object with directional lights + bool m_bUnimportantStream : 1; // set that this object is unimportant, if streaming is having problems + bool m_bTunnel : 1; // Is this model part of a tunnel + bool m_bTunnelTransition : 1; // This model should be rendered from within and outside the tunnel }; uint32 m_nFlags; }; diff --git a/source/game_sa/Entity/Object/Object.h b/source/game_sa/Entity/Object/Object.h index d64eb1576e..5c2d8b5744 100644 --- a/source/game_sa/Entity/Object/Object.h +++ b/source/game_sa/Entity/Object/Object.h @@ -34,8 +34,8 @@ class NOTSA_EXPORT_VTABLE CObject : public CPhysical { uint32 b0x02 : 1; // 0x2 - collision related uint32 bPickupPropertyForSale : 1; // 0x4 uint32 bPickupInShopOutOfStock : 1; // 0x8 - uint32 bGlassBroken : 1; // 0x10 - uint32 b0x20 : 1; // 0x20 - Something glass related, see `WindowRespondsToCollision` + uint32 bHasBrokenGlass : 1; // 0x10 + uint32 bGlassBrokenAltogether : 1; // 0x20 uint32 bIsExploded : 1; // 0x40 uint32 bChangesVehColor : 1; // 0x80 @@ -57,7 +57,7 @@ class NOTSA_EXPORT_VTABLE CObject : public CPhysical { uint32 bFadingIn : 1; // works only for objects with type 2 (OBJECT_MISSION) uint32 bAffectedByColBrightness : 1; - uint32 b0x1000000 : 1; + uint32 bEnableDisabledAttractors : 1; uint32 bDoNotRender : 1; uint32 bFadingIn2 : 1; uint32 b0x08000000 : 1; diff --git a/source/game_sa/Entity/Ped/Ped.cpp b/source/game_sa/Entity/Ped/Ped.cpp index beea47c5e8..aabbcbac86 100644 --- a/source/game_sa/Entity/Ped/Ped.cpp +++ b/source/game_sa/Entity/Ped/Ped.cpp @@ -3675,6 +3675,17 @@ bool CPed::IsRunningOrSprinting() const { return false; } +bool CPed::IsPedStandingInPlace() const { + switch (m_nMoveState) { + case PEDMOVE_NONE: + case PEDMOVE_STILL: + case PEDMOVE_TURN_L: + case PEDMOVE_TURN_R: + return true; + } + return false; +} + // 0x6497A0 bool SayJacked(CPed* jacked, CVehicle* vehicle, uint32 offset) { if (vehicle->m_vehicleAudio.GetVehicleTypeForAudio()) diff --git a/source/game_sa/Entity/Ped/Ped.h b/source/game_sa/Entity/Ped/Ped.h index 443c081e55..0275156236 100644 --- a/source/game_sa/Entity/Ped/Ped.h +++ b/source/game_sa/Entity/Ped/Ped.h @@ -118,140 +118,140 @@ class NOTSA_EXPORT_VTABLE CPed : public CPhysical { /* https://github.com/multitheftauto/mtasa-blue/blob/master/Client/game_sa/CPedSA.h */ struct { // 1st byte starts here (m_nPedFlags) - uint32 bIsStanding : 1 = false; // is ped standing on something - uint32 bWasStanding : 1 = false; // was ped standing on something - uint32 bIsLooking : 1 = false; // is ped looking at something or in a direction - uint32 bIsRestoringLook : 1 = false; // is ped restoring head position from a look - uint32 bIsAimingGun : 1 = false; // is ped aiming gun - uint32 bIsRestoringGun : 1 = false; // is ped moving gun back to default posn - uint32 bCanPointGunAtTarget : 1 = false; // can ped point gun at target - uint32 bIsTalking : 1 = false; // is ped talking(see Chat()) - - uint32 bInVehicle : 1 = false; // is in a vehicle - uint32 bIsInTheAir : 1 = false; // is in the air - uint32 bIsLanding : 1 = false; // is landing after being in the air - uint32 bHitSomethingLastFrame : 1 = false; // has been in a collision last frame - uint32 bIsNearCar : 1 = false; // has been in a collision last frame - uint32 bRenderPedInCar : 1 = true; // has been in a collision last frame - uint32 bUpdateAnimHeading : 1 = false; // update ped heading due to heading change during anim sequence - uint32 bRemoveHead : 1 = false; // waiting on AntiSpazTimer to remove head - TODO: See `RemoveBodyPart` - The name seems to be incorrect. It should be like `bHasBodyPartToRemove`. - - uint32 bFiringWeapon : 1 = false; // is pulling trigger - uint32 bHasACamera : 1; // does ped possess a camera to document accidents - uint32 bPedIsBleeding : 1 = false; // Ped loses a lot of blood if true - uint32 bStopAndShoot : 1 = false; // Ped cannot reach target to attack with fist, need to use gun - uint32 bIsPedDieAnimPlaying : 1 = false; // is ped die animation finished so can dead now - uint32 bStayInSamePlace : 1 = false; // when set, ped stays put - uint32 bKindaStayInSamePlace : 1 = false; // when set, ped doesn't seek out opponent or cover large distances. Will still shuffle and look for cover - uint32 bBeingChasedByPolice : 1 = false; // use nodes for route find - - uint32 bNotAllowedToDuck : 1 = false; // Is this ped allowed to duck at all? - uint32 bCrouchWhenShooting : 1 = false; // duck behind cars etc - uint32 bIsDucking : 1 = false; // duck behind cars etc - uint32 bGetUpAnimStarted : 1 = false; // don't want to play getup anim if under something - uint32 bDoBloodyFootprints : 1 = false; // bIsLeader - uint32 bDontDragMeOutCar : 1 = false; - uint32 bStillOnValidPoly : 1 = false; // set if the polygon the ped is on is still valid for collision - uint32 bAllowMedicsToReviveMe : 1 = true; + bool bIsStanding : 1 = false; // is ped standing on something + bool bWasStanding : 1 = false; // was ped standing on something + bool bIsLooking : 1 = false; // is ped looking at something or in a direction + bool bIsRestoringLook : 1 = false; // is ped restoring head position from a look + bool bIsAimingGun : 1 = false; // is ped aiming gun + bool bIsRestoringGun : 1 = false; // is ped moving gun back to default posn + bool bCanPointGunAtTarget : 1 = false; // can ped point gun at target + bool bIsTalking : 1 = false; // is ped talking(see Chat()) + + bool bInVehicle : 1 = false; // is in a vehicle + bool bIsInTheAir : 1 = false; // is in the air + bool bIsLanding : 1 = false; // is landing after being in the air + bool bHitSomethingLastFrame : 1 = false; // has been in a collision last frame + bool bIsNearCar : 1 = false; // has been in a collision last frame + bool bRenderPedInCar : 1 = true; // has been in a collision last frame + bool bUpdateAnimHeading : 1 = false; // update ped heading due to heading change during anim sequence + bool bRemoveHead : 1 = false; // waiting on AntiSpazTimer to remove head - TODO: See `RemoveBodyPart` - The name seems to be incorrect. It should be like `bHasBodyPartToRemove`. + + bool bFiringWeapon : 1 = false; // is pulling trigger + bool bHasACamera : 1; // does ped possess a camera to document accidents + bool bPedIsBleeding : 1 = false; // Ped loses a lot of blood if true + bool bStopAndShoot : 1 = false; // Ped cannot reach target to attack with fist, need to use gun + bool bIsPedDieAnimPlaying : 1 = false; // is ped die animation finished so can dead now + bool bStayInSamePlace : 1 = false; // when set, ped stays put + bool bKindaStayInSamePlace : 1 = false; // when set, ped doesn't seek out opponent or cover large distances. Will still shuffle and look for cover + bool bBeingChasedByPolice : 1 = false; // use nodes for route find + + bool bNotAllowedToDuck : 1 = false; // Is this ped allowed to duck at all? + bool bCrouchWhenShooting : 1 = false; // duck behind cars etc + bool bIsDucking : 1 = false; // duck behind cars etc + bool bGetUpAnimStarted : 1 = false; // don't want to play getup anim if under something + bool bDoBloodyFootprints : 1 = false; // bIsLeader + bool bDontDragMeOutCar : 1 = false; + bool bStillOnValidPoly : 1 = false; // set if the polygon the ped is on is still valid for collision + bool bAllowMedicsToReviveMe : 1 = true; // 5th byte starts here (m_nSecondPedFlags) - uint32 bResetWalkAnims : 1 = false; - uint32 bOnBoat : 1 = false; // flee but only using nodes - uint32 bBusJacked : 1 = false; // flee but only using nodes - uint32 bFadeOut : 1 = false; // set if you want ped to fade out - uint32 bKnockedUpIntoAir : 1 = false; // has ped been knocked up into the air by a car collision - uint32 bHitSteepSlope : 1 = false; // has ped collided/is standing on a steep slope (surface type) - uint32 bCullExtraFarAway : 1 = false; // special ped only gets culled if it's extra far away (for roadblocks) - uint32 bTryingToReachDryLand : 1 = false; // has ped just exited boat and trying to get to dry land - - uint32 bCollidedWithMyVehicle : 1 = false; - uint32 bRichFromMugging : 1 = false; // ped has lots of cash cause they've been mugging people - uint32 bChrisCriminal : 1 = false; // Is a criminal as killed during Chris' police mission (should be counted as such) - uint32 bShakeFist : 1 = false; // test shake hand at look entity - uint32 bNoCriticalHits : 1 = false; // ped cannot be killed by a single bullet - uint32 bHasAlreadyBeenRecorded : 1 = false; // Used for replays - uint32 bUpdateMatricesRequired : 1 = false; // if PedIK has altered bones so matrices need updated this frame - uint32 bFleeWhenStanding : 1 = false; // - - uint32 bMiamiViceCop : 1 = false; - uint32 bMoneyHasBeenGivenByScript : 1 = false; - uint32 bHasBeenPhotographed : 1 = false; - uint32 bIsDrowning : 1 = false; - uint32 bDrownsInWater : 1 = true; - uint32 bHeadStuckInCollision : 1 = false; - uint32 bDeadPedInFrontOfCar : 1 = false; - uint32 bStayInCarOnJack : 1 = false; - - uint32 bDontFight : 1 = false; - uint32 bDoomAim : 1 = true; - uint32 bCanBeShotInVehicle : 1 = true; - uint32 bPushedAlongByCar : 1 = false; // ped is getting pushed along by car collision (so don't take damage from horz velocity) - uint32 bNeverEverTargetThisPed : 1 = false; - uint32 bThisPedIsATargetPriority : 1 = false; - uint32 bCrouchWhenScared : 1 = false; - uint32 bKnockedOffBike : 1 = false; // TODO: Maybe rename to `bIsJumpingOut` or something similar, see x-refs + bool bResetWalkAnims : 1 = false; + bool bOnBoat : 1 = false; // flee but only using nodes + bool bBusJacked : 1 = false; // flee but only using nodes + bool bFadeOut : 1 = false; // set if you want ped to fade out + bool bKnockedUpIntoAir : 1 = false; // has ped been knocked up into the air by a car collision + bool bHitSteepSlope : 1 = false; // has ped collided/is standing on a steep slope (surface type) + bool bCullExtraFarAway : 1 = false; // special ped only gets culled if it's extra far away (for roadblocks) + bool bTryingToReachDryLand : 1 = false; // has ped just exited boat and trying to get to dry land + + bool bCollidedWithMyVehicle : 1 = false; + bool bRichFromMugging : 1 = false; // ped has lots of cash cause they've been mugging people + bool bChrisCriminal : 1 = false; // Is a criminal as killed during Chris' police mission (should be counted as such) + bool bShakeFist : 1 = false; // test shake hand at look entity + bool bNoCriticalHits : 1 = false; // ped cannot be killed by a single bullet + bool bHasAlreadyBeenRecorded : 1 = false; // Used for replays + bool bUpdateMatricesRequired : 1 = false; // if PedIK has altered bones so matrices need updated this frame + bool bFleeWhenStanding : 1 = false; // + + bool bMiamiViceCop : 1 = false; + bool bMoneyHasBeenGivenByScript : 1 = false; + bool bHasBeenPhotographed : 1 = false; + bool bIsDrowning : 1 = false; + bool bDrownsInWater : 1 = true; + bool bHeadStuckInCollision : 1 = false; + bool bDeadPedInFrontOfCar : 1 = false; + bool bStayInCarOnJack : 1 = false; + + bool bDontFight : 1 = false; + bool bDoomAim : 1 = true; + bool bCanBeShotInVehicle : 1 = true; + bool bPushedAlongByCar : 1 = false; // ped is getting pushed along by car collision (so don't take damage from horz velocity) + bool bNeverEverTargetThisPed : 1 = false; + bool bThisPedIsATargetPriority : 1 = false; + bool bCrouchWhenScared : 1 = false; + bool bKnockedOffBike : 1 = false; // TODO: Maybe rename to `bIsJumpingOut` or something similar, see x-refs // 9th byte starts here (m_nThirdPedFlags) - uint32 bDonePositionOutOfCollision : 1 = false; - uint32 bDontRender : 1 = false; - uint32 bHasBeenAddedToPopulation : 1 = false; - uint32 bHasJustLeftCar : 1 = false; - uint32 bIsInDisguise : 1 = false; - uint32 bDoesntListenToPlayerGroupCommands : 1 = false; - uint32 bIsBeingArrested : 1 = false; - uint32 bHasJustSoughtCover : 1 = false; - - uint32 bKilledByStealth : 1 = false; - uint32 bDoesntDropWeaponsWhenDead : 1 = false; - uint32 bCalledPreRender : 1 = false; - uint32 bBloodPuddleCreated : 1 = false; // Has a static puddle of blood been created yet - uint32 bPartOfAttackWave : 1 = false; - uint32 bClearRadarBlipOnDeath : 1 = false; - uint32 bNeverLeavesGroup : 1 = false; // flag that we want to test 3 extra spheres on col model - uint32 bTestForBlockedPositions : 1 = false; // this sets these indicator flags for various positions on the front of the ped - - uint32 bRightArmBlocked : 1 = false; - uint32 bLeftArmBlocked : 1 = false; - uint32 bDuckRightArmBlocked : 1 = false; - uint32 bMidriffBlockedForJump : 1 = false; - uint32 bFallenDown : 1 = false; - uint32 bUseAttractorInstantly : 1 = false; - uint32 bDontAcceptIKLookAts : 1 = false; - uint32 bHasAScriptBrain : 1 = false; - - uint32 bWaitingForScriptBrainToLoad : 1 = false; - uint32 bHasGroupDriveTask : 1 = false; - uint32 bCanExitCar : 1 = true; - uint32 CantBeKnockedOffBike : 2 = false; // (harder for mission peds) normal(also for mission peds) - uint32 bHasBeenRendered : 1 = false; - uint32 bIsCached : 1 = false; - uint32 bPushOtherPeds : 1 = false; // GETS RESET EVERY FRAME - SET IN TASK: want to push other peds around (eg. leader of a group or ped trying to get in a car) + bool bDonePositionOutOfCollision : 1 = false; + bool bDontRender : 1 = false; + bool bHasBeenAddedToPopulation : 1 = false; + bool bHasJustLeftCar : 1 = false; + bool bIsInDisguise : 1 = false; + bool bDoesntListenToPlayerGroupCommands : 1 = false; + bool bIsBeingArrested : 1 = false; + bool bHasJustSoughtCover : 1 = false; + + bool bKilledByStealth : 1 = false; + bool bDoesntDropWeaponsWhenDead : 1 = false; + bool bCalledPreRender : 1 = false; + bool bBloodPuddleCreated : 1 = false; // Has a static puddle of blood been created yet + bool bPartOfAttackWave : 1 = false; + bool bClearRadarBlipOnDeath : 1 = false; + bool bNeverLeavesGroup : 1 = false; // flag that we want to test 3 extra spheres on col model + bool bTestForBlockedPositions : 1 = false; // this sets these indicator flags for various positions on the front of the ped + + bool bRightArmBlocked : 1 = false; + bool bLeftArmBlocked : 1 = false; + bool bDuckRightArmBlocked : 1 = false; + bool bMidriffBlockedForJump : 1 = false; + bool bFallenDown : 1 = false; + bool bUseAttractorInstantly : 1 = false; + bool bDontAcceptIKLookAts : 1 = false; + bool bHasAScriptBrain : 1 = false; + + bool bWaitingForScriptBrainToLoad : 1 = false; + bool bHasGroupDriveTask : 1 = false; + bool bCanExitCar : 1 = true; + bool CantBeKnockedOffBike : 2 = false; // (harder for mission peds) normal(also for mission peds) + bool bHasBeenRendered : 1 = false; + bool bIsCached : 1 = false; + bool bPushOtherPeds : 1 = false; // GETS RESET EVERY FRAME - SET IN TASK: want to push other peds around (eg. leader of a group or ped trying to get in a car) // 13th byte starts here (m_nFourthPedFlags) - uint32 bHasBulletProofVest : 1 = false; - uint32 bUsingMobilePhone : 1 = false; - uint32 bUpperBodyDamageAnimsOnly : 1 = false; - uint32 bStuckUnderCar : 1 = false; - uint32 bKeepTasksAfterCleanUp : 1 = false; // If true ped will carry on with task even after cleanup - uint32 bIsDyingStuck : 1 = false; - uint32 bIgnoreHeightCheckOnGotoPointTask : 1 = false; // set when walking round buildings, reset when task quits - uint32 bForceDieInCar : 1 = false; - - uint32 bCheckColAboveHead : 1 = false; - uint32 bIgnoreWeaponRange : 1 = false; - uint32 bDruggedUp : 1 = false; - uint32 bWantedByPolice : 1 = false; // if this is set, the cops will always go after this ped when they are doing a KillCriminal task - uint32 bSignalAfterKill : 1 = true; - uint32 bCanClimbOntoBoat : 1 = false; - uint32 bPedHitWallLastFrame : 1 = false; // useful to store this so that AI knows (normal will still be available) - uint32 bIgnoreHeightDifferenceFollowingNodes : 1 = false; - - uint32 bMoveAnimSpeedHasBeenSetByTask : 1 = false; - uint32 bGetOutUpsideDownCar : 1 = true; - uint32 bJustGotOffTrain : 1 = false; - uint32 bDeathPickupsPersist : 1 = false; - uint32 bTestForShotInVehicle : 1 = false; - uint32 bUsedForReplay : 1 = false; // This ped is controlled by replay and should be removed when replay is done. + bool bHasBulletProofVest : 1 = false; + bool bUsingMobilePhone : 1 = false; + bool bUpperBodyDamageAnimsOnly : 1 = false; + bool bStuckUnderCar : 1 = false; + bool bKeepTasksAfterCleanUp : 1 = false; // If true ped will carry on with task even after cleanup + bool bIsDyingStuck : 1 = false; + bool bIgnoreHeightCheckOnGotoPointTask : 1 = false; // set when walking round buildings, reset when task quits + bool bForceDieInCar : 1 = false; + + bool bCheckColAboveHead : 1 = false; + bool bIgnoreWeaponRange : 1 = false; + bool bDruggedUp : 1 = false; + bool bWantedByPolice : 1 = false; // if this is set, the cops will always go after this ped when they are doing a KillCriminal task + bool bSignalAfterKill : 1 = true; + bool bCanClimbOntoBoat : 1 = false; + bool bPedHitWallLastFrame : 1 = false; // useful to store this so that AI knows (normal will still be available) + bool bIgnoreHeightDifferenceFollowingNodes : 1 = false; + + bool bMoveAnimSpeedHasBeenSetByTask : 1 = false; + bool bGetOutUpsideDownCar : 1 = true; + bool bJustGotOffTrain : 1 = false; + bool bDeathPickupsPersist : 1 = false; + bool bTestForShotInVehicle : 1 = false; + bool bUsedForReplay : 1 = false; // This ped is controlled by replay and should be removed when replay is done. }; CPedIntelligence* m_pIntelligence; CPlayerPedData* m_pPlayerData; @@ -325,7 +325,7 @@ class NOTSA_EXPORT_VTABLE CPed : public CPhysical { int16 m_nMoneyCount; // Used for money pickup when ped is killed float field_758; float field_75C; - char m_nLastWeaponDamage; + char m_nLastWeaponDamage; // See eWeaponType CEntity* m_pLastEntityDamage; int32 field_768; @@ -546,6 +546,7 @@ class NOTSA_EXPORT_VTABLE CPed : public CPhysical { bool IsStateDriving() const noexcept { return m_nPedState == PEDSTATE_DRIVING; } bool IsStateDead() const noexcept { return m_nPedState == PEDSTATE_DEAD; } bool IsStateDying() const noexcept { return m_nPedState == PEDSTATE_DEAD || m_nPedState == PEDSTATE_DIE; } + bool IsStateDeadForScript() const noexcept { return m_nPedState == PEDSTATE_DEAD || m_nPedState == PEDSTATE_DIE || m_nPedState == PEDSTATE_DIE_BY_STEALTH; } bool IsInVehicleAsPassenger() const noexcept; bool IsCop() const noexcept { return m_nPedType == PED_TYPE_COP; } @@ -580,6 +581,12 @@ class NOTSA_EXPORT_VTABLE CPed : public CPhysical { */ bool IsRunningOrSprinting() const; + /*! + * @notsa + * @brief Is the ped standing in place (might still be moving, but in place) + */ + bool IsPedStandingInPlace() const; + /*! * @notsa * @brief Is the ped's right arm blocked right now diff --git a/source/game_sa/Entity/Physical.h b/source/game_sa/Entity/Physical.h index 1aabd9f5e8..5079d583c1 100644 --- a/source/game_sa/Entity/Physical.h +++ b/source/game_sa/Entity/Physical.h @@ -226,8 +226,9 @@ class NOTSA_EXPORT_VTABLE CPhysical : public CEntity { bool CheckCollision(); bool CheckCollision_SimpleCar(); - CVector& GetMoveSpeed() { return m_vecMoveSpeed; } - void ResetMoveSpeed() { m_vecMoveSpeed = CVector(); } + CVector& GetMoveSpeed() { return m_vecMoveSpeed; } + void SetVelocity(CVector velocity) { m_vecMoveSpeed = velocity; } // 0x441130 + void ResetMoveSpeed() { SetVelocity(CVector{}); } CVector& GetTurnSpeed() { return m_vecTurnSpeed; } void ResetTurnSpeed() { m_vecTurnSpeed = CVector(); } diff --git a/source/game_sa/Entity/Placeable.cpp b/source/game_sa/Entity/Placeable.cpp index b8d0d0692e..fd463d720b 100644 --- a/source/game_sa/Entity/Placeable.cpp +++ b/source/game_sa/Entity/Placeable.cpp @@ -14,7 +14,7 @@ void CPlaceable::InjectHooks() { RH_ScopedOverloadedInstall(SetPosn, "xyz", 0x420B80, void(CPlaceable::*)(float, float, float)); RH_ScopedOverloadedInstall(SetPosn, "vector", 0x4241C0, void(CPlaceable::*)(const CVector&)); - RH_ScopedInstall(SetOrientation, 0x439A80); + RH_ScopedOverloadedInstall(SetOrientation, "xyz", 0x439A80, void(CPlaceable::*)(float, float, float)); RH_ScopedInstall(SetHeading, 0x43E0C0); RH_ScopedInstall(GetHeading, 0x441DB0); RH_ScopedOverloadedInstall(IsWithinArea, "xy", 0x54F200, bool(CPlaceable::*)(float, float, float, float) const); diff --git a/source/game_sa/Entity/Placeable.h b/source/game_sa/Entity/Placeable.h index cffa5ef4e6..bf572d7b7a 100644 --- a/source/game_sa/Entity/Placeable.h +++ b/source/game_sa/Entity/Placeable.h @@ -36,6 +36,7 @@ class CPlaceable { void SetPosn(float x, float y, float z); void SetPosn(const CVector& posn); void SetOrientation(float x, float y, float z); + void SetOrientation(CVector radians) { SetOrientation(radians.x, radians.y, radians.z); } // TODO: Replace method above with this void GetOrientation(float& x, float& y, float& z); void SetHeading(float heading); float GetHeading(); diff --git a/source/game_sa/Entity/Vehicle/Automobile.h b/source/game_sa/Entity/Vehicle/Automobile.h index cb920642da..8d424947f8 100644 --- a/source/game_sa/Entity/Vehicle/Automobile.h +++ b/source/game_sa/Entity/Vehicle/Automobile.h @@ -129,6 +129,8 @@ class NOTSA_EXPORT_VTABLE CAutomobile : public CVehicle { static CVector& vecHunterGunPos; // { 0.0f, 4.8f, -1.3f } static CMatrix* matW2B; + static constexpr auto Type = VEHICLE_TYPE_AUTOMOBILE; + public: CAutomobile(int32 modelIndex, eVehicleCreatedBy createdBy, bool setupSuspensionLines); ~CAutomobile() override; diff --git a/source/game_sa/Entity/Vehicle/Bike.h b/source/game_sa/Entity/Vehicle/Bike.h index ceeed8c7e7..f977e8f259 100644 --- a/source/game_sa/Entity/Vehicle/Bike.h +++ b/source/game_sa/Entity/Vehicle/Bike.h @@ -88,6 +88,8 @@ class NOTSA_EXPORT_VTABLE CBike : public CVehicle { float m_fGasPedalAudioRevs; tWheelState m_aWheelState[2]; + static constexpr auto Type = VEHICLE_TYPE_BIKE; + public: CBike(int32 modelIndex, eVehicleCreatedBy createdBy); // 0x6BF430 ~CBike() override; diff --git a/source/game_sa/Entity/Vehicle/Bmx.h b/source/game_sa/Entity/Vehicle/Bmx.h index 6280c449e7..889057ffe3 100644 --- a/source/game_sa/Entity/Vehicle/Bmx.h +++ b/source/game_sa/Entity/Vehicle/Bmx.h @@ -36,6 +36,8 @@ class CBmx : public CBike { float m_fMidWheelFracY; bool m_bIsFreewheeling; + static constexpr auto Type = VEHICLE_TYPE_BMX; + public: CBmx(int32 modelIndex, eVehicleCreatedBy createdBy); ~CBmx() override; diff --git a/source/game_sa/Entity/Vehicle/Boat.h b/source/game_sa/Entity/Vehicle/Boat.h index 8ee457d13b..4958447b5b 100644 --- a/source/game_sa/Entity/Vehicle/Boat.h +++ b/source/game_sa/Entity/Vehicle/Boat.h @@ -75,6 +75,8 @@ class NOTSA_EXPORT_VTABLE CBoat : public CVehicle { static const constexpr auto uiNumIndices{ 6u }; static RxVertexIndex* auRenderIndices; + static constexpr auto Type = VEHICLE_TYPE_BOAT; + public: CBoat(int32 modelIndex, eVehicleCreatedBy createdBy); ~CBoat() override; diff --git a/source/game_sa/Entity/Vehicle/Heli.h b/source/game_sa/Entity/Vehicle/Heli.h index 1569fc2aa0..0ee47fae8e 100644 --- a/source/game_sa/Entity/Vehicle/Heli.h +++ b/source/game_sa/Entity/Vehicle/Heli.h @@ -97,6 +97,8 @@ class CHeli : public CAutomobile { static inline bool& bHeliControlsCheat = *(bool*)0xC1C970; static inline std::array& HeliSearchLights = *(std::array*)0xC1C990; + static constexpr auto Type = VEHICLE_TYPE_HELI; + public: CHeli(int32 modelIndex, eVehicleCreatedBy createdBy); ~CHeli() override; // 0x6C4340, 0x6C4810 diff --git a/source/game_sa/Entity/Vehicle/MonsterTruck.h b/source/game_sa/Entity/Vehicle/MonsterTruck.h index 34602a5e84..c42068819a 100644 --- a/source/game_sa/Entity/Vehicle/MonsterTruck.h +++ b/source/game_sa/Entity/Vehicle/MonsterTruck.h @@ -43,6 +43,8 @@ class NOTSA_EXPORT_VTABLE CMonsterTruck : public CAutomobile { static float& DUMPER_COL_ANGLEMULT; // 0.0002f + static constexpr auto Type = VEHICLE_TYPE_MTRUCK; + public: CMonsterTruck(int32 modelIndex, eVehicleCreatedBy createdBy); ~CMonsterTruck() override = default; // 0x6C7D10, 0x6C7F90 diff --git a/source/game_sa/Entity/Vehicle/Plane.h b/source/game_sa/Entity/Vehicle/Plane.h index ad752b35a4..e8e99bf747 100644 --- a/source/game_sa/Entity/Vehicle/Plane.h +++ b/source/game_sa/Entity/Vehicle/Plane.h @@ -68,6 +68,8 @@ class CPlane : public CAutomobile { uint32 m_nSmokeTimer; bool m_bSmokeEjectorEnabled; + static constexpr auto Type = VEHICLE_TYPE_PLANE; + public: static int32& GenPlane_ModelIndex; static uint32& GenPlane_Status; diff --git a/source/game_sa/Entity/Vehicle/QuadBike.h b/source/game_sa/Entity/Vehicle/QuadBike.h index f148633ae4..6aa3375022 100644 --- a/source/game_sa/Entity/Vehicle/QuadBike.h +++ b/source/game_sa/Entity/Vehicle/QuadBike.h @@ -41,6 +41,8 @@ class NOTSA_EXPORT_VTABLE CQuadBike : public CAutomobile { float field_9A8[4]; // unused uint8 m_nQuadFlags; + static constexpr auto Type = VEHICLE_TYPE_QUAD; + public: CQuadBike(int32 modelIndex, eVehicleCreatedBy createdBy); ~CQuadBike() override = default; // 0x6CDC30 diff --git a/source/game_sa/Entity/Vehicle/Trailer.h b/source/game_sa/Entity/Vehicle/Trailer.h index 833760f9bb..bdbd7b9328 100644 --- a/source/game_sa/Entity/Vehicle/Trailer.h +++ b/source/game_sa/Entity/Vehicle/Trailer.h @@ -47,6 +47,8 @@ class NOTSA_EXPORT_VTABLE CTrailer : public CAutomobile { float m_fTrailerTowedRatio{ 1.f }; float m_fTrailerTowedRatio2{ 1.f }; + static constexpr auto Type = VEHICLE_TYPE_TRAILER; + public: CTrailer(int32 modelIndex, eVehicleCreatedBy createdBy); ~CTrailer() override = default; // 0x6CED10 diff --git a/source/game_sa/Entity/Vehicle/Train.h b/source/game_sa/Entity/Vehicle/Train.h index e023694cd9..a314c80a0f 100644 --- a/source/game_sa/Entity/Vehicle/Train.h +++ b/source/game_sa/Entity/Vehicle/Train.h @@ -86,6 +86,8 @@ class CTrain : public CVehicle { static bool& bDisableRandomTrains; static CVector aStationCoors[6]; + static constexpr auto Type = VEHICLE_TYPE_TRAIN; + public: CTrain(int32 modelIndex, eVehicleCreatedBy createdBy); diff --git a/source/game_sa/Entity/Vehicle/Vehicle.h b/source/game_sa/Entity/Vehicle/Vehicle.h index 91846c37f7..55615ff34f 100644 --- a/source/game_sa/Entity/Vehicle/Vehicle.h +++ b/source/game_sa/Entity/Vehicle/Vehicle.h @@ -421,6 +421,8 @@ class NOTSA_EXPORT_VTABLE CVehicle : public CPhysical { static bool &s_bPlaneGunsEjectShellCasings; static inline tHydraulicData(&m_aSpecialHydraulicData)[4] = *(tHydraulicData(*)[4])0xC1CB60; + static constexpr auto Type = VEHICLE_TYPE_IGNORE; + public: CVehicle(eVehicleCreatedBy createdBy); ~CVehicle() override; diff --git a/source/game_sa/Enums/ePedType.h b/source/game_sa/Enums/ePedType.h index 3f4b5cd11c..da35a8ae68 100644 --- a/source/game_sa/Enums/ePedType.h +++ b/source/game_sa/Enums/ePedType.h @@ -58,6 +58,15 @@ static constexpr bool IsPedTypeGang(ePedType ptype) { return std::ranges::find(s_GangPedTypes, ptype) != s_GangPedTypes.end(); } +inline bool IsPedTypeFemale(ePedType type) { + switch (type) { + case PED_TYPE_PROSTITUTE: + case PED_TYPE_CIVFEMALE: + return true; + } + return false; +} + static constexpr auto GetAllGangPedTypes() { return std::array{ PED_TYPE_GANG1, diff --git a/source/game_sa/Enums/eScriptCommands.h b/source/game_sa/Enums/eScriptCommands.h index 37e6a460d9..674e6f8d4d 100644 --- a/source/game_sa/Enums/eScriptCommands.h +++ b/source/game_sa/Enums/eScriptCommands.h @@ -2674,13 +2674,8 @@ enum eScriptCommands { COMMAND_IS_XBOX_PLAYER2_PRESSING_START = 0x0A4E, COMMAND_FINISHED_WITH_XBOX_PLAYER2 = 0x0A4F, COMMAND_DO_DEBUG_STUFF = 0x0A50, - - COMMAND_HIGHEST_ID - /* - // NOTSA - - // CLEO + // NOTSA -- CLEO COMMAND_WRITE_MEMORY = 0x0A8C, // 2700 COMMAND_READ_MEMORY = 0x0A8D, COMMAND_INT_ADD = 0x0A8E, @@ -2877,18 +2872,19 @@ enum eScriptCommands { COMMAND_IMGUI_GET_SCALING_SIZE = 0x0C37, COMMAND_IMGUI_SET_NEXT_WINDOW_TRANSPARENCY = 0x0C38, // 3128 - // INI - COMMAND_READ_INT_FROM_INI_FILE = 0x0AF0, // 2800 - COMMAND_WRITE_INT_TO_INI_FILE = 0x0AF1, - COMMAND_READ_FLOAT_FROM_INI_FILE = 0x0AF2, - COMMAND_WRITE_FLOAT_TO_INI_FILE = 0x0AF3, - COMMAND_READ_STRING_FROM_INI_FILE = 0x0AF4, - COMMAND_WRITE_STRING_TO_INI_FILE = 0x0AF5, // 2805 - - - // Make sure this is always up to date! - COMMAND_HIGHEST_ID = COMMAND_PERLIN_NOISE_FRACTAL_3D + //! Make sure this is always up to date! + COMMAND_HIGHEST_ID = COMMAND_PERLIN_NOISE_FRACTAL_3D, */ + //! Highest vanilla SA ID + COMMAND_HIGHEST_VANILLA_ID = COMMAND_DO_DEBUG_STUFF, + + //! Highest command ID that may be hooked + COMMAND_HIGHEST_ID_TO_HOOK +#ifdef NOTSA_USE_CLEO_COMMANDS + = COMMAND_HIGHEST_ID +#else + = COMMAND_HIGHEST_VANILLA_ID +#endif }; #define CASE(c) \ @@ -5543,7 +5539,6 @@ static std::string_view GetScriptCommandName(eScriptCommands cmd) { CASE(COMMAND_IS_XBOX_PLAYER2_PRESSING_START); CASE(COMMAND_FINISHED_WITH_XBOX_PLAYER2); CASE(COMMAND_DO_DEBUG_STUFF); - /* CASE(COMMAND_WRITE_MEMORY); CASE(COMMAND_READ_MEMORY); diff --git a/source/game_sa/Events/EventAttractor.cpp b/source/game_sa/Events/EventAttractor.cpp index 343dbbce1b..35fcad7467 100644 --- a/source/game_sa/Events/EventAttractor.cpp +++ b/source/game_sa/Events/EventAttractor.cpp @@ -85,7 +85,7 @@ bool CEventAttractor::AffectsPed_Reversed(CPed* ped) return true; if (!g_ikChainMan.IsLooking(ped)) { uint32 time = CGeneral::GetRandomNumberInRange(2000, 4000); - CVector point = m_entity->GetMatrix() * m_2dEffect->m_vecPosn; + CVector point = m_entity->GetMatrix() * m_2dEffect->m_pos; g_ikChainMan.LookAt("CEventAttractor", ped, 0, time, BONE_UNKNOWN, &point, false, 0.25f, 500, 3, false); } } @@ -104,7 +104,7 @@ bool CEventAttractor::IsEffectActive(CEntity* entity, const C2dEffect* effect) { auto modelInfo = CModelInfo::GetModelInfo(entity->m_nModelIndex); for (int32 i = 0; i < modelInfo->m_n2dfxCount; i++) { - if (effect->m_nType == EFFECT_ATTRACTOR && effect == modelInfo->Get2dEffect(i)) + if (effect->m_type == EFFECT_ATTRACTOR && effect == modelInfo->Get2dEffect(i)) return true; } return false; diff --git a/source/game_sa/Events/EventDamage.cpp b/source/game_sa/Events/EventDamage.cpp index ac9ccf03e0..5b717604ab 100644 --- a/source/game_sa/Events/EventDamage.cpp +++ b/source/game_sa/Events/EventDamage.cpp @@ -954,3 +954,12 @@ void CEventDamage::ComputeDamageAnim(CPed* ped, bool bMakeActiveTaskAbortable) { } } } + +// NOTSA +void CEventDamage::ComputeDamageResponseIfAffectsPed(CPed* ped, CPedDamageResponseCalculator calculator, bool bSpeak) { + if (AffectsPed(ped)) { + calculator.ComputeDamageResponse(ped, m_damageResponse, bSpeak); + } else { + m_damageResponse.SetDamageAsCalculated(); + } +} diff --git a/source/game_sa/Events/EventDamage.h b/source/game_sa/Events/EventDamage.h index f0608cecd3..6b0fd7ba78 100644 --- a/source/game_sa/Events/EventDamage.h +++ b/source/game_sa/Events/EventDamage.h @@ -7,6 +7,7 @@ #include "Event.h" enum ePedPieceTypes; +class CPedDamageResponseCalculator; class NOTSA_EXPORT_VTABLE CEventDamage : public CEventEditableResponse { public: @@ -58,6 +59,9 @@ class NOTSA_EXPORT_VTABLE CEventDamage : public CEventEditableResponse { void ComputeDeathAnim(CPed* ped, bool bMakeActiveTaskAbortable); void ComputeDamageAnim(CPed* ped, bool bMakeActiveTaskAbortable); + //! Either computes the damage, or sets it as computed (Without computing it) - Very common logic in the code + void ComputeDamageResponseIfAffectsPed(CPed* ped, CPedDamageResponseCalculator calculator, bool bSpeak); + private: friend void InjectHooksMain(); static void InjectHooks(); diff --git a/source/game_sa/Events/EventGroup.h b/source/game_sa/Events/EventGroup.h index 7087cd2579..0fd35162d6 100644 --- a/source/game_sa/Events/EventGroup.h +++ b/source/game_sa/Events/EventGroup.h @@ -42,9 +42,9 @@ class CEventGroup { auto GetEvents() { return std::span{ m_events, (size_t)m_count }; } auto GetEvents() const { return std::span{ m_events, (size_t)m_count }; } - // Helper so events can be directly passed in without having to put them into a variable - template - CEvent* Add(T event, bool valid = false) requires std::is_base_of_v { + //! Helper so events can be directly passed in without having to put them into a variable + template T> + CEvent* Add(T event, bool valid = false) { return Add(&event, valid); } }; diff --git a/source/game_sa/FileLoader.cpp b/source/game_sa/FileLoader.cpp index 52590a3413..e8197c7c80 100644 --- a/source/game_sa/FileLoader.cpp +++ b/source/game_sa/FileLoader.cpp @@ -962,8 +962,8 @@ void CFileLoader::Load2dEffect(const char* line) { auto& effect = CModelInfo::Get2dEffectStore()->AddItem(); CModelInfo::GetModelInfo(modelId)->Add2dEffect(&effect); - effect.m_vecPosn = pos; - effect.m_nType = *reinterpret_cast(type); + effect.m_pos = pos; + effect.m_type = *reinterpret_cast(type); switch (type) { case EFFECT_LIGHT: diff --git a/source/game_sa/Fire.h b/source/game_sa/Fire.h index 6095f9a7dd..b38c484756 100644 --- a/source/game_sa/Fire.h +++ b/source/game_sa/Fire.h @@ -64,6 +64,9 @@ class CFire { bool HasTimeToBurn() const; bool IsNotInRemovalDistance() const; auto& GetPosition() const { return m_vecPosition; } + + //! Script thing ID + auto GetId() { return m_nScriptReferenceIndex; } }; VALIDATE_SIZE(CFire, 0x28); diff --git a/source/game_sa/Fx/Fx.cpp b/source/game_sa/Fx/Fx.cpp index 4b01b6d70c..941191f83c 100644 --- a/source/game_sa/Fx/Fx.cpp +++ b/source/game_sa/Fx/Fx.cpp @@ -258,8 +258,8 @@ void Fx_c::TriggerWaterHydrant(CVector& posn) { } // 0x4A0DE0 -void Fx_c::TriggerGunshot(CEntity* entity, CVector& origin, CVector& target, bool doGunflash) { - ((void(__thiscall*)(Fx_c*, CEntity*, CVector&, CVector&, bool))0x4A0DE0)(this, entity, origin, target, doGunflash); +void Fx_c::TriggerGunshot(CEntity* entity, const CVector& origin, const CVector& direction, bool doGunflash) { + ((void(__thiscall*)(Fx_c*, CEntity*, const CVector&, const CVector&, bool))0x4A0DE0)(this, entity, origin, direction, doGunflash); } // 0x4A0FA0 diff --git a/source/game_sa/Fx/Fx.h b/source/game_sa/Fx/Fx.h index 395a1dedab..c602474322 100644 --- a/source/game_sa/Fx/Fx.h +++ b/source/game_sa/Fx/Fx.h @@ -100,7 +100,7 @@ class Fx_c { void AddWheelSand(CVehicle* vehicle, CVector posn, uint8 arg2, float brightness); void AddWheelDust(CVehicle* vehicle, CVector posn, uint8 arg2, float brightness); void TriggerWaterHydrant(CVector& posn); - void TriggerGunshot(CEntity* entity, CVector& origin, CVector& target, bool doGunflash); + void TriggerGunshot(CEntity* entity, const CVector& origin, const CVector& target, bool doGunflash); void TriggerTankFire(CVector& origin, CVector& target); void TriggerWaterSplash(CVector& posn); void TriggerBulletSplash(CVector& posn); diff --git a/source/game_sa/GangWars.cpp b/source/game_sa/GangWars.cpp index 7d75908426..de82d050c2 100644 --- a/source/game_sa/GangWars.cpp +++ b/source/game_sa/GangWars.cpp @@ -177,7 +177,7 @@ void CGangWars::CheerVictory() { }; for (auto i = 0u; i < std::size(zoneNames); i++) { - if (!_stricmp(pZoneToFightOver->m_szTextKey, zoneNames[i])) { + if (!_stricmp(pZoneToFightOver->m_TextLabel, zoneNames[i])) { nearestMember->Say(208 + i); break; } diff --git a/source/game_sa/Glass.cpp b/source/game_sa/Glass.cpp index 3744b03410..c390f856bc 100644 --- a/source/game_sa/Glass.cpp +++ b/source/game_sa/Glass.cpp @@ -74,7 +74,7 @@ bool CGlass::HasGlassBeenShatteredAtCoors(CVector point) { FindWindowSectorList(GetSector(sectorX, sectorY)->m_dummies, maxDist, entity, point); } } - return entity && !entity->IsDummy() && entity->AsObject()->objectFlags.bGlassBroken; + return entity && !entity->IsDummy() && entity->AsObject()->objectFlags.bHasBrokenGlass; } // 0x71C2B0 @@ -195,12 +195,12 @@ void CGlass::WasGlassHitByBullet(CEntity* entity, CVector hitPos) { return; const auto object = entity->AsObject(); - if (object->objectFlags.bGlassBroken) { + if (object->objectFlags.bHasBrokenGlass) { if (CGeneral::GetRandomNumber() % 4 == 2) { WindowRespondsToCollision(entity, 0.0f, {}, hitPos, false); } } else { - object->objectFlags.bGlassBroken = true; // Just mark it as broken + object->objectFlags.bHasBrokenGlass = true; // Just mark it as broken } } @@ -213,10 +213,10 @@ std::pair FindMinMaxZOfVertices(CVector (&vertices)[N]) { // 0x71BC40 void CGlass::WindowRespondsToCollision(CEntity* entity, float fDamageIntensity, CVector vecMoveSpeed, CVector vecPoint, bool max1PaneSection) { auto object = entity->AsObject(); - if (object->objectFlags.b0x20) + if (object->objectFlags.bGlassBrokenAltogether) return; - object->objectFlags.bGlassBroken = true; + object->objectFlags.bHasBrokenGlass = true; if (const auto cd = object->GetColModel()->m_pColData; cd && cd->m_nNumTriangles == 2) { // Object space vertices @@ -251,7 +251,7 @@ void CGlass::WindowRespondsToCollision(CEntity* entity, float fDamageIntensity, object->m_bUsesCollision = false; object->m_bIsVisible = false; - object->objectFlags.b0x20 = true; + object->objectFlags.bGlassBrokenAltogether = true; } /* @@ -523,9 +523,9 @@ CFallingGlassPane* CGlass::FindFreePane() { // 0x71AF70 void CGlass::WindowRespondsToSoftCollision(CEntity* entity, float fDamageIntensity) { - if (entity->m_bUsesCollision && fDamageIntensity > 50.f && !entity->AsObject()->objectFlags.bGlassBroken) { + if (entity->m_bUsesCollision && fDamageIntensity > 50.f && !entity->AsObject()->objectFlags.bHasBrokenGlass) { AudioEngine.ReportGlassCollisionEvent(AE_GLASS_HIT, entity->GetPosition()); - entity->AsObject()->objectFlags.bGlassBroken = true; + entity->AsObject()->objectFlags.bHasBrokenGlass = true; } } @@ -575,9 +575,9 @@ void CGlass::BreakGlassPhysically(CVector point, float radius) { LastColCheckMS = CTimer::GetTimeInMS(); - if (!object->objectFlags.bGlassBroken) { + if (!object->objectFlags.bHasBrokenGlass) { AudioEngine.ReportGlassCollisionEvent(AE_GLASS_HIT, objPos); - object->objectFlags.bGlassBroken = true; + object->objectFlags.bHasBrokenGlass = true; return; } @@ -606,14 +606,14 @@ void CGlass::BreakGlassPhysically(CVector point, float radius) { {}, point, 0.1f, - object->objectFlags.bGlassBroken, + object->objectFlags.bHasBrokenGlass, false, 1, false ); object->m_bUsesCollision = false; object->m_bIsVisible = false; - object->objectFlags.bGlassBroken = true; + object->objectFlags.bHasBrokenGlass = true; } } @@ -628,7 +628,7 @@ void CGlass::WindowRespondsToExplosion(CEntity* entity, CVector pos) { const auto dist = entityToPosDir.Magnitude(); if (dist >= 10.f) { if (dist < 30.f) { - entity->AsObject()->objectFlags.bGlassBroken = true; + entity->AsObject()->objectFlags.bHasBrokenGlass = true; } } else { WindowRespondsToCollision(entity, 10000.f, entityToPosDir * (0.3f / dist), entityPos, true); diff --git a/source/game_sa/Messages.cpp b/source/game_sa/Messages.cpp index 828d9e2756..f5e93617e0 100644 --- a/source/game_sa/Messages.cpp +++ b/source/game_sa/Messages.cpp @@ -106,7 +106,7 @@ void CMessages::AddMessage2(const char* text, uint32 time, uint16 flag, bool bPr * Adds message to [Q]ueue * @addr 0x69F0B0 */ -void CMessages::AddMessageQ(const char* text, uint32 time, uint16 flag, bool bPreviousBrief) { +void CMessages::AddMessageQ(const char* text, uint32 time, uint16 flag, bool bPreviousBrief) { // Renamed from AddMessage /* Some string copy code here */ AddMessage2(text, time, flag, bPreviousBrief, false); } @@ -124,7 +124,7 @@ void CMessages::AddMessageWithStringQ(const char* text, uint32 time, uint16 flag * Show a message instantly * @addr 0x69F1E0 */ -void CMessages::AddMessageJump(const char* text, uint32 time, uint16 flag, bool bPreviousBrief) { +void CMessages::AddMessageJump(const char* text, uint32 time, uint16 flag, bool bPreviousBrief) { // Renamed from AddMessageJumpQ /* unused string copy here */ AddMessage2(text, time, flag, bPreviousBrief, true); } diff --git a/source/game_sa/Messages.h b/source/game_sa/Messages.h index f46ca46495..cd1f3f4771 100644 --- a/source/game_sa/Messages.h +++ b/source/game_sa/Messages.h @@ -72,10 +72,10 @@ class CMessages { static void Init(); static void AddMessage2(const char* text, uint32 time, uint16 flag, bool bPreviousBrief, bool showInstantly = false, char* str = nullptr, std::optional> numbers = {}); - static void AddMessageQ(const char* text, uint32 time, uint16 flag, bool bPreviousBrief); + static void AddMessageQ(const char* text, uint32 time, uint16 flag, bool bPreviousBrief); // Renamed from AddMessage static void AddMessageWithStringQ(const char* text, uint32 time, uint16 flag, char* string, bool bPreviousBrief); static void AddMessageWithNumberQ(const char* text, uint32 time, uint16 flag, int32 n1 = -1, int32 n2 = -1, int32 n3 = -1, int32 n4 = -1, int32 n5 = -1, int32 n6 = -1, bool bPreviousBrief = false); - static void AddMessageJump(const char* text, uint32 time, uint16 flag, bool bPreviousBrief); + static void AddMessageJump(const char* text, uint32 time, uint16 flag, bool bPreviousBrief); // Renamed from AddMessageJumpQ static void AddMessageJumpQWithNumber(const char* text, uint32 time, uint16 flag, int32 n1 = -1, int32 n2 = -1, int32 n3 = -1, int32 n4 = -1, int32 n5 = -1, int32 n6 = -1, bool bPreviousBrief = false); static void AddMessageJumpQWithString(const char* text, uint32 time, uint16 flag, char* string, bool bPreviousBrief); diff --git a/source/game_sa/MissionCleanup.cpp b/source/game_sa/MissionCleanup.cpp index 221b3ffa09..536a2a366f 100644 --- a/source/game_sa/MissionCleanup.cpp +++ b/source/game_sa/MissionCleanup.cpp @@ -54,3 +54,31 @@ void CMissionCleanup::RemoveEntityFromList(int32 handle, MissionCleanUpEntityTyp void CMissionCleanup::CheckIfCollisionHasLoadedForMissionObjects() { ((void(__thiscall*)(CMissionCleanup*))0x4652D0)(this); } + +// NOTSA +void CMissionCleanup::AddEntityToList(CObject& obj) { + return AddEntityToList(GetObjectPool()->GetRef(&obj), MISSION_CLEANUP_ENTITY_TYPE_OBJECT); +} + +// NOTSA +void CMissionCleanup::AddEntityToList(CPed& ped) { + return AddEntityToList(GetPedPool()->GetRef(&ped), MISSION_CLEANUP_ENTITY_TYPE_PED); +} + +// NOTSA +void CMissionCleanup::AddEntityToList(CVehicle& veh) { + return AddEntityToList(GetVehiclePool()->GetRef(&veh), MISSION_CLEANUP_ENTITY_TYPE_VEHICLE); +} + +// NOTSA +void CMissionCleanup::RemoveEntityFromList(CObject& obj) { + return RemoveEntityFromList(GetObjectPool()->GetRef(&obj), MISSION_CLEANUP_ENTITY_TYPE_OBJECT); +} + +void CMissionCleanup::RemoveEntityFromList(CPed& ped) { + return RemoveEntityFromList(GetPedPool()->GetRef(&ped), MISSION_CLEANUP_ENTITY_TYPE_PED); +} + +void CMissionCleanup::RemoveEntityFromList(CVehicle& veh) { + return RemoveEntityFromList(GetVehiclePool()->GetRef(&veh), MISSION_CLEANUP_ENTITY_TYPE_VEHICLE); +} diff --git a/source/game_sa/MissionCleanup.h b/source/game_sa/MissionCleanup.h index d60af59ea1..b79e33579f 100644 --- a/source/game_sa/MissionCleanup.h +++ b/source/game_sa/MissionCleanup.h @@ -62,6 +62,16 @@ class CMissionCleanup { // Checks if collision has loaded for mission objects void CheckIfCollisionHasLoadedForMissionObjects(); + + + // QoL (NOTSA) - TODO: Use `PooledType` + some traits shit to get the `MissionCleanUpEntityType` of `T` + void AddEntityToList(CObject& obj); + void AddEntityToList(CPed& ped); + void AddEntityToList(CVehicle& veh); + + void RemoveEntityFromList(CObject& obj); + void RemoveEntityFromList(CPed& ped); + void RemoveEntityFromList(CVehicle& veh); }; VALIDATE_SIZE(CMissionCleanup, 0x25C); diff --git a/source/game_sa/NodeAddress.h b/source/game_sa/NodeAddress.h index bc0b7cac88..610b7b826d 100644 --- a/source/game_sa/NodeAddress.h +++ b/source/game_sa/NodeAddress.h @@ -20,6 +20,9 @@ class CNodeAddress { void ResetAreaId() { m_wAreaId = UINT16_MAX; } void ResetNodeId() { m_wNodeId = UINT16_MAX; } - [[nodiscard]] bool IsValid() const { return m_wAreaId != (uint16)-1; }}; + [[nodiscard]] bool IsAreaValid() const { return m_wAreaId != (uint16)-1; } + [[nodiscard]] bool IsValid() const { return m_wAreaId != (uint16)-1 || m_wNodeId != (uint16)-1; } + operator bool() const { return IsValid(); } +}; VALIDATE_SIZE(CNodeAddress, 0x4); diff --git a/source/game_sa/PathFind.h b/source/game_sa/PathFind.h index 834261ab10..f1c81fc745 100644 --- a/source/game_sa/PathFind.h +++ b/source/game_sa/PathFind.h @@ -303,8 +303,8 @@ class CPathFind { bool GeneratePedCreationCoors_Interior(float x, float y, CVector* outCoords, CNodeAddress* unused1, CNodeAddress* unused2, float* outOrientation); void GeneratePedCreationCoors(float x, float y, float minDist1, float maxDist1, float minDist2, float maxDist2, CVector* outCoords, CNodeAddress* outAddress1, CNodeAddress* outAddress2, float* outOrientation, bool bLowTraffic, CMatrix* transformMatrix); - CNodeAddress FindNodeClosestToCoors(CVector pos, ePathType nodeType, float maxDistance, uint16 unk2, int32 unk3, uint16 unk4, - uint16 bBoatsOnly, int32 unk6); + CNodeAddress FindNodeClosestToCoors(CVector pos, ePathType nodeType = PATH_TYPE_PED, float maxDistance = 999999.88f, uint16 unk2 = 1, int32 unk3 = 0, uint16 unk4 = 0, + uint16 bBoatsOnly = 0, int32 unk6 = 0); void MarkRoadNodeAsDontWander(float x, float y, float z); void RecordNodesClosestToCoors(CVector pos, uint8 nodeType, int count, CNodeAddress* outAddresses, float maxDist, bool, bool, bool, bool); void Find2NodesForCarCreation(CVector pos, CNodeAddress* outAddress1, CNodeAddress* outAddress2, bool bLowTraffic); diff --git a/source/game_sa/PedAttractorManager.cpp b/source/game_sa/PedAttractorManager.cpp index 8a5a304f5b..621e81878e 100644 --- a/source/game_sa/PedAttractorManager.cpp +++ b/source/game_sa/PedAttractorManager.cpp @@ -209,8 +209,8 @@ void* CPedAttractorManager::GetPedUsingEffect(const C2dEffect* effect, const CEn } // 0x5EBE50 -void* CPedAttractorManager::GetPedUsingEffect(const C2dEffect* effect, const CEntity* entity) { - return plugin::CallMethodAndReturn(this, effect, entity); +CPed* CPedAttractorManager::GetPedUsingEffect(const C2dEffect* effect, const CEntity* entity) { + return plugin::CallMethodAndReturn(this, effect, entity); } // 0x5EB7B0 @@ -225,7 +225,7 @@ void* CPedAttractorManager::GetRelevantAttractor(const CPed* ped, const C2dEffec // 0x5E96C0 void CPedAttractorManager::ComputeEffectPos(const C2dEffect* effect, const CMatrix& mat, CVector& vec) { - vec.FromMultiply(mat, &effect->m_vecPosn); + vec.FromMultiply(mat, &effect->m_pos); } // 0x5E96E0 diff --git a/source/game_sa/PedAttractorManager.h b/source/game_sa/PedAttractorManager.h index 99fec20020..56377b5999 100644 --- a/source/game_sa/PedAttractorManager.h +++ b/source/game_sa/PedAttractorManager.h @@ -39,7 +39,7 @@ class CPedAttractorManager { bool HasEmptySlot(const C2dEffect* effect, const CEntity* entity); void* GetPedUsingEffect(const C2dEffect* effect, const CEntity* entity, const SArray& array); - void* GetPedUsingEffect(const C2dEffect* effect, const CEntity* entity); + CPed* GetPedUsingEffect(const C2dEffect* effect, const CEntity* entity = nullptr); void* GetRelevantAttractor(const CPed* ped, const C2dEffect* effect, const CEntity* entity, const SArray& array); void* GetRelevantAttractor(const CPed* ped, const C2dEffect* effect, const CEntity* entity); diff --git a/source/game_sa/PedDamageResponse.h b/source/game_sa/PedDamageResponse.h index 3f358b6511..349d1f6cb2 100644 --- a/source/game_sa/PedDamageResponse.h +++ b/source/game_sa/PedDamageResponse.h @@ -17,6 +17,9 @@ class CPedDamageResponse { m_bDamageCalculated = false; m_bCheckIfAffectsPed = false; } + + //! Mark the damage as calculated + void SetDamageAsCalculated() { m_bDamageCalculated = true; } }; VALIDATE_SIZE(CPedDamageResponse, 0xC); diff --git a/source/game_sa/PedDamageResponseCalculator.cpp b/source/game_sa/PedDamageResponseCalculator.cpp index 7eb365fa6a..2401d5be62 100644 --- a/source/game_sa/PedDamageResponseCalculator.cpp +++ b/source/game_sa/PedDamageResponseCalculator.cpp @@ -128,3 +128,4 @@ bool CPedDamageResponseCalculator::IsBleedingWeapon(CPed* ped) const { void CPedDamageResponseCalculator::ComputeDamageResponse(CPed* ped, CPedDamageResponse& response, bool bSpeak) { plugin::CallMethod<0x4B5AC0, CPedDamageResponseCalculator*, CPed*, CPedDamageResponse&, bool>(this, ped, response, bSpeak); } + diff --git a/source/game_sa/PedGeometryAnalyser.h b/source/game_sa/PedGeometryAnalyser.h index 592066d553..69ae714eb6 100644 --- a/source/game_sa/PedGeometryAnalyser.h +++ b/source/game_sa/PedGeometryAnalyser.h @@ -65,11 +65,14 @@ class CPedGeometryAnalyser { static void CanPedJumpObstacle(const CPed& ped, const CEntity& entity); static void CanPedJumpObstacle(const CPed& ped, const CEntity& entity, const CVector&, const CVector&); - static bool CanPedTargetPed(CPed& ped, CPed& targetPed, bool a3); + + static bool CanPedTargetPed(CPed& ped, CPed& targetPed, bool checkDirection); static bool CanPedTargetPoint(const CPed& ped, const CVector& a2, bool a3); static int32 ComputeBuildingHitPoints(const CVector& a1, const CVector& a2); + static void ComputeClearTarget(const CPed& ped, const CVector&, CVector&); + static bool ComputeClosestSurfacePoint(const CPed& ped, CEntity& entity, CVector& point); static bool ComputeClosestSurfacePoint(const CVector& posn, CEntity& entity, CVector& point); static bool ComputeClosestSurfacePoint(const CVector& posn, const CVector* corners, CVector& point); diff --git a/source/game_sa/PedGroupMembership.cpp b/source/game_sa/PedGroupMembership.cpp index e48c8feb1e..9a28bf8cea 100644 --- a/source/game_sa/PedGroupMembership.cpp +++ b/source/game_sa/PedGroupMembership.cpp @@ -97,6 +97,11 @@ void CPedGroupMembership::RemoveMember(int32 memberID) { plugin::CallMethod<0x5F80D0, CPedGroupMembership*, int32>(this, memberID); } +// 0x5FB210 +void CPedGroupMembership::RemoveMember(CPed& ped) { + plugin::CallMethod<0x5FB210>(this, &ped); +} + // 0x5FB1D0 char CPedGroupMembership::RemoveNFollowers(int32 count) { return plugin::CallMethodAndReturn(this, count); diff --git a/source/game_sa/PedGroupMembership.h b/source/game_sa/PedGroupMembership.h index d747704460..0445f62544 100644 --- a/source/game_sa/PedGroupMembership.h +++ b/source/game_sa/PedGroupMembership.h @@ -45,6 +45,7 @@ class CPedGroupMembership { void Process(); void RemoveAllFollowers(bool bCreatedByGameOnly); void RemoveMember(int32 memberID); + void RemoveMember(CPed& ped); char RemoveNFollowers(int32 count); void SetLeader(CPed* ped); diff --git a/source/game_sa/PlaceName.cpp b/source/game_sa/PlaceName.cpp index b981213499..a4de7c9647 100644 --- a/source/game_sa/PlaceName.cpp +++ b/source/game_sa/PlaceName.cpp @@ -46,11 +46,11 @@ void CPlaceName::Process() { Init(); } - if ((smallestZone == m_pZone || CGame::currArea == AREA_CODE_1) // todo: !CGame::CanSeeOutSideFromCurrArea() ? + if ((smallestZone == m_pZone || CGame::currArea == AREA_CODE_1) && m_pZone || smallestZone - && m_pZone // yep, checked twice - && memcmp(smallestZone->m_szTextKey, m_pZone->m_szTextKey, sizeof(smallestZone->m_szTextKey)) == 0 + && m_pZone // yep, checked twice (Pirulax: gotta account for those cosmis bit flipping rays?) + && memcmp(smallestZone->m_TextLabel, m_pZone->m_TextLabel, sizeof(smallestZone->m_TextLabel)) == 0 ) { if (m_nAdditionalTimer) { m_nAdditionalTimer -= 1; diff --git a/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.cpp b/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.cpp index de271ce4ab..a8d68de5a1 100644 --- a/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.cpp +++ b/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.cpp @@ -33,7 +33,7 @@ void C2dEffect::InjectHooks() void C2dEffect::Shutdown() { - if (m_nType == e2dEffectType::EFFECT_ROADSIGN) { + if (m_type == e2dEffectType::EFFECT_ROADSIGN) { if (roadsign.m_pText) { CMemoryMgr::Free(roadsign.m_pText); roadsign.m_pText = nullptr; @@ -44,7 +44,7 @@ void C2dEffect::Shutdown() roadsign.m_pAtomic = nullptr; } } - else if (m_nType == e2dEffectType::EFFECT_LIGHT) { + else if (m_type == e2dEffectType::EFFECT_LIGHT) { if (light.m_pCoronaTex) { RwTextureDestroy(light.m_pCoronaTex); light.m_pCoronaTex = nullptr; diff --git a/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.h b/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.h index b8530e2589..7f367d1b9e 100644 --- a/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.h +++ b/source/game_sa/Plugins/TwoDEffectPlugin/2dEffect.h @@ -57,6 +57,8 @@ enum e2dCoronaFlashType : uint8 { }; struct tEffectLight { + static inline constexpr e2dEffectType Type = EFFECT_LIGHT; + RwRGBA m_color; float m_fCoronaFarClip; float m_fPointlightRange; @@ -97,11 +99,15 @@ struct tEffectLight { VALIDATE_SIZE(tEffectLight, 0x30); struct tEffectParticle { + static inline constexpr e2dEffectType Type = EFFECT_PARTICLE; + char m_szName[24]; }; VALIDATE_SIZE(tEffectParticle, 0x18); struct tEffectPedAttractor { + static inline constexpr e2dEffectType Type = EFFECT_ATTRACTOR; + RwV3d m_vecQueueDir; RwV3d m_vecUseDir; RwV3d m_vecForwardDir; @@ -114,6 +120,8 @@ struct tEffectPedAttractor { VALIDATE_SIZE(tEffectPedAttractor, 0x30); struct tEffectEnEx { + static inline constexpr e2dEffectType Type = EFFECT_ENEX; + float m_fEnterAngle; RwV2d m_vecRadius; RwV3d m_vecExitPosn; @@ -143,6 +151,8 @@ struct CRoadsignAttrFlags { VALIDATE_SIZE(CRoadsignAttrFlags, 0x2); struct tEffectRoadsign { + static inline constexpr e2dEffectType Type = EFFECT_ROADSIGN; + RwV2d m_vecSize; RwV3d m_vecRotation; CRoadsignAttrFlags m_nFlags; @@ -152,17 +162,23 @@ struct tEffectRoadsign { VALIDATE_SIZE(tEffectRoadsign, 0x20); struct tEffectSlotMachineWheel { + static inline constexpr e2dEffectType Type = EFFECT_TRIGGER_POINT; + int32 m_nId; }; VALIDATE_SIZE(tEffectSlotMachineWheel, 0x4); struct tEffectCoverPoint { + static inline constexpr e2dEffectType Type = EFFECT_COVER_POINT; + RwV2d m_vecDirection; uint8 m_nType; }; VALIDATE_SIZE(tEffectCoverPoint, 0xC); struct tEffectEscalator { + static inline constexpr e2dEffectType Type = EFFECT_ESCALATOR; + RwV3d m_vecBottom; RwV3d m_vecTop; RwV3d m_vecEnd; @@ -170,11 +186,26 @@ struct tEffectEscalator { }; VALIDATE_SIZE(tEffectEscalator, 0x28); -class C2dEffect { - public: - CVector m_vecPosn; - e2dEffectType m_nType; - char _pad0[3]; +//! NOTASA base class (otherwise SA) +struct C2dEffectBase { + CVector m_pos; + e2dEffectType m_type; +}; +VALIDATE_SIZE(C2dEffectBase, 0x10); + +//! NOTSA +//! Prefer using these types instead of the `C2dEffect` for type safety reasons +//! They are binary compatible (that is, same memory layout) +struct C2dEffectLight : C2dEffectBase, tEffectLight {}; +struct C2dEffectParticle : C2dEffectBase, tEffectParticle {}; +struct C2dEffectPedAttractor : C2dEffectBase, tEffectPedAttractor {}; +struct C2dEffectEnEx : C2dEffectBase, tEffectEnEx {}; +struct C2dEffectRoadsign : C2dEffectBase, tEffectRoadsign {}; +struct C2dEffectSlotMachineWheel : C2dEffectBase, tEffectSlotMachineWheel {}; +struct C2dEffectCoverPoint : C2dEffectBase, tEffectCoverPoint {}; +struct C2dEffectEscalator : C2dEffectBase, tEffectEscalator {}; + +struct C2dEffect : public C2dEffectBase { union { tEffectLight light; tEffectParticle particle; diff --git a/source/game_sa/PopCycle.cpp b/source/game_sa/PopCycle.cpp index 6e0775b8af..9673dbee2f 100644 --- a/source/game_sa/PopCycle.cpp +++ b/source/game_sa/PopCycle.cpp @@ -16,7 +16,7 @@ void CPopCycle::InjectHooks() { RH_ScopedGlobalInstall(Initialise, 0x5BC090); RH_ScopedGlobalInstall(PickGangToCreateMembersOf, 0x60F8D0); RH_ScopedGlobalInstall(FindNewPedType, 0x60FBD0); - RH_ScopedGlobalInstall(PickPedMIToStreamInForCurrentZone, 0x60FFD0); + RH_ScopedGlobalInstall(PickPedMIToStreamInForCurrentZone, 0x60FFD0, { .reversed = false }); RH_ScopedGlobalInstall(IsPedAppropriateForCurrentZone, 0x610150); RH_ScopedGlobalInstall(IsPedInGroup, 0x610210); RH_ScopedGlobalInstall(PickARandomGroupOfOtherPeds, 0x610420); diff --git a/source/game_sa/Population.cpp b/source/game_sa/Population.cpp index f14a3c0b7f..0d952010cd 100644 --- a/source/game_sa/Population.cpp +++ b/source/game_sa/Population.cpp @@ -516,7 +516,7 @@ CPed* CPopulation::AddExistingPedInCar(CPed* ped, CVehicle* vehicle) { } // 0x611570 -void CPopulation::UpdatePedCount(CPed* ped, uint8 pedAddedOrRemoved) { +void CPopulation::UpdatePedCount(CPed* ped, bool pedAddedOrRemoved) { if (pedAddedOrRemoved != ped->bHasBeenAddedToPopulation) { return; } @@ -1466,7 +1466,7 @@ bool CPopulation::AddPedAtAttractor(eModelID modelIndex, C2dEffect* attractor, C ped->GetIntelligence()->SetPedDecisionMakerType(decisionMakerType == -1 ? 2 : decisionMakerType); ped->GetTaskManager().SetTask(CTaskComplexWander::GetWanderTaskByPedType(ped), TASK_PRIMARY_DEFAULT); - CPedAttractorPedPlacer::PlacePedAtEffect(*attractor, entity, ped, 0.02f); + CPedAttractorPedPlacer::PlacePedAtEffect(reinterpret_cast(*attractor), entity, ped, 0.02f); ped->bUseAttractorInstantly = true; ped->GetEventGroup().Add(CEventAttractor{ attractor, ped, true, TASK_COMPLEX_USE_ATTRACTOR }); @@ -1671,12 +1671,12 @@ int32 CPopulation::GeneratePedsAtAttractors( if (!ent->IsObject()) { continue; } - if (!ent->AsObject()->objectFlags.b0x1000000) { + if (!ent->AsObject()->objectFlags.bEnableDisabledAttractors) { continue; } } - const auto effectPosWS = ent->GetMatrix() * effect->m_vecPosn; // ws = world space + const auto effectPosWS = ent->GetMatrix() * effect->m_pos; // ws = world space if (!IsEffectInRadius(effectPosWS)) { continue; } diff --git a/source/game_sa/Population.h b/source/game_sa/Population.h index f029b7408d..93a15ae540 100644 --- a/source/game_sa/Population.h +++ b/source/game_sa/Population.h @@ -120,7 +120,7 @@ class CPopulation { * @param ped The ped that was added/removed * @param pedAddedOrRemoved Should be `false` if the ped was added, or `true` if it was removed */ - static void UpdatePedCount(CPed* ped, uint8 pedAddedOrRemoved); + static void UpdatePedCount(CPed* ped, bool pedAddedOrRemoved); // empty function static void MoveCarsAndPedsOutOfAbandonedZones(); diff --git a/source/game_sa/Rope.cpp b/source/game_sa/Rope.cpp index d3a7ac64e5..d8d3c5aa3f 100644 --- a/source/game_sa/Rope.cpp +++ b/source/game_sa/Rope.cpp @@ -20,10 +20,10 @@ void CRope::InjectHooks() { // use switch like in Android? // 0x555FB0 bool CRope::DoControlsApply() const { - return m_nType == eRopeType::CRANE_MAGNO && CRopes::PlayerControlsCrane == 1 - || m_nType == eRopeType::WRECKING_BALL && CRopes::PlayerControlsCrane == 2 - || m_nType == eRopeType::CRANE_TROLLEY && CRopes::PlayerControlsCrane == 3 - || m_nType == eRopeType::QUARRY_CRANE_ARM && CRopes::PlayerControlsCrane == 4 + return m_nType == eRopeType::CRANE_MAGNO && CRopes::PlayerControlsCrane == eControlledCrane::MAGNO_CRANE + || m_nType == eRopeType::WRECKING_BALL && CRopes::PlayerControlsCrane == eControlledCrane::WRECKING_BALL + || m_nType == eRopeType::CRANE_TROLLEY && CRopes::PlayerControlsCrane == eControlledCrane::LAS_VEGAS_CRANE + || m_nType == eRopeType::QUARRY_CRANE_ARM && CRopes::PlayerControlsCrane == eControlledCrane::QUARRY_CRANE || m_nType == eRopeType::CRANE_MAGNET1 || m_nType == eRopeType::MAGNET || m_nType == eRopeType::CRANE_HARNESS; diff --git a/source/game_sa/Ropes.cpp b/source/game_sa/Ropes.cpp index 9ecb794e07..fcad28eb08 100644 --- a/source/game_sa/Ropes.cpp +++ b/source/game_sa/Ropes.cpp @@ -10,7 +10,7 @@ #include "Rope.h" #include "Ropes.h" -int32& CRopes::PlayerControlsCrane = *(int32*)0xB76898; +eControlledCrane& CRopes::PlayerControlsCrane = *(eControlledCrane*)0xB76898; uint32& CRopes::m_nRopeIdCreationCounter = *(uint32*)0xB781F8; void CRopes::InjectHooks() { @@ -35,7 +35,7 @@ void CRopes::Init() { for (auto& rope : aRopes) { rope.m_nType = eRopeType::NONE; } - PlayerControlsCrane = false; + PlayerControlsCrane = eControlledCrane::NONE; } // 0x556B10 diff --git a/source/game_sa/Ropes.h b/source/game_sa/Ropes.h index 90391d853f..a8434ebe74 100644 --- a/source/game_sa/Ropes.h +++ b/source/game_sa/Ropes.h @@ -8,12 +8,21 @@ #include "Rope.h" +// NOTSA +enum class eControlledCrane : int32 { + NONE, + MAGNO_CRANE, + WRECKING_BALL, + LAS_VEGAS_CRANE, + QUARRY_CRANE +}; + class CRopes { public: static constexpr auto MAX_NUM_ROPES{ 8u }; static inline std::array& aRopes = *(std::array*)0xB768B8; // Access using CRopes::GetRope() - static int32& PlayerControlsCrane; + static eControlledCrane& PlayerControlsCrane; static uint32& m_nRopeIdCreationCounter; public: diff --git a/source/game_sa/ScriptResourceManager.cpp b/source/game_sa/ScriptResourceManager.cpp index 83bdcd9711..f89533ce7f 100644 --- a/source/game_sa/ScriptResourceManager.cpp +++ b/source/game_sa/ScriptResourceManager.cpp @@ -21,19 +21,17 @@ void CScriptResourceManager::Initialise() { // 0x4704B0 void CScriptResourceManager::AddToResourceManager(int32 modelId, eScriptResourceType type, CRunningScript* script) { - assert(false); + plugin::Call<0x4704B0, int32, eScriptResourceType, CRunningScript*>(modelId, type, script); } // 0x470510 bool CScriptResourceManager::RemoveFromResourceManager(int32 modelId, eScriptResourceType type, CRunningScript* script) { - assert(false); - return true; + return plugin::CallAndReturn(modelId, type, script); } // 0x470620 bool CScriptResourceManager::HasResourceBeenRequested(int32 modelId, eScriptResourceType type) { - assert(false); - return true; + return plugin::CallAndReturn(modelId, type); } // 0x0 diff --git a/source/game_sa/Scripts/CommandParser/LUTGenerator.hpp b/source/game_sa/Scripts/CommandParser/LUTGenerator.hpp index 7ee9d1f3d4..1a222ec14c 100644 --- a/source/game_sa/Scripts/CommandParser/LUTGenerator.hpp +++ b/source/game_sa/Scripts/CommandParser/LUTGenerator.hpp @@ -9,11 +9,19 @@ #include "eScriptCommands.h" #include "RunningScript.h" +#if 0 namespace notsa { namespace script { /// Type of the function used in the LUT using T_LUTFunction = OpcodeResult(*)(CRunningScript*); + +//! Whenever command has a handler made by us (or uses the default GTA one) +template +static constexpr auto CommandHasCustomHandler() { + return CommandHandler::value; +} + namespace detail { /// Wrap `CRunningScript::ProcessCommand` into a type function with type `T_LUTFunction` @@ -46,12 +54,12 @@ static constexpr auto GetHandlerOfCommand() { } constexpr void IterateCommandIDs(auto&& functor) { - ::notsa::IterateFunction<0, (int)COMMAND_HIGHEST_ID>(functor); + ::notsa::IterateFunction<0, (int)COMMAND_HIGHEST_ID_TO_HOOK>(functor); } /// Generate the script command handler look-up-table (LUT) static constexpr auto GenerateLUT() { - std::array lut{}; + std::array lut{}; IterateCommandIDs([&]() { lut[Idx] = GetHandlerOfCommand<(eScriptCommands)Idx>(); @@ -62,3 +70,4 @@ static constexpr auto GenerateLUT() { }; // namespace script }; // namespace notsa +#endif diff --git a/source/game_sa/Scripts/CommandParser/Parser.hpp b/source/game_sa/Scripts/CommandParser/Parser.hpp index 060832adaa..3f2a6b597f 100644 --- a/source/game_sa/Scripts/CommandParser/Parser.hpp +++ b/source/game_sa/Scripts/CommandParser/Parser.hpp @@ -11,52 +11,111 @@ namespace notsa { namespace script { namespace detail { template -OpcodeResult CollectArgsAndCall(CRunningScript* S, T_FnRet(*CommandFn)(T_FnArgs...), T_CollectedArgs&&... args) { +inline OpcodeResult CollectArgsAndCall(CRunningScript* S, eScriptCommands command, T_FnRet(*CommandFn)(T_FnArgs...), T_CollectedArgs&&... args) { if constexpr (sizeof...(T_CollectedArgs) == sizeof...(T_FnArgs)) { // Has it collected enough args? - const auto CallCommandFn = [&] { + const auto CallCommandFn = [&]() -> T_FnRet { return std::invoke(CommandFn, std::forward(args)...); }; + if constexpr (std::is_same_v) { CallCommandFn(); // Returns void, nothing to push } else { - const auto ret = CallCommandFn(); - if constexpr (std::is_same_v) { // Special handling for OpcodeResult's, hopefully soon to be removed (once all commands start using the parser) + T_FnRet ret = CallCommandFn(); + if constexpr (std::is_same_v) { return ret; } else { - StoreArg(S, ret); // Store result to script + StoreArg(S, std::forward(ret)); // Store result to script } } + return OR_CONTINUE; - } else { - // Not enough args, collect more. - return CollectArgsAndCall( - S, - CommandFn, - std::forward(args)..., // Forward read ones - notsa::script::Read>(S) // Read next parameter - ); + } else { // Not enough args, read more + using T_ToRead = nth_element_t; + const auto ContinueWithArg = [&](T&& arg) { + return CollectArgsAndCall( + S, + command, + CommandFn, + std::forward(args)..., + std::forward(arg) + ); + }; + + if constexpr (std::is_same_v) { // Special case for reading the current script comamnd + return ContinueWithArg(command); + } else { + return ContinueWithArg(notsa::script::Read(S)); // Read next parameter and continue + } + + // Don't remove this, we might need it! + //try { + //} catch (const std::exception&) { + // In cases the argument parser has errored out we still + // have to read all values before returning. + // Reason: The IP has to be increased + //((void)(notsa::script::Read(S)), ...); + //return OR_CONTINUE; + //} } } -}; // namespace detail + +//! Called for unimplemented commands +//! These are commands that have no (special) code associated with them +inline auto NotImplemented(eScriptCommands cmd) { +#ifdef NOTSA_DEBUG + DEV_LOG("Unimplemented command has been called! [ID: {:04X}; Name: {}]", (unsigned)(cmd), GetScriptCommandName(cmd)); +#endif + return OR_INTERRUPT; // Vanilla SA behavior +} template -OpcodeResult CommandParser(CRunningScript* S) { - return detail::CollectArgsAndCall(S, CommandFn); +inline OpcodeResult CommandParser(CRunningScript* S) { + return detail::CollectArgsAndCall(S, Command, CommandFn); } -}; -}; +template +inline void AddCommandHandler() { + auto& entry = CRunningScript::CustomCommandHandlerOf(Command); + // Make sure it doesn't already have a handler + if (entry) { + NOTSA_UNREACHABLE("Command already handled!\nOpcode: {:04X} ({})", (unsigned)(Command), GetScriptCommandName(Command)); + } + entry = &CommandParser; // Register handler +} +}; // namespace detail +}; // namespace script +}; // namespace notsa template struct CommandHandler : std::false_type {}; -/*! -* Use this macro to register a parsed function -*/ +namespace notsa { +namespace detail { +//! Returns raw function pointer of a function or lambda. +//! The `+` operator is a hack to get lambda's function pointer. +//! The `+` is a no-op in this case (As in, it won't increment the value, or anything like that, is just merely forces a decay to a pointer [from a lambda object instance]) +//! See: https://stackoverflow.com/questions/18889028 +template +constexpr inline auto AddressOfFunction(T fn) { + return +(fn); +} +}; +}; + +//! Register a custom command handler #define REGISTER_COMMAND_HANDLER(cmd, fn) \ - template<> \ - struct CommandHandler : std::true_type { \ - constexpr static auto Command = cmd; \ - constexpr static auto Function = fn; \ - } \ + ::notsa::script::detail::AddCommandHandler() + +//! Register a command handler for an unimplemented command (That is, a command that wasn't implemented in the game either) +#define REGISTER_COMMAND_UNIMPLEMENTED(cmd) \ + REGISTER_COMMAND_HANDLER(cmd, ::notsa::script::detail::NotImplemented) +#ifdef IMPLEMENT_UNSUPPORTED_OPCODES +//! Register a custom command handler for an unimplmented command. (Won't be implemented if IMPLEMENT_UNSUPPORTED_OPCODES is not defined.) +#define REGISTER_UNSUPPORTED_COMMAND_HANDLER(cmd, fn) \ + REGISTER_COMMAND_HANDLER(cmd, fn) +#else +//! Register a custom command handler for an unimplmented command. (Not implemented if IMPLEMENT_UNSUPPORTED_OPCODES is not defined.) +#define REGISTER_UNSUPPORTED_COMMAND_HANDLER(cmd, fn) \ + REGISTER_COMMAND_HANDLER(cmd, ::notsa::script::detail::NotImplemented) +#endif diff --git a/source/game_sa/Scripts/CommandParser/ReadArg.hpp b/source/game_sa/Scripts/CommandParser/ReadArg.hpp index 6abfe15df7..3b5f965b5e 100644 --- a/source/game_sa/Scripts/CommandParser/ReadArg.hpp +++ b/source/game_sa/Scripts/CommandParser/ReadArg.hpp @@ -1,173 +1,285 @@ #pragma once #include +#include #include "RunningScript.h" +#include "app_debug.h" +#include "World.h" #include "TheScripts.h" #include "Utility.hpp" #include "Rect.h" +#include "Scripted2dEffects.h" namespace notsa { namespace script { -template -Y Read(CRunningScript* S); +// eModelID wrapper that is read either from a static +// int32 value or UsedObjectArray. +struct Model { + eModelID model; -auto ReadArrayInfo(CRunningScript* S) { + operator eModelID() const { return model; } +}; + +namespace detail { + +//! Script thing bullshittery +namespace scriptthing { +//! Get script thing at index +template +inline auto GetAt(uint32 index) -> T& = delete; +template<> +inline auto GetAt(uint32 idx) -> tScriptSphere& { return CTheScripts::ScriptSphereArray[idx]; } +template<> +inline auto GetAt(uint32 idx) -> tScriptEffectSystem& { return CTheScripts::ScriptEffectSystemArray[idx]; } +template<> +inline auto GetAt(uint32 idx) -> tScriptSearchlight& { return CTheScripts::ScriptSearchLightArray[idx]; } +template<> +inline auto GetAt(uint32 idx) -> tScriptSequence& { return CTheScripts::ScriptSequenceTaskArray[idx]; } +template<> +inline auto GetAt(uint32 idx) -> tScriptCheckpoint& { return CTheScripts::ScriptCheckpointArray[idx]; } +template T> +inline auto GetAt(uint32 idx) -> T& { + auto& effect = CScripted2dEffects::ms_effects[idx]; + assert(T::Type == effect.m_type); + return reinterpret_cast(effect); +} + +//! Get ID of a thing at a given index +template +inline auto GetId(uint32 idx) -> uint32 { return GetAt(idx).GetId(); } +template T> +inline auto GetId(uint32 idx) -> uint32 { return CScripted2dEffects::ScriptReferenceIndex[idx]; } + +//! Get if a script thing is active +template +inline auto IsActive(uint32 idx) -> bool { return GetAt(idx).IsActive(); } +template T> +inline auto IsActive(uint32 idx) -> bool { return CScripted2dEffects::ms_activated[idx]; } + +//! Check if `T` is a script thing +template +inline constexpr auto is_script_thing_v = requires(uint32 index) { GetAt(index); }; +static_assert(is_script_thing_v && !is_script_thing_v); +}; // namespace scriptthing +}; + +namespace detail { +inline auto ReadArrayInfo(CRunningScript* S) { uint16 offset{}; int32 idx{}; S->ReadArrayInformation(true, &offset, &idx); return std::make_tuple(offset, idx); } -/*! -* @notsa -* @brief Read an arithmetic type (numbers/enums/bools/floats) -* @return The value read casted to the required type. The value will be read correctly, -* even if the original type was wider or narrower then the -* requested one (eg.: originally it was `int8`, but `T = int32`) -*/ template -T Read(CRunningScript* S) requires(std::is_arithmetic_v || std::is_enum_v) { - auto& ip = S->m_pCurrentIP; - - switch (CTheScripts::Read1ByteFromScript(ip)) { - case SCRIPT_PARAM_STATIC_INT_32BITS: - return static_cast(CTheScripts::Read4BytesFromScript(ip)); - case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: - return *reinterpret_cast(&CTheScripts::ScriptSpace[CTheScripts::Read2BytesFromScript(ip)]); - case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: - return *reinterpret_cast(S->GetPointerToLocalVariable(CTheScripts::Read2BytesFromScript(ip))); - case SCRIPT_PARAM_STATIC_INT_8BITS: - return static_cast(CTheScripts::Read1ByteFromScript(ip)); - case SCRIPT_PARAM_STATIC_INT_16BITS: - return static_cast(CTheScripts::Read2BytesFromScript(ip)); - case SCRIPT_PARAM_STATIC_FLOAT: - return static_cast(CTheScripts::ReadFloatFromScript(ip)); - case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: { - const auto [offset, idx] = ReadArrayInfo(S); - return *reinterpret_cast(CTheScripts::ScriptSpace[offset + 4 * idx]); - } - case SCRIPT_PARAM_LOCAL_NUMBER_ARRAY: { - const auto [offset, idx] = ReadArrayInfo(S); - return *reinterpret_cast(S->GetPointerToLocalArrayElement(offset, idx, 1)); +concept PooledType = + requires { detail::PoolOf(); }; +}; + +namespace detail { +//! Safely cast one arithmetic type to another (Checks for under/overflow in debug mode only), then casts to `T` +template +inline T safe_arithmetic_cast(F value) { +#ifdef NOTSA_DEBUG + if constexpr (std::numeric_limits::lowest() < std::numeric_limits::lowest()) { // Underflow + assert(value >= static_cast(std::numeric_limits::lowest())); } - default: - NOTSA_UNREACHABLE(); + if constexpr (std::numeric_limits::max() >= std::numeric_limits::max()) { // Overflow + assert(value <= static_cast(std::numeric_limits::max())); } +#endif + return static_cast(value); // In release mode just a simple, regular cast } -/*! -* @brief Special overload for stuff in pools. -* @return Always a reference to the object `T` -*/ -template -concept PooledType = requires { - detail::PoolOf>(); +//! Check if `Derived` is derived from `Base` but isn't `Base` +template +constexpr auto is_derived_from_but_not_v = std::is_base_of_v && !std::is_same_v; }; -template> -Y& Read(CRunningScript* S) { - auto& pool = detail::PoolOf(); - const auto ref = Read(S); - if (const auto p = pool.GetAtRef(ref)) { - return *p; - } - NOTSA_UNREACHABLE("Object expected, got null. [Script Ref/Index: {}/{}]", ref, pool.GetIndexFromRef(ref)); -} +//! Read a value (Possibly from script => increases IP, or return a value (w/o increasing IP) +template +inline T Read(CRunningScript* S) { + using Y = std::remove_pointer_t>; -/*! -* Allow taking the current script as a parameter. -* This isn't an actual script parameter, so reading -* it doesn't increase the IP. -*/ -template<> -CRunningScript& Read(CRunningScript* S) { - return *S; -} + // First of all, deal with references + // References are a way to express that a valud (non-null) value must be present + // While simple pointers are a way to express that "it's okay if it's null, I can handle it". + // This check here also means that all other branches must either return by-value or a pointer (not a refernce) + if constexpr (std::is_reference_v) { + const auto ptr = Read*>(S); + assert(ptr); + return *ptr; + } else if constexpr (std::is_same_v) { + return { Read(S), Read(S), Read(S) }; + } else if constexpr (std::is_same_v) { + return { Read(S), Read(S) }; + } else if constexpr (std::is_same_v) { + return { Read(S), Read(S) }; + } else if constexpr (std::is_same_v) { + auto& IP = S->m_IP; -// Same as above, but returns a pointer instead -template<> -CRunningScript* Read(CRunningScript* S) { - return S; -} + const auto FromScriptSpace = [](const auto offset) -> std::string_view { + return { (const char*)&CTheScripts::ScriptSpace[offset] }; + }; -template<> -CVector Read(CRunningScript* S) { - return { Read(S), Read(S), Read(S) }; -} + const auto FromGlobalArray = [&](uint8 elemsz) -> std::string_view { + const auto [idx, offset] = detail::ReadArrayInfo(S); + return FromScriptSpace(elemsz * idx + offset); + }; -template<> -CVector2D Read(CRunningScript* S) { - return { Read(S), Read(S) }; -} + const auto FromLocalArray = [&](uint8 elemsz) -> std::string_view { + const auto [idx, offset] = detail::ReadArrayInfo(S); + return { (const char*)S->GetPointerToLocalArrayElement(offset, idx, elemsz) }; + }; -template<> -CRect Read(CRunningScript* S) { - return { Read(S), Read(S) }; -} -template<> -CPlayerInfo& Read(CRunningScript* S) { - return FindPlayerInfo(Read(S)); -} + const auto FromStaticString = [&](size_t strsz) -> std::string_view { + const auto str = (const char*)(IP); + IP += strsz; + return str; + }; -template<> -CPlayerPed& Read(CRunningScript* S) { - return *FindPlayerPed(Read(S)); -} + switch (const auto ptype = (eScriptParameterType)S->ReadAtIPAs()) { + case SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE: + return FromScriptSpace(S->ReadAtIPAs()); -template<> -std::string_view Read(CRunningScript* S) { - auto& IP = S->m_pCurrentIP; - - const auto FromScriptSpace = [](const auto offset) -> std::string_view { - return { (const char*)&CTheScripts::ScriptSpace[offset] }; - }; - - const auto FromGlobalArray = [&](uint8 elemsz) -> std::string_view { - const auto [idx, offset] = ReadArrayInfo(S); - return FromScriptSpace(elemsz * idx + offset); - }; - - const auto FromLocalArray = [&](uint8 elemsz) -> std::string_view { - const auto [idx, offset] = ReadArrayInfo(S); - return { (const char*)S->GetPointerToLocalArrayElement(offset, idx, elemsz) }; - }; - - const auto FromStaticString = [&](size_t buffsz) -> std::string_view { - IP += buffsz; - return { (const char*)(IP - buffsz) }; - }; - - const auto type = (eScriptParameterType)CTheScripts::Read1ByteFromScript(IP); // As a variable so can inspect it in debugger - switch (type) { - case SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE: - case SCRIPT_PARAM_STATIC_SHORT_STRING: - return FromStaticString(SHORT_STRING_SIZE); - case SCRIPT_PARAM_STATIC_LONG_STRING: - case SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE: - return FromStaticString(LONG_STRING_SIZE); - case SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE: - return FromScriptSpace(CTheScripts::Read2BytesFromScript(IP)); - case SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE: - return { (const char*)S->GetPointerToLocalVariable(CTheScripts::Read2BytesFromScript(IP)) }; - case SCRIPT_PARAM_GLOBAL_SHORT_STRING_ARRAY: - return FromGlobalArray(SHORT_STRING_SIZE); - case SCRIPT_PARAM_GLOBAL_LONG_STRING_ARRAY: - return FromGlobalArray(LONG_STRING_SIZE); - case SCRIPT_PARAM_LOCAL_SHORT_STRING_ARRAY: - return FromLocalArray(2); - case SCRIPT_PARAM_LOCAL_LONG_STRING_ARRAY: - return FromLocalArray(4); - case SCRIPT_PARAM_STATIC_PASCAL_STRING: { - const auto sz = CTheScripts::Read1ByteFromScript(IP); // sign extension. max size = 127, not 255 - assert(sz >= 0); - IP += sz; - return { (const char*)(IP - sz), (size_t)sz }; - } - default: - NOTSA_UNREACHABLE(); + case SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE: + return { (const char*)S->GetPointerToLocalVariable(S->ReadAtIPAs()) }; + + case SCRIPT_PARAM_GLOBAL_SHORT_STRING_ARRAY: + return FromGlobalArray(SHORT_STRING_SIZE); + case SCRIPT_PARAM_GLOBAL_LONG_STRING_ARRAY: + return FromGlobalArray(LONG_STRING_SIZE); + + case SCRIPT_PARAM_LOCAL_SHORT_STRING_ARRAY: + return FromLocalArray(2); + case SCRIPT_PARAM_LOCAL_LONG_STRING_ARRAY: + return FromLocalArray(4); + + case SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE: + case SCRIPT_PARAM_STATIC_SHORT_STRING: + return FromStaticString(SHORT_STRING_SIZE); + case SCRIPT_PARAM_STATIC_LONG_STRING: + case SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE: + return FromStaticString(LONG_STRING_SIZE); + case SCRIPT_PARAM_STATIC_PASCAL_STRING: { + const auto sz = S->ReadAtIPAs(); // sign extension. max size = 127, not 255 + assert(sz >= 0); + const auto str = (const char*)(IP); + IP += (ptrdiff_t)(sz); + return std::string_view{ str, (size_t)(sz) }; + } + default: + NOTSA_UNREACHABLE("Unknown param type: {}", (int32)(ptype)); + } + } else if constexpr (std::is_same_v) { // Read C-style string (Hacky) + const auto sv = Read(S); + assert(sv.data()[sv.size()] == 0); // Check if str is 0 terminated - Not using `str[]` here as it would assert. + return sv.data(); + } else if constexpr (std::is_arithmetic_v) { // Simple arithmetic types (After reading a string, because `const char*` with cv and pointer removed is just `char` which is an arithmetic type) + const auto ptype = S->ReadAtIPAs(); + if constexpr (std::is_pointer_v) { // This is a special case, as some basic ops need a reference instead of a value + switch (ptype) { + case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: + return reinterpret_cast(&CTheScripts::ScriptSpace[S->ReadAtIPAs()]); + case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: + return reinterpret_cast(S->GetPointerToLocalVariable(S->ReadAtIPAs())); + case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: { + const auto [offset, idx] = detail::ReadArrayInfo(S); + return reinterpret_cast(&CTheScripts::ScriptSpace[offset + sizeof(tScriptParam) * idx]); + } + case SCRIPT_PARAM_LOCAL_NUMBER_ARRAY: { + const auto [offset, idx] = detail::ReadArrayInfo(S); + return reinterpret_cast(S->GetPointerToLocalArrayElement(offset, idx, 1)); + } + } + } else { // Regular by-value + switch (ptype) { + case SCRIPT_PARAM_STATIC_INT_32BITS: + return detail::safe_arithmetic_cast(S->ReadAtIPAs()); + case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: + return *reinterpret_cast(&CTheScripts::ScriptSpace[S->ReadAtIPAs()]); + case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: + return *reinterpret_cast(S->GetPointerToLocalVariable(S->ReadAtIPAs())); + case SCRIPT_PARAM_STATIC_INT_8BITS: + return detail::safe_arithmetic_cast(S->ReadAtIPAs()); + case SCRIPT_PARAM_STATIC_INT_16BITS: + return detail::safe_arithmetic_cast(S->ReadAtIPAs()); + case SCRIPT_PARAM_STATIC_FLOAT: { + if constexpr (!std::is_floating_point_v) { + DebugBreak(); // Possibly unintended truncation of `float` to integeral type! Check your call stack and change the function argument type to a float. + } + return detail::safe_arithmetic_cast(S->ReadAtIPAs()); + } + case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: { + const auto [offset, idx] = detail::ReadArrayInfo(S); + return *reinterpret_cast(&CTheScripts::ScriptSpace[offset + sizeof(tScriptParam) * idx]); + } + case SCRIPT_PARAM_LOCAL_NUMBER_ARRAY: { + const auto [offset, idx] = detail::ReadArrayInfo(S); + return *reinterpret_cast(S->GetPointerToLocalArrayElement(offset, idx, 1)); + } + } + } + NOTSA_UNREACHABLE("Unknown param type: {}", (int32)(ptype)); + } else if constexpr (std::is_enum_v) { // Read underlaying arithmetic type and cast (then profit) + return static_cast(Read>(S)); + } else if constexpr (std::is_same_v) { // Special case for `CPlayerPed` (As the IDs for it aren't from the pool) + return FindPlayerPed(Read(S)); + } else if constexpr (detail::PooledType) { // Pooled types (CVehicle, CPed, etc) + T ptr = static_cast(detail::PoolOf().GetAtRef(Read(S))); + + #ifdef NOTSA_DEBUG + if (ptr) { + if constexpr (detail::is_derived_from_but_not_v) { + assert(Y::Type == ptr->m_nVehicleType); + } else if constexpr (detail::is_derived_from_but_not_v) { + assert(Y::Type == ptr->GetTaskType()); + } // TODO: Eventually add this for `CEvent` too + } + #endif + + return ptr; + } else if constexpr (std::is_same_v) { // Just return the script from where this command was invoked from + return S; + } else if constexpr (std::is_same_v) { + return &FindPlayerInfo(Read(S)); + } else if constexpr (detail::scriptthing::is_script_thing_v) { + // Read information (packed 2x16 int) + const auto info = Read(S); + if (info == (uint32)(-1)) { // Invalid handle, may happen if a function returned it (and they didn't handle it properly) + return nullptr; + } + + // Extract index and (expected) ID of the object + const auto index = (uint16)(HIWORD(info)), id = (uint16)(LOWORD(info)); + + // Check if the object is active (If not, it has been reused/deleted) + if (!detail::scriptthing::IsActive(index)) { + return nullptr; + } + + // Check if ID is what we expect (If not, that means that the object has been reused) + if (detail::scriptthing::GetId(index) != id) { + return nullptr; + } + + return &detail::scriptthing::GetAt(index); + } else if constexpr (std::is_same_v) { + const auto value = Read(S); + if (value < 0) { + // we get the model from UsedObjectArray. + return {static_cast(CTheScripts::UsedObjectArray[-value].nModelIndex)}; + } + + return {static_cast(value)}; } + // If there's an error like "function must return a value" here, + // that means that no suitable branch was found for `T` + // You have 2 options: Either add a branch for that type, + // or check and make sure you didn't try taking some object by-value + // that you shouldn't have (Like `CPed`, `CVehicle`, or such) } }; // namespace script diff --git a/source/game_sa/Scripts/CommandParser/StoreArg.hpp b/source/game_sa/Scripts/CommandParser/StoreArg.hpp index e8c53f9290..36e979791d 100644 --- a/source/game_sa/Scripts/CommandParser/StoreArg.hpp +++ b/source/game_sa/Scripts/CommandParser/StoreArg.hpp @@ -1,9 +1,8 @@ #pragma once -//#include - #include "Base.h" #include "Utility.hpp" +#include "ReadArg.hpp" // TODO: We only use `PooledType` from here, so move that out to somewhere common between the 2 headers (because including this here is ugly) #include "TheScripts.h" #include "RunningScript.h" // ScriptParams #include "Pools.h" @@ -14,7 +13,7 @@ namespace script { * Bool is a sepcial case, it isn't stored, but rather updates the compare flag. * TODO: Actually verify this theory. */ -void StoreArg(CRunningScript* S, bool arg) { +inline void StoreArg(CRunningScript* S, bool arg) { S->UpdateCompareFlag(arg); } /*! @@ -24,9 +23,10 @@ void StoreArg(CRunningScript* S, bool arg) { * @param arg The argument to store */ template -void StoreArg(CRunningScript* S, const T& arg) requires (std::is_arithmetic_v) { // Add requirements to filter out possible mistakes (Like returning an unsupported type) + requires (std::is_arithmetic_v) +inline void StoreArg(CRunningScript* S, const T& arg) { // Add requirements to filter out possible mistakes (Like returning an unsupported type) tScriptParam* dest = [&] { - auto& ip = S->m_pCurrentIP; + auto& ip = S->m_IP; // Helper const auto ReadArrayInfo = [S] { @@ -36,7 +36,7 @@ void StoreArg(CRunningScript* S, const T& arg) requires (std::is_arithmetic_v return std::make_tuple(offset, idx); }; - switch (CTheScripts::Read1ByteFromScript(ip)) { + switch (const auto t = CTheScripts::Read1ByteFromScript(ip)) { case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: return reinterpret_cast(&CTheScripts::ScriptSpace[CTheScripts::Read2BytesFromScript(ip)]); case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: { @@ -51,7 +51,7 @@ void StoreArg(CRunningScript* S, const T& arg) requires (std::is_arithmetic_v return S->GetPointerToLocalArrayElement(offset, idx, 1); } default: - NOTSA_UNREACHABLE("Variable type unknown"); + NOTSA_UNREACHABLE("Variable type unknown ={:x}", t); } }(); static_assert(sizeof(T) <= sizeof(tScriptParam)); // Otherwise we'd be overwriting the script => bad @@ -62,40 +62,56 @@ void StoreArg(CRunningScript* S, const T& arg) requires (std::is_arithmetic_v // Vector overloads -void StoreArg(CRunningScript* script, const CVector& v3) { +inline void StoreArg(CRunningScript* script, const CVector& v3) { for (auto&& c : v3.GetComponents()) { StoreArg(script, c); } } -void StoreArg(CRunningScript* S, const CVector2D& v2) { +inline void StoreArg(CRunningScript* S, const CVector2D& v2) { for (auto&& c : v2.GetComponents()) { StoreArg(S, c); } } -void StoreArg(CRunningScript* S, CompareFlagUpdate flag) { +inline void StoreArg(CRunningScript* S, CompareFlagUpdate flag) { S->UpdateCompareFlag(flag.state); } // Below must be after the basic overloads, otherwise won't compile +//! Store a pooled type (CPed, CVehicle, etc) - It pushes a handle of the entity to the script +template +inline void StoreArg(CRunningScript* S, const T& value) { + const auto StoreEntity = [&](auto ptr) { StoreArg(S, detail::PoolOf>().GetRef(ptr)); }; + if constexpr (std::is_pointer_v) { + if (value) { // As always, pointers might be null, so we have to check. + StoreEntity(value); + } else { + StoreArg(S, -1); // If null, store `-1` (indicates an invalid handle, it is handled properly!) + } + } else { // References are never invalid + StoreEntity(&value); + } +} + + /*! -* @brief Overload for types that have a pool (thus we store a reference) -*/ -template> -void StoreArg(CRunningScript* S, T* arg) requires(detail::PoolOf()) { - StoreArg(S, detail::PoolOf().GetRef(arg)); + * @brief Overload for enum types. They're casted to their underlying type. + */ +template + requires std::is_enum_v +inline void StoreArg(CRunningScript* S, T value) { + StoreArg(S, static_cast>(value)); } /*! * @brief Overload for MultiReturn => Stores each arg separately, in same order as they appear in the multireturn */ template -void StoreArg(CRunningScript* S, const MultiRet& arg) { +inline void StoreArg(CRunningScript* S, const MultiRet& arg) { std::apply([S](const Ts&... args) { (StoreArg(S, args), ...); }, arg); } - }; // namespace script }; // namespace notsa diff --git a/source/game_sa/Scripts/CommandParser/Utility.hpp b/source/game_sa/Scripts/CommandParser/Utility.hpp index 5cd3cc2405..bf1dddd81c 100644 --- a/source/game_sa/Scripts/CommandParser/Utility.hpp +++ b/source/game_sa/Scripts/CommandParser/Utility.hpp @@ -8,50 +8,84 @@ namespace notsa { namespace script { namespace detail { template -auto& PoolOf(); +auto& PoolOf() = delete; -template<> -auto& PoolOf() { return *GetPedPool(); } -template<> -auto& PoolOf() { return *GetVehiclePool(); } -template<> -auto& PoolOf() { return *GetBuildingPool(); } -template<> -auto& PoolOf() { return *GetObjectPool(); } -template<> -auto& PoolOf() { return *GetDummyPool(); } template<> auto& PoolOf() { return *GetColModelPool(); } -template<> -auto& PoolOf() { return *GetTaskPool(); } + template<> auto& PoolOf() { return *GetPedIntelligencePool(); } + template<> auto& PoolOf() { return *GetPtrNodeSingleLinkPool(); } + template<> auto& PoolOf() { return *GetPtrNodeDoubleLinkPool(); } + template<> auto& PoolOf() { return *GetEntryInfoNodePool(); } + template<> auto& PoolOf() { return *GetPointRoutePool(); } + template<> auto& PoolOf() { return *GetPatrolRoutePool(); } -template<> -auto& PoolOf() { return *GetEventPool(); } + template<> auto& PoolOf() { return *GetNodeRoutePool(); } + template<> auto& PoolOf() { return *GetTaskAllocatorPool(); } + template<> auto& PoolOf() { return *GetPedAttractorPool(); } + +/* +* Pools of derived types +*/ +template + requires std::is_base_of_v +auto& PoolOf() { return *GetPedPool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetVehiclePool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetBuildingPool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetObjectPool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetDummyPool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetTaskPool(); } + +template + requires std::is_base_of_v +auto& PoolOf() { return *GetEventPool(); } }; // detail /*! * Struct used to return multiple values from a script function. -* Eg.: MultiRet => Push 2 ints using `StoreParameters` +* Eg.: MultiRet => Should push 2 ints (Using `StoreArg`) +* Don't use this directly, use `return_multiple` instead (Unless you have to fwd. declare the function or smth) */ template using MultiRet = std::tuple; + +//! Used to return multiple values from a script function, see `MultiReturn` +template +constexpr auto return_multiple(Ts&&... args) { + return MultiRet{args...}; +} + //struct MultiRet : public std::tuple { // using std::tuple::tuple; //}; @@ -63,6 +97,26 @@ struct CompareFlagUpdate { bool state{}; }; +using CommandHandlerFunction = OpcodeResult(*)(CRunningScript*); + +//! Fix angles (in degrees) - heaviliy used in scripts. +inline float FixAngleDegrees(float deg) { + if (deg < 0.f) { + return deg + 360.f; + } + if (deg > 360.f) { + return deg - 360.f; + } + return deg; +} + +//! Get the ped or it's vehicle (if in one) +inline auto GetPedOrItsVehicle(CPed& ped) -> CPhysical& { + if (ped.IsInVehicle()) { + return *ped.m_pVehicle; + } + return ped; +} }; // script diff --git a/source/game_sa/Scripts/Commands/Basic.cpp b/source/game_sa/Scripts/Commands/Basic.cpp new file mode 100644 index 0000000000..79d35f45bc --- /dev/null +++ b/source/game_sa/Scripts/Commands/Basic.cpp @@ -0,0 +1,394 @@ +#include + +#include "./Commands.hpp" +#include +#include + +/* +* Basic language feature commands (Comparasions, assingments, etc...) +*/ + +// +// GREATER_OR_EQUAL +// +template +bool IsGreaterEqual(T lhs, T rhs) { + return lhs >= rhs; +} + +// +// GREATER +// +template +bool IsGreater(T lhs, T rhs) { + return lhs > rhs; +} + +// +// EQUAL +// +template +bool IsEqual(T lhs, T rhs) { + return lhs == rhs; +} + +// +// SUB +// +template +void SubInPlace(T& lhs, T rhs) { + lhs -= (T)(rhs); +} + +// +// ADD +// +template +void AddInPlace(T& lhs, T rhs) { + lhs += (T)(rhs); +} + +// +// MULT +// +template +void MultInPlace(T& lhs, T rhs) { + lhs *= (T)(rhs); +} + +// +// DIV +// +template +void DivInPlace(T& lhs, T rhs) { + lhs /= (T)(rhs); +} + +// +// SET (Used for casting float <=> int too) +// +template +void AssignTo(T& lhs, Y rhs) { + lhs = (T)(rhs); +} + +// +// SUB TIMED +// +void SubTimedStore(float& lhs, float rhs) { + lhs -= CTimer::GetTimeStep() * rhs; +} + +// +// ADD TIMED +// +void AddTimedStore(float& lhs, float rhs) { + lhs += CTimer::GetTimeStep() * rhs; +} + +// +// ABS +// +template +void AbsStore(T& value) { + value = std::abs(value); +} + +// +// GOTO +// +auto GoTo(CRunningScript& S, int32 address) { + S.UpdatePC(address); +} + +auto GoToIfFalse(CRunningScript& S, int32 goToAddress) { + if (!S.m_bCondResult) { + S.UpdatePC(goToAddress); + } +} + +auto GoToSub(CRunningScript& S, int32 goToAddress) { // 0x050 + S.m_IPStack[S.m_StackDepth++] = S.m_IP; + S.UpdatePC(goToAddress); +} + +// +// RETURN +// + +//! Return true (from function) +auto ReturnTrue() { + return true; +} + +//! Return false (from function) +auto ReturnFalse() { + return false; +} + +//! Jump back to calle +auto Return(CRunningScript& S) { + S.m_IP = S.m_IPStack[--S.m_StackDepth]; +} + +// +// LOGICAL OPS +// + +// OR, AND +auto AndOr(CRunningScript& S, int32 logicalOp) { // 0x0D6 + // Read comment above `CRunningScript::LogicalOpType` for a little more insight! + S.m_nLogicalOp = logicalOp; + if (S.m_nLogicalOp == CRunningScript::ANDOR_NONE) { + S.m_bCondResult = false; + } else if (S.m_nLogicalOp >= CRunningScript::ANDS_1 && S.m_nLogicalOp <= CRunningScript::ANDS_8) { // It's an `AND` + S.m_nLogicalOp++; + S.m_bCondResult = true; + } else if (S.m_nLogicalOp >= CRunningScript::ORS_1 && S.m_nLogicalOp <= CRunningScript::ORS_8) { // It's an `OR` + S.m_nLogicalOp++; + S.m_bCondResult = false; + } else { + NOTSA_UNREACHABLE("Unknown LogicalOp: {}", logicalOp); + } + return OR_CONTINUE; +} + +// +// Script loading, stopping +// + +auto TerminateThisScript(CRunningScript& S) { // 0x04E + if (S.m_bIsMission) { + CTheScripts::bAlreadyRunningAMissionScript = false; + } + S.RemoveScriptFromList(&CTheScripts::pActiveScripts); + S.AddScriptToList(&CTheScripts::pIdleScripts); + S.ShutdownThisScript(); + return OR_WAIT; +} + +auto StartNewScript(CRunningScript& S, int32 offset) { // 0x04F + assert(offset >= 0); + /* Weird code that would probably just make the game crash + if (offset < 0) {// This doesn't make sense + offset = COMMAND_START_NEW_SCRIPT; // For Mobile, offset is set to 0 here. WD fix perhaps? + } + */ + + CRunningScript* script = CTheScripts::StartNewScript(&CTheScripts::ScriptSpace[offset]); + S.ReadParametersForNewlyStartedScript(script); + return OR_CONTINUE; +} + +// +// Other misc stuff (todo: move to appropriate categories) +// +void DebugOn() { +#ifndef FINAL + CTheScripts::DbgFlag = true; +#endif +} + +void DebugOff() { +#ifndef FINAL + CTheScripts::DbgFlag = false; +#endif +} + +auto Wait(CRunningScript& S, uint32 duration) { + S.m_nWakeTime = CTimer::GetTimeInMS() + duration; + return OR_WAIT; +} + +auto Nop() { + /* Hello! How are you? */ +} + +void notsa::script::commands::basic::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER, IsGreaterEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR, IsGreaterEqual); + + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR, IsGreater); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR, IsGreater); + + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR, IsEqual); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR, IsEqual); + + REGISTER_COMMAND_HANDLER(COMMAND_SUB_INT_LVAR_FROM_INT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_INT_VAR_FROM_INT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_INT_LVAR_FROM_INT_VAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_VAL_FROM_INT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_VAL_FROM_INT_VAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_INT_VAR_FROM_INT_VAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_VAL_FROM_FLOAT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_VAL_FROM_FLOAT_VAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR, SubInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR, SubInPlace); + + REGISTER_COMMAND_HANDLER(COMMAND_ADD_INT_VAR_TO_INT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_INT_VAR_TO_INT_VAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_INT_LVAR_TO_INT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_VAL_TO_INT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_INT_LVAR_TO_INT_VAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_VAL_TO_INT_VAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_VAL_TO_FLOAT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_VAL_TO_FLOAT_VAR, AddInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR, AddInPlace); + + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_LVAR_BY_INT_VAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_VAR_BY_INT_VAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_VAR_BY_VAL, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_LVAR_BY_VAL, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_LVAR_BY_INT_LVAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_INT_VAR_BY_INT_LVAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_LVAR_BY_VAL, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_VAR_BY_VAL, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR, MultInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR, MultInPlace); + + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_VAR_BY_INT_VAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_LVAR_BY_VAL, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_VAR_BY_INT_LVAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_VAR_BY_VAL, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_LVAR_BY_INT_LVAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_INT_LVAR_BY_INT_VAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_VAR_BY_VAL, DivInPlace); + REGISTER_COMMAND_HANDLER(COMMAND_DIV_FLOAT_LVAR_BY_VAL, DivInPlace); + + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_INT_TO_LVAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_INT_TO_VAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_INT_TO_CONSTANT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_INT_TO_CONSTANT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_INT_TO_VAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_INT_TO_LVAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_VAR_INT_TO_VAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_VAR_FLOAT_TO_VAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT, (AssignTo)); + REGISTER_COMMAND_HANDLER(COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT, (AssignTo)); + + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR, SubTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR, AddTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR, AddTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR, AddTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR, AddTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR, AddTimedStore); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR, AddTimedStore); + + REGISTER_COMMAND_HANDLER(COMMAND_ABS_VAR_INT, AbsStore); + REGISTER_COMMAND_HANDLER(COMMAND_ABS_LVAR_INT, AbsStore); + REGISTER_COMMAND_HANDLER(COMMAND_ABS_VAR_FLOAT, AbsStore); + REGISTER_COMMAND_HANDLER(COMMAND_ABS_LVAR_FLOAT, AbsStore); + + REGISTER_COMMAND_HANDLER(COMMAND_GOTO, GoTo); + REGISTER_COMMAND_HANDLER(COMMAND_GOTO_IF_FALSE, GoToIfFalse); + REGISTER_COMMAND_HANDLER(COMMAND_GOSUB, GoToSub); + REGISTER_COMMAND_HANDLER(COMMAND_RETURN_TRUE, ReturnTrue); + REGISTER_COMMAND_HANDLER(COMMAND_RETURN_FALSE, ReturnFalse); + REGISTER_COMMAND_HANDLER(COMMAND_RETURN, Return); + + REGISTER_COMMAND_HANDLER(COMMAND_ANDOR, AndOr); + + REGISTER_COMMAND_HANDLER(COMMAND_TERMINATE_THIS_SCRIPT, TerminateThisScript); + REGISTER_COMMAND_HANDLER(COMMAND_START_NEW_SCRIPT, StartNewScript); + REGISTER_COMMAND_HANDLER(COMMAND_DEBUG_ON, DebugOn); + REGISTER_COMMAND_HANDLER(COMMAND_DEBUG_OFF, DebugOff); + REGISTER_COMMAND_HANDLER(COMMAND_WAIT, Wait); + REGISTER_COMMAND_HANDLER(COMMAND_NOP, Nop); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GOTO_IF_TRUE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IFNOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ELSE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENDIF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_WHILE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_WHILENOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENDWHILE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_REPEAT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_VAR_INT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_VAR_FLOAT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LVAR_INT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LVAR_FLOAT); +} diff --git a/source/game_sa/Scripts/Commands/Basic.hpp b/source/game_sa/Scripts/Commands/Basic.hpp deleted file mode 100644 index 343150349a..0000000000 --- a/source/game_sa/Scripts/Commands/Basic.hpp +++ /dev/null @@ -1,1244 +0,0 @@ -#pragma once - -#include "CommandParser/Parser.hpp" - -/* -* Basic language feature commands (Comparasions, assingments, etc...) -*/ -/* -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x001 - CollectParameters(1); - m_nWakeTime = ScriptParams[0].uParam + CTimer::GetTimeInMS(); - return OR_WAIT; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x002 - CollectParameters(1); - UpdatePC(ScriptParams[0].iParam); - return OR_CONTINUE; -} - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x004 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - *var = ScriptParams[0]; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x005 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - *var = ScriptParams[0]; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x006 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - *var = ScriptParams[0]; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x007 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - *var = ScriptParams[0]; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x008 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->iParam += ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x009 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam += ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00A - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->iParam += ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00B - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam += ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00C - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->iParam -= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00D - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam -= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00E - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->iParam -= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00F - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam -= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x010 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->iParam *= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x011 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam *= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x012 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->iParam *= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x013 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam *= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x014 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->iParam /= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x015 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam /= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x016 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->iParam /= ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x017 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam /= ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x018 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam > ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x019 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam > ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01A - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].iParam > var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01B - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].iParam > var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->iParam > var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam > var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01E - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam > var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x01F - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->iParam > var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x020 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam > ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x021 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam > ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x022 - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].fParam > var->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x023 - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].fParam > var->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x024 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->fParam > var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x025 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam > var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x026 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam > var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x027 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->fParam > var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x028 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam >= ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x029 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam >= ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02A - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].iParam >= var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02B - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].iParam >= var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->iParam >= var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam >= var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02E - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam >= var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x02F - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->iParam >= var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x030 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam >= ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x031 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam >= ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x032 - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].fParam >= var->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x033 - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].fParam >= var->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x034 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->fParam >= var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x035 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam >= var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x036 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam >= var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x037 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->fParam >= var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x038 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam == ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x039 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam == ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x03A - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->iParam == var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x03B - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam == var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x03C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->iParam == var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x042 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam == ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x043 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->fParam == ScriptParams[0].fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x044 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(var1->fParam == var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x045 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam == var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x046 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(var1->fParam == var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam -= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x065 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam -= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x066 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam -= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x067 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam -= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x068 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam *= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x069 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam *= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06A - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam *= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06B - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam *= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam *= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam *= var2->fParam; - return OR_CONTINUE; -} -*/ -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06E - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam *= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x06F - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam *= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x070 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam /= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x071 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam /= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x072 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam /= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x073 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam /= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x074 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam /= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x075 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam /= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x076 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam /= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x077 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam /= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x078 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam += CTimer::GetTimeStep() * ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x079 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam += CTimer::GetTimeStep() * ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07A - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam += CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07B - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam += CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam += CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam += CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07E - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->fParam -= CTimer::GetTimeStep() * ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x07F - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->fParam -= CTimer::GetTimeStep() * ScriptParams[0].fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x080 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam -= CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x081 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam -= CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x082 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam -= CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x083 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam -= CTimer::GetTimeStep() * var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x084 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x085 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x086 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x087 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x088 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x089 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08A - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08B - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - *var1 = *var2; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam = static_cast(var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam = static_cast(var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08E - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam = static_cast(var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x08F - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam = static_cast(var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x090 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam = static_cast(var2->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x091 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam = static_cast(var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x092 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam = static_cast(var2->fParam); - return OR_CONTINUE; -} - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x093 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam = static_cast(var2->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x094 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - var->iParam = std::abs(var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x095 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - var->iParam = std::abs(var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x096 - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - var->fParam = std::abs(var->fParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x097 - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - var->fParam = std::abs(var->fParam); - return OR_CONTINUE; -} - -/* -void GoToIfFalse(CRunningScript& S, int32 label) { // COMMAND_GOTO_IF_FALSE - if (S.m_bCondResult) { - S.UpdatePC(label); - } -} -REGISTER_COMMAND_HANDLER(COMMAND_GOTO_IF_FALSE, GoToIfFalse); -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0C3 -#ifndef FINAL - CTheScripts::DbgFlag = true; -#endif - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0C4 -#ifndef FINAL - CTheScripts::DbgFlag = false; -#endif - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0C5 - UpdateCompareFlag(true); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0C6 - UpdateCompareFlag(false); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B2 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B3 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B4 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B5 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B6 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B7 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B8 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0B9 | NOTSA - NOTSA_UNREACHABLE(); -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x04E - if (m_bIsMission) - CTheScripts::bAlreadyRunningAMissionScript = false; - RemoveScriptFromList(&CTheScripts::pActiveScripts); - AddScriptToList(&CTheScripts::pIdleScripts); - ShutdownThisScript(); - return OR_WAIT; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x04F - CollectParameters(1); - - int32 offset = ScriptParams[0].iParam; - if (offset < 0) // This doesn't make sense - offset = COMMAND_START_NEW_SCRIPT; // For Mobile, offset is set to 0 here. WD fix perhaps? - - CRunningScript* script = CTheScripts::StartNewScript(&CTheScripts::ScriptSpace[offset]); - ReadParametersForNewlyStartedScript(script); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x050 - CollectParameters(1); - m_apStack[m_nSP++] = m_pCurrentIP; - UpdatePC(ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x051 - m_pCurrentIP = m_apStack[--m_nSP]; - return OR_CONTINUE; -} - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x058 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam += var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x059 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam += var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05A - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam += var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05B - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam += var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05C - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam += var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05D - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam += var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05E - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam += var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x05F - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam += var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x060 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->iParam -= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x061 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_GLOBAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_GLOBAL); - var1->fParam -= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x062 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->iParam -= var2->iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x063 - tScriptParam* var1 = GetPointerToScriptVariable(VAR_LOCAL); - tScriptParam* var2 = GetPointerToScriptVariable(VAR_LOCAL); - var1->fParam -= var2->fParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0D6 - CollectParameters(1); - m_nLogicalOp = ScriptParams[0].iParam; - if (m_nLogicalOp == ANDOR_NONE) - { - m_bCondResult = false; - } - else if (m_nLogicalOp >= ANDS_1 && m_nLogicalOp <= ANDS_8) - { - m_nLogicalOp++; - m_bCondResult = true; - } - else if (m_nLogicalOp >= ORS_1 && m_nLogicalOp <= ORS_8) - { - m_nLogicalOp++; - m_bCondResult = false; - } - return OR_CONTINUE; -} - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam > ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam > ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].iParam > var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].iParam > var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam >= ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - UpdateCompareFlag(var->iParam >= ScriptParams[0].iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - UpdateCompareFlag(ScriptParams[0].iParam >= var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - CollectParameters(1); - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - UpdateCompareFlag(ScriptParams[0].iParam >= var->iParam); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_GLOBAL); - CollectParameters(1); - var->iParam = ScriptParams[0].iParam; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - tScriptParam* var = GetPointerToScriptVariable(VAR_LOCAL); - CollectParameters(1); - var->iParam = ScriptParams[0].iParam; - return OR_CONTINUE; -} diff --git a/source/game_sa/Scripts/Commands/CLEO/AudioStream.hpp b/source/game_sa/Scripts/Commands/CLEO/AudioStream.cpp similarity index 93% rename from source/game_sa/Scripts/Commands/CLEO/AudioStream.hpp rename to source/game_sa/Scripts/Commands/CLEO/AudioStream.cpp index 7ae5561f89..ef3b85aff0 100644 --- a/source/game_sa/Scripts/Commands/CLEO/AudioStream.hpp +++ b/source/game_sa/Scripts/Commands/CLEO/AudioStream.cpp @@ -1,9 +1,12 @@ -#pragma once +#include + +#include "./Commands.hpp" +#include /*! * Various CLEO audio commands */ - +/* template<> OpcodeResult CRunningScript::ProcessCommand() { // 0x0AAC (string audioFileName) CollectParameters(1); @@ -81,3 +84,7 @@ OpcodeResult CRunningScript::ProcessCommand -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AB7 (Car self) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AB8 (Car self) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0ABD (Car self) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0ABE (Car self) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0ABF (Car self, bool state) - CollectParameters(2); - return OR_CONTINUE; -} - diff --git a/source/game_sa/Scripts/Commands/CLEO/Char.hpp b/source/game_sa/Scripts/Commands/CLEO/Char.hpp deleted file mode 100644 index 391f5d029b..0000000000 --- a/source/game_sa/Scripts/Commands/CLEO/Char.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -/*! -* Various utility commands -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AB5 (Char self) - CollectParameters(1); - return OR_CONTINUE; -} - diff --git a/source/game_sa/Scripts/Commands/CLEO/Character.cpp b/source/game_sa/Scripts/Commands/CLEO/Character.cpp new file mode 100644 index 0000000000..c3a077e202 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Character.cpp @@ -0,0 +1,7 @@ +#include + +#include "./Commands.hpp" +#include + +void notsa::script::commands::cleo::character::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Commands.hpp b/source/game_sa/Scripts/Commands/CLEO/Commands.hpp new file mode 100644 index 0000000000..6810bbb06f --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Commands.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace notsa { +namespace script { +namespace commands { +namespace cleo { +namespace audiostream { void RegisterHandlers(); }; +namespace character { void RegisterHandlers(); }; +namespace dynamiclibrary { void RegisterHandlers(); }; +namespace fs { void RegisterHandlers(); }; +namespace game { void RegisterHandlers(); }; +namespace generic { void RegisterHandlers(); }; +namespace memory { void RegisterHandlers(); }; +namespace pad { void RegisterHandlers(); }; +namespace script { void RegisterHandlers(); }; +namespace vehicle { void RegisterHandlers(); }; +namespace world { void RegisterHandlers(); }; +}; // namespace cleo +}; // namespace notsa +}; // namespace script +}; // namespace commands diff --git a/source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.hpp b/source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.cpp similarity index 78% rename from source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.hpp rename to source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.cpp index bf68115675..beda92554b 100644 --- a/source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.hpp +++ b/source/game_sa/Scripts/Commands/CLEO/DynamicLibrary.cpp @@ -1,9 +1,13 @@ -#pragma once +#include + +#include "./Commands.hpp" +#include /*! * Various CLEO dynlib commands */ +/* template<> OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA2 (string libraryFileName) CollectParameters(1); @@ -21,4 +25,7 @@ OpcodeResult CRunningScript::ProcessCommand + +#include "./Commands.hpp" +#include + +void notsa::script::commands::cleo::extensions::cleoplus::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Extensions/Clipboard.cpp b/source/game_sa/Scripts/Commands/CLEO/Extensions/Clipboard.cpp new file mode 100644 index 0000000000..74c60d33fd --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Extensions/Clipboard.cpp @@ -0,0 +1,7 @@ +#include + +#include "./Commands.hpp" +#include + +void notsa::script::commands::cleo::extensions::clipboard::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Extensions/Commands.hpp b/source/game_sa/Scripts/Commands/CLEO/Extensions/Commands.hpp new file mode 100644 index 0000000000..9651c18250 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Extensions/Commands.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace notsa { +namespace script { +namespace commands { +namespace cleo { +namespace extensions { +namespace cleoplus { void RegisterHandlers(); }; +namespace clipboard { void RegisterHandlers(); }; +namespace fs { void RegisterHandlers(); }; +namespace imgui { void RegisterHandlers(); }; +namespace intoperations { void RegisterHandlers(); }; +}; // namespace extensions +}; // namespace notsa +}; // namespace script +}; // namespace commands +}; // namespace cleo diff --git a/source/game_sa/Scripts/Commands/CLEO/Extensions/Fs.cpp b/source/game_sa/Scripts/Commands/CLEO/Extensions/Fs.cpp new file mode 100644 index 0000000000..0c4d8fb769 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Extensions/Fs.cpp @@ -0,0 +1,7 @@ +#include + +#include "./Commands.hpp" +#include + +void notsa::script::commands::cleo::extensions::fs::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Extensions/Imgui.cpp b/source/game_sa/Scripts/Commands/CLEO/Extensions/Imgui.cpp new file mode 100644 index 0000000000..3682f70d4c --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Extensions/Imgui.cpp @@ -0,0 +1,7 @@ +#include + +#include "./Commands.hpp" +#include + +void notsa::script::commands::cleo::extensions::imgui::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Extensions/IntOperations.cpp b/source/game_sa/Scripts/Commands/CLEO/Extensions/IntOperations.cpp new file mode 100644 index 0000000000..14b8f6e126 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Extensions/IntOperations.cpp @@ -0,0 +1,72 @@ +#include + +#include "./Commands.hpp" +#include +#include "CommandParser/Parser.hpp" +/*! + * Various int operations (bitwise and modulo) + */ + +auto BitAnd(uint32 a, uint32 b) { + return a & b; +} +auto BitwiseAndThingWithThing(uint32& a, uint32 b) { + a &= b; +} + +auto BitOr(uint32 a, uint32 b) { + return a | b; +} +auto BitwiseOrThingWithThing(uint32& a, uint32 b) { + a |= b; +} + +auto BitXor(uint32 a, uint32 b) { + return a ^ b; +} +auto BitwiseXorThingWithThing(uint32& a, uint32 b) { + a ^= b; +} + +auto BitNot(uint32 a) { + return ~a; +} + +auto BitShl(uint32 a, uint32 b) { + return a << b; +} +auto BitwiseShlThingByThing(uint32& a, uint32 b) { + a <<= b; +} + +auto BitShr(uint32 a, uint32 b) { + return a >> b; +} +auto BitwiseShrThingByThing(uint32& a, uint32 b) { + a >>= b; +} + +auto BitMod(uint32 a, uint32 b) { + return a % b; +} +auto BitwiseModThingByThing(uint32& a, uint32 b) { + a %= b; +} + +void notsa::script::commands::cleo::extensions::intoperations::RegisterHandlers() { + /* + REGISTER_COMMAND_HANDLER(COMMAND_BIT_AND, BitAnd); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_AND_THING_WITH_THING, BitwiseAndThingWithThing); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_OR, BitOr); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_OR_THING_WITH_THING, BitwiseOrThingWithThing); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_XOR, BitXor); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_XOR_THING_WITH_THING, BitwiseXorThingWithThing); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_NOT, BitNot); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_SHL, BitShl); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_SHL_THING_BY_THING, BitwiseShlThingByThing); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_SHR, BitShr); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_SHR_THING_BY_THING, BitwiseShrThingByThing); + REGISTER_COMMAND_HANDLER(COMMAND_BIT_MOD, BitMod); + REGISTER_COMMAND_HANDLER(COMMAND_BITWISE_MOD_THING_BY_THING, BitwiseModThingByThing); + */ +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Fs.hpp b/source/game_sa/Scripts/Commands/CLEO/Fs.cpp similarity index 88% rename from source/game_sa/Scripts/Commands/CLEO/Fs.hpp rename to source/game_sa/Scripts/Commands/CLEO/Fs.cpp index 385c0050ae..9369ee01b6 100644 --- a/source/game_sa/Scripts/Commands/CLEO/Fs.hpp +++ b/source/game_sa/Scripts/Commands/CLEO/Fs.cpp @@ -1,11 +1,13 @@ -#pragma once +#include + +#include "./Commands.hpp" +#include /*! * Various CLEO filesystem commands */ - - +/* template<> OpcodeResult CRunningScript::ProcessCommand() { // 0x0A99 (any path) CollectParameters(1); @@ -47,3 +49,7 @@ OpcodeResult CRunningScript::ProcessCommand() { // 0x0A CollectParameters(1); return OR_CONTINUE; } +*/ + +void notsa::script::commands::cleo::fs::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Game.cpp b/source/game_sa/Scripts/Commands/CLEO/Game.cpp new file mode 100644 index 0000000000..52afac19f4 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Game.cpp @@ -0,0 +1,17 @@ +#include + +#include "./Commands.hpp" +#include +#include "CommandParser/Parser.hpp" + +/*! +* Various CLEO game commands +*/ + +bool IsGameVersionOriginal() { + return false; +} + +void notsa::script::commands::cleo::game::RegisterHandlers() { + //REGISTER_COMMAND_HANDLER(COMMAND_IS_GAME_VERSION_ORIGINAL, IsGameVersionOriginal); +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Game.hpp b/source/game_sa/Scripts/Commands/CLEO/Game.hpp deleted file mode 100644 index 1b09944f68..0000000000 --- a/source/game_sa/Scripts/Commands/CLEO/Game.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -/*! -* Various CLEO game commands -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA9 - CollectParameters(0); - return OR_CONTINUE; -} - diff --git a/source/game_sa/Scripts/Commands/CLEO/Generic.hpp b/source/game_sa/Scripts/Commands/CLEO/Generic.cpp similarity index 84% rename from source/game_sa/Scripts/Commands/CLEO/Generic.hpp rename to source/game_sa/Scripts/Commands/CLEO/Generic.cpp index cba92a6efa..6da1dc72e8 100644 --- a/source/game_sa/Scripts/Commands/CLEO/Generic.hpp +++ b/source/game_sa/Scripts/Commands/CLEO/Generic.cpp @@ -1,10 +1,13 @@ -#pragma once +#include +#include "./Commands.hpp" +#include +#include "CommandParser/Parser.hpp" /*! * Generic CLEO commands */ - +/* template<> OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA0 (label) CollectParameters(1); @@ -40,3 +43,7 @@ OpcodeResult CRunningScript::ProcessCommand() { // CollectParameters(1); return OR_CONTINUE; } +*/ + +void notsa::script::commands::cleo::generic::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Memory.cpp b/source/game_sa/Scripts/Commands/CLEO/Memory.cpp new file mode 100644 index 0000000000..d8be5294d6 --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Memory.cpp @@ -0,0 +1,94 @@ +#include + +#include "./Commands.hpp" +#include + +#include "app_debug.h" +#include "CommandParser/Parser.hpp" +/*! +* Various CLEO memory commands +*/ + +void WriteMemory(uint32 address, uint32 size, uint32 value, bool virtualProtect) { + UNUSED(virtualProtect); + + DEV_LOG("WriteMemory(addr={}, size={}, value={} ,virtual_protect={})", address, size, value, virtualProtect); + memcpy(reinterpret_cast(address), &value, size); +} + +uint32 ReadMemory(uint32 address, uint32 size, bool virtualProtect) { + UNUSED(virtualProtect); + + DEV_LOG("ReadMemory(addr={}, size={}, virtual_protect={})", address, size, virtualProtect); + if (size != 4 && size != 2) { + size = 1; // just to be sure not to write anything stupid. + } + uint32 ret; + memcpy(&ret, reinterpret_cast(address), size); + return ret; +} + +auto GetPedPointer(CPed& ped) { + return &ped; +} + +auto GetVehiclePointer(CVehicle& vehicle) { + return &vehicle; +} + +auto GetObjectPointer(CObject& object) { + return &object; +} + +auto GetThisScriptStruct(CRunningScript* S) { + return S; +} + +/* +template<> +OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA5 (int32 address, int32 numParams, int32 pop, arguments funcParams) + // perhaps this can't be ported to new-form? + CollectParameters(4); + return OR_CONTINUE; +} + +template<> +OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA6 (int32 address, int32 struct, int32 numParams, int32 pop, arguments funcParams) + // perhaps this can't be ported to new-form? + CollectParameters(5); + return OR_CONTINUE; +} + +template<> +OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA7 (int32 address, int32 numParams, int32 pop, arguments funcParams) + // perhaps this can't be ported to new-form? + CollectParameters(4); + return OR_CONTINUE; +} + +template<> +OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA8 (int32 address, int32 struct, int32 numParams, int32 pop, arguments funcParams) + // perhaps this can't be ported to new-form? + CollectParameters(5); + return OR_CONTINUE; +} +*/ + +const char* GetScriptStructNamed(CRunningScript* S) { + NOTSA_UNREACHABLE("Not implemented!"); + S->m_bCondResult = false; + + return nullptr; +} + +void notsa::script::commands::cleo::memory::RegisterHandlers() { + /* + REGISTER_COMMAND_HANDLER(COMMAND_WRITE_MEMORY, WriteMemory); + REGISTER_COMMAND_HANDLER(COMMAND_READ_MEMORY, ReadMemory); + REGISTER_COMMAND_HANDLER(COMMAND_GET_PED_POINTER, GetPedPointer); + REGISTER_COMMAND_HANDLER(COMMAND_GET_VEHICLE_POINTER, GetVehiclePointer); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_POINTER, GetObjectPointer); + REGISTER_COMMAND_HANDLER(COMMAND_GET_THIS_SCRIPT_STRUCT, GetThisScriptStruct); + REGISTER_COMMAND_HANDLER(COMMAND_GET_THIS_SCRIPT_STRUCT, GetScriptStructNamed); + */ +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Memory.hpp b/source/game_sa/Scripts/Commands/CLEO/Memory.hpp deleted file mode 100644 index d795bd7b04..0000000000 --- a/source/game_sa/Scripts/Commands/CLEO/Memory.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -/*! -* Various CLEO memory commands -*/ - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A8C (int32 address, int32 size, any value, bool vp) - CollectParameters(4); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A8D (int32 address, int32 size, bool vp) - CollectParameters(3); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A96 (Char char) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A97 (Car vehicle) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A98 (Object object) - CollectParameters(1); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0A9F - CollectParameters(0); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA5 (int32 address, int32 numParams, int32 pop, arguments funcParams) - CollectParameters(4); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA6 (int32 address, int32 struct, int32 numParams, int32 pop, arguments funcParams) - CollectParameters(5); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA7 (int32 address, int32 numParams, int32 pop, arguments funcParams) - CollectParameters(4); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AA8 (int32 address, int32 struct, int32 numParams, int32 pop, arguments funcParams) - CollectParameters(5); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AAA (string scriptName) - CollectParameters(1); - return OR_CONTINUE; -} - diff --git a/source/game_sa/Scripts/Commands/CLEO/Pad.cpp b/source/game_sa/Scripts/Commands/CLEO/Pad.cpp new file mode 100644 index 0000000000..26f6940ddf --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/Pad.cpp @@ -0,0 +1,17 @@ +#include + +#include "./Commands.hpp" +#include +#include "CommandParser/Parser.hpp" + +/*! +* Various player pad commands +*/ + +bool IsKeyPressed(int32 virtualKey) { + return GetKeyState(virtualKey) & 0x8000; +} + +void notsa::script::commands::cleo::pad::RegisterHandlers() { + //REGISTER_COMMAND_HANDLER(COMMAND_IS_KEY_PRESSED, IsKeyPressed); +} diff --git a/source/game_sa/Scripts/Commands/CLEO/Script.hpp b/source/game_sa/Scripts/Commands/CLEO/Script.cpp similarity index 81% rename from source/game_sa/Scripts/Commands/CLEO/Script.hpp rename to source/game_sa/Scripts/Commands/CLEO/Script.cpp index 1644758a7d..fb5338be48 100644 --- a/source/game_sa/Scripts/Commands/CLEO/Script.hpp +++ b/source/game_sa/Scripts/Commands/CLEO/Script.cpp @@ -1,9 +1,15 @@ -#pragma once +#include + +#include "./Commands.hpp" +#include + +#include "TheScripts.h" +#include "CommandParser/Parser.hpp" /*! * Various CLEO script related commands */ - +/* template<> OpcodeResult CRunningScript::ProcessCommand() { // 0x0A92 (string scriptFileName, arguments) CollectParameters(2); @@ -33,4 +39,7 @@ OpcodeResult CRunningScript::ProcessCommand + +#include "./Commands.hpp" +#include + +#include "Vehicle.h" +#include "CommandParser/Parser.hpp" + +/*! +* Various CLEO car commands +*/ + +uint8 GetCarNumberOfGears(CVehicle& vehicle) { + return vehicle.m_pHandlingData->m_transmissionData.m_nNumberOfGears; +} + +uint8 GetCarCurrentGear(CVehicle& vehicle) { + return vehicle.m_nCurrentGear; +} + +bool IsCarSirenOn(CVehicle& vehicle) { + return vehicle.vehicleFlags.bSirenOrAlarm; +} + +bool IsCarEngineOn(CVehicle& vehicle) { + return vehicle.vehicleFlags.bEngineOn; +} + +void SetCarEngineOn(CVehicle& vehicle, bool state) { + vehicle.vehicleFlags.bEngineOn = state; +} + + +void notsa::script::commands::cleo::vehicle::RegisterHandlers() { +#if 0 + REGISTER_COMMAND_HANDLER(COMMAND_GET_CAR_NUMBER_OF_GEARS, GetCarNumberOfGears); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CAR_CURRENT_GEAR, GetCarNumberOfGears); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CAR_SIREN_ON, IsCarSirenOn); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CAR_ENGINE_ON, IsCarEngineOn); + REGISTER_COMMAND_HANDLER(COMMAND_CLEO_SET_CAR_ENGINE_ON, SetCarEngineOn); +#endif +} diff --git a/source/game_sa/Scripts/Commands/CLEO/World.cpp b/source/game_sa/Scripts/Commands/CLEO/World.cpp new file mode 100644 index 0000000000..52a4ab02ca --- /dev/null +++ b/source/game_sa/Scripts/Commands/CLEO/World.cpp @@ -0,0 +1,33 @@ +#include + +#include "./Commands.hpp" +#include + +#include "Radar.h" +#include "MenuManager.h" +#include "CommandParser/Parser.hpp" + +/*! +* Various CLEO world commands +*/ + +CVector GetCheckpointBlipCoords(CRunningScript* S) { + S->m_bCondResult = false; + + const auto blipIdx = CRadar::GetActualBlipArrayIndex(FrontEndMenuManager.m_nTargetBlipIndex); + if (blipIdx == -1) + return {}; + + if (const auto& trace = CRadar::ms_RadarTrace[blipIdx]; trace.m_nBlipDisplayFlag != BLIP_DISPLAY_NEITHER) { + S->m_bCondResult = true; + return trace.m_vPosition; + } + + return {}; +} + +void notsa::script::commands::cleo::world::RegisterHandlers() { + /* + REGISTER_COMMAND_HANDLER(COMMAND_GET_TARGET_BLIP_COORDS, GetCheckpointBlipCoords); + */ +} diff --git a/source/game_sa/Scripts/Commands/CLEO/World.hpp b/source/game_sa/Scripts/Commands/CLEO/World.hpp deleted file mode 100644 index 8fd6b79152..0000000000 --- a/source/game_sa/Scripts/Commands/CLEO/World.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -/*! -* Various CLEO world commands -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AB6 - CollectParameters(0); - return OR_CONTINUE; -} - diff --git a/source/game_sa/Scripts/Commands/Camera.cpp b/source/game_sa/Scripts/Commands/Camera.cpp new file mode 100644 index 0000000000..f5eac1891e --- /dev/null +++ b/source/game_sa/Scripts/Commands/Camera.cpp @@ -0,0 +1,45 @@ +#include + +#include "./Commands.hpp" +#include + +#include "CommandParser/Parser.hpp" +using namespace notsa::script; +/*! +* Various camera commands +*/ + +bool IsPointOnScreen(CVector pos, float radius) { + if (pos.z <= MAP_Z_LOW_LIMIT) { + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + } + return TheCamera.IsSphereVisible(&pos, radius); +} + +void ShakeCam(float strength) { + CamShakeNoPos(&TheCamera, strength / 1000.0f); +} + +void AttachCameraToVehicleLookAtVehicle(CVehicle& attachTo, CVector offset, CVehicle& lookAt, float tilt, eSwitchType switchType) { + CVector zero{}; + TheCamera.TakeControlAttachToEntity( + &lookAt, + &attachTo, + &offset, + &zero, + tilt, + switchType, + 1 + ); +} + +void DoCameraBump(float horizontal, float vetical) { + CCamera::GetActiveCamera().DoCamBump(horizontal, vetical); +} + +void notsa::script::commands::camera::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_IS_POINT_ON_SCREEN, IsPointOnScreen); + REGISTER_COMMAND_HANDLER(COMMAND_SHAKE_CAM, ShakeCam); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CAMERA_TO_VEHICLE_LOOK_AT_VEHICLE, AttachCameraToVehicleLookAtVehicle); + REGISTER_COMMAND_HANDLER(COMMAND_DO_CAMERA_BUMP, DoCameraBump); +} diff --git a/source/game_sa/Scripts/Commands/Camera.hpp b/source/game_sa/Scripts/Commands/Camera.hpp deleted file mode 100644 index cf3e6682e5..0000000000 --- a/source/game_sa/Scripts/Commands/Camera.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "CommandParser/Parser.hpp" - -/*! -* Various camera commands -*/ - -notsa::script::CompareFlagUpdate IsPointOnScreen(CVector pos, float radius) { - if (pos.z <= MAP_Z_LOW_LIMIT) { - pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); - } - return { TheCamera.IsSphereVisible(&pos, radius) }; -} -REGISTER_COMMAND_HANDLER(COMMAND_IS_POINT_ON_SCREEN, IsPointOnScreen); - -void ShakeCam(float strength) { - CamShakeNoPos(&TheCamera, strength / 1000.0f); -} -REGISTER_COMMAND_HANDLER(COMMAND_SHAKE_CAM, ShakeCam); - -void AttachCameraToVehicleLookAtVehicle(CVehicle& attachTo, CVector offset, CVehicle& lookAt, float tilt, eSwitchType switchType) { - CVector zero{}; - TheCamera.TakeControlAttachToEntity( - &lookAt, - &attachTo, - &offset, - &zero, - tilt, - switchType, - 1 - ); -} -REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CAMERA_TO_VEHICLE_LOOK_AT_VEHICLE, AttachCameraToVehicleLookAtVehicle); diff --git a/source/game_sa/Scripts/Commands/Car.hpp b/source/game_sa/Scripts/Commands/Car.hpp deleted file mode 100644 index 619ac1ae7c..0000000000 --- a/source/game_sa/Scripts/Commands/Car.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "TheCarGenerators.h" - -/*! -* Various utility commands -*/ - -void SetCarProofs(CVehicle& veh, bool bullet, bool fire, bool explosion, bool collision, bool melee) { - auto& flags = veh.physicalFlags; - flags.bBulletProof = bullet; - flags.bFireProof = fire; - flags.bExplosionProof = explosion; - flags.bCollisionProof = collision; - flags.bMeleeProof = melee; -} -REGISTER_COMMAND_HANDLER(COMMAND_SET_CAR_PROOFS, SetCarProofs); - -void SwitchCarGenerator(int32 generatorId, int32 count) { - const auto generator = CTheCarGenerators::Get(generatorId); - if (count) { - generator->SwitchOn(); - if (count <= 100) { - generator->m_nGenerateCount = count; - } - } else { - generator->SwitchOff(); - } -} -REGISTER_COMMAND_HANDLER(COMMAND_SWITCH_CAR_GENERATOR, SwitchCarGenerator); - -float GetCarSpeed(CVehicle& veh) { - return veh.m_vecMoveSpeed.Magnitude() * 50.f; -} -REGISTER_COMMAND_HANDLER(COMMAND_GET_CAR_SPEED, GetCarSpeed); - -void SetCarDrivingStyle(CVehicle& veh, eCarDrivingStyle style) { - veh.m_autoPilot.m_nCarDrivingStyle = style; -} -REGISTER_COMMAND_HANDLER(COMMAND_SET_CAR_DRIVING_STYLE, SetCarDrivingStyle); diff --git a/source/game_sa/Scripts/Commands/Char.hpp b/source/game_sa/Scripts/Commands/Char.hpp deleted file mode 100644 index 80ea0c413d..0000000000 --- a/source/game_sa/Scripts/Commands/Char.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "CommandParser/Parser.hpp" - -/*! -* Various utility commands -*/ - -auto IsCharInArea2D(CRunningScript& S, CPed& ped, CVector2D a, CVector2D b, bool drawSphere) -> notsa::script::CompareFlagUpdate { - const auto Check = [&](const auto& e) { return e.IsWithinArea(a.x, a.y, b.x, b.y); }; - - if (drawSphere) { - CTheScripts::HighlightImportantArea(reinterpret_cast(&S) + reinterpret_cast(S.m_pCurrentIP), a.x, a.y, b.x, b.y, -100.f); - } - - return { ped.IsInVehicle() ? Check(*ped.m_pVehicle) : Check(ped) }; -} -REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_AREA_2D, IsCharInArea2D); - -void SetCharProofs(CPed& ped, bool bullet, bool fire, bool explosion, bool collision, bool melee) { - auto& flags = ped.physicalFlags; - flags.bBulletProof = bullet; - flags.bFireProof = fire; - flags.bExplosionProof = explosion; - flags.bCollisionProof = collision; - flags.bMeleeProof = melee; -} -REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_PROOFS, SetCharProofs); - diff --git a/source/game_sa/Scripts/Commands/Character.cpp b/source/game_sa/Scripts/Commands/Character.cpp new file mode 100644 index 0000000000..75ce2ac496 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Character.cpp @@ -0,0 +1,1870 @@ +#include + +#include "./Commands.hpp" +#include +#include +#include "Utility.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace notsa::script; +/*! +* Various character (ped) commands +*/ + +template +void HandleEntityMissionCleanup(CRunningScript& S, T& entity) { + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(entity); + } +} + +// +// Flags getters/setters +// + +void SetCharProofs(CPed& ped, bool bullet, bool fire, bool explosion, bool collision, bool melee) { + auto& flags = ped.physicalFlags; + flags.bBulletProof = bullet; + flags.bFireProof = fire; + flags.bExplosionProof = explosion; + flags.bCollisionProof = collision; + flags.bMeleeProof = melee; +} + +auto SetCharDropsWeaponsWhenDead(CPed& ped, bool dropsWepsWhenDead) { + ped.bDoesntDropWeaponsWhenDead = !dropsWepsWhenDead; +} + +auto SetCharNeverLeavesGroup(CPed& ped, bool bNeverLeavesGroup) { + ped.bNeverLeavesGroup = bNeverLeavesGroup; +} + +// SET_CHAR_BLEEDING +auto SetCharBleeding(CPed& ped, bool isBleeding) { + ped.bPedIsBleeding = isBleeding; +} + +// SET_CHAR_CANT_BE_DRAGGED_OUT +auto SetCharCantBeDraggedOut(CPed& ped, bool bDontDrag) { + ped.bDontDragMeOutCar = bDontDrag; +} + +// SET_CHAR_IS_CHRIS_CRIMINAL +auto SetCharIsChrisCriminal(CPed& ped, bool value) { + ped.bChrisCriminal = value; +} + +// SET_CHAR_SUFFERS_CRITICAL_HITS +auto SetCharSuffersCriticalHits(CPed& ped, bool value) { + ped.bNoCriticalHits = !value; +} + +// FREEZE_CHAR_POSITION +auto FreezeCharPosition(CPed& ped, bool freeze) { + ped.physicalFlags.bDontApplySpeed = freeze; +} + +// SET_CHAR_DROWNS_IN_WATER +auto SetCharDrownsInWater(CPed& ped, bool bDrownsInWater) { + ped.bDrownsInWater = bDrownsInWater; +} + +// SET_CHAR_STAY_IN_CAR_WHEN_JACKED +auto SetCharStayInCarWhenJacked(CPed& ped, bool bStayInCarOnJack) { + ped.bStayInCarOnJack = bStayInCarOnJack; +} + +// SET_CHAR_CAN_BE_SHOT_IN_VEHICLE +auto SetCharCanBeShotInVehicle(CPed& ped, bool bCanBeShotInVehicle) { + ped.bCanBeShotInVehicle = bCanBeShotInVehicle; +} + +// SET_CHAR_NEVER_TARGETTED +auto SetCharNeverTargetted(CPed& ped, bool bNeverEverTargetThisPed) { + ped.bNeverEverTargetThisPed = bNeverEverTargetThisPed; +} + +// SET_CHAR_COLLISION +auto SetCharCollision(CPed& ped, bool bUsesCollision) { + ped.m_bUsesCollision = bUsesCollision; +} + +// IS_CHAR_SHOOTING +auto IsCharShooting(CPed& ped) { + return ped.bFiringWeapon; +} + +// IS_CHAR_WAITING_FOR_WORLD_COLLISION +auto IsCharWaitingForWorldCollision(CPed& ped) { + return ped.m_bIsStaticWaitingForCollision; +} + +// IS_CHAR_DUCKING +auto IsCharDucking(CPed& ped) { + return ped.bIsDucking; +} + +auto GetCharVelocity(CPed& ped) { + return ped.GetMoveSpeed(); +} + +auto SetCharVelocity(CPed& ped, CVector velocity) { + ped.SetVelocity(velocity / 50.f); +} + +auto SetCharRotation(CPed& ped, CVector angles) { + ped.SetOrientation(angles * DegreesToRadians(1.0f)); // make euler angles from degrees + CWorld::Add(&ped); +} + +auto SetCharAllowedToDuck(CPed& ped, CVector rotdeg) { + CWorld::Remove(&ped); + ped.SetOrientation(rotdeg * DegreesToRadians(1.f)); // degrees => radians + CWorld::Add(&ped); +} + +auto SetCharAreaVisible(CPed& ped, eAreaCodes area) { + ped.m_nAreaCode = area; + if (area != AREA_CODE_NORMAL_WORLD) { + return; + } + ped.m_pEnex = nullptr; + if (!ped.IsPlayer()) { + return; + } + CEntryExitManager::ms_entryExitStackPosn = 0; + CTimeCycle::StopExtraColour(0); +} + +auto AttachFxSystemToCharBone(tScriptEffectSystem& fx, CPed& ped, ePedBones bone) { + fx.m_pFxSystem->AttachToBone(&ped, bone); +} + +auto GetDeadCharCoordinates(CPed& ped) { + if (ped.IsInVehicle()) { + return ped.m_pVehicle->GetPosition(); + } else { + return ped.GetBonePosition(BONE_PELVIS); + } +} + +auto GetCharCoordinates(CPed& ped) { + if (ped.IsInVehicle()) { + return ped.m_pVehicle->GetPosition(); + } else { + return ped.GetPosition(); + } +} + +auto SetCharCoordinates(CPed& ped, CVector coords) { + CRunningScript::SetCharCoordinates(ped, coords, true, true); +} + +auto IsCharInArea2D(CRunningScript& S, CPed& ped, CVector2D a, CVector2D b, bool highlightArea) { + if (highlightArea) { + S.HighlightImportantArea(a, b); + } + + const auto Check = [&](const auto& e) { return e.IsWithinArea(a.x, a.y, b.x, b.y); }; + return ped.IsInVehicle() + ? Check(*ped.m_pVehicle) + : Check(ped); +} + +auto IsCharInArea3D(CRunningScript& S, CPed& ped, CVector a, CVector b, bool highlightArea) { + if (highlightArea) { + S.HighlightImportantArea(a, b); + } + + const auto Check = [&](const auto& e) { return e.IsWithinArea(a.x, a.y, a.z, b.x, b.y, b.z); }; + return ped.IsInVehicle() + ? Check(*ped.m_pVehicle) + : Check(ped); +} + +auto StoreCarCharIsIn(CRunningScript& S, CPed& ped) { // 0x469481 + const auto veh = ped.GetVehicleIfInOne(); + + if (GetVehiclePool()->GetRef(veh) != CTheScripts::StoreVehicleIndex && S.m_bUseMissionCleanup) { + // Unstore previous (If it still exists) + if (const auto stored = GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex)) { + CCarCtrl::RemoveFromInterestingVehicleList(stored); + if (stored->IsMissionVehicle() && CTheScripts::StoreVehicleWasRandom) { + stored->SetVehicleCreatedBy(RANDOM_VEHICLE); + stored->vehicleFlags.bIsLocked = false; + CTheScripts::MissionCleanUp.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, MISSION_CLEANUP_ENTITY_TYPE_VEHICLE); + } + } + + // Now store this vehicle + CTheScripts::StoreVehicleIndex = GetVehiclePool()->GetRef(veh); + switch (veh->GetCreatedBy()) { + case RANDOM_VEHICLE: + case PARKED_VEHICLE: { + veh->SetVehicleCreatedBy(MISSION_VEHICLE); + CTheScripts::StoreVehicleWasRandom = true; + CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, MISSION_CLEANUP_ENTITY_TYPE_VEHICLE); + break; + } + case MISSION_VEHICLE: + case PERMANENT_VEHICLE: { + CTheScripts::StoreVehicleWasRandom = false; + break; + } + } + } + + return CTheScripts::StoreVehicleIndex; +} + +auto IsCharInCar(CPed& ped, CVehicle& veh) { + return ped.IsInVehicle() && ped.m_pVehicle == &veh; +} + +auto IsCharInModel(CPed& ped, eModelID model) { + return ped.IsInVehicle() && ped.m_pVehicle->m_nModelIndex == model; +} + +auto IsCharInAnyCar(CPed& ped) { + return ped.IsInVehicle(); +} + +// +// Locate[Stopped]Character(Any Means/On Foot/In Car) +// + +//! Does the usual checks +bool DoLocateCharChecks(CPed& ped, bool mustBeInCar, bool mustBeOnFoot, bool mustBeStopped) { + if (mustBeInCar && !ped.IsInVehicle()) { + return false; + } + if (mustBeOnFoot && ped.IsInVehicle()) { + return false; + } + if (mustBeStopped && !CTheScripts::IsPedStopped(&ped)) { + return false; + } + return true; +} + +//! Check is char within 3D area (with some restrictions) +bool LocateChar3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot, bool mustBeStopped) { + const CBox bb{ pos - radius, pos + radius }; + if (highlightArea) { // Highlight area every time + S.HighlightImportantArea(bb.m_vecMin, bb.m_vecMax); + } + if (!DoLocateCharChecks(ped, mustBeInCar, mustBeOnFoot, mustBeStopped)) { + return false; + } + return bb.IsPointInside(GetCharCoordinates(ped)); +} + +bool LocateCharEntity3D(CRunningScript& S, CPed& ped, CEntity& entity, CVector radius, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot) { + return LocateChar3D(S, ped, entity.GetPosition(), radius, highlightArea, mustBeInCar, mustBeOnFoot, false); +} + +bool LocateChar2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot, bool mustBeStopped) { + const CRect rect{ pos - radius, pos + radius }; + if (highlightArea) { // Highlight area every time + S.HighlightImportantArea(rect.GetTopLeft(), rect.GetBottomRight()); + } + if (!DoLocateCharChecks(ped, mustBeInCar, mustBeOnFoot, mustBeStopped)) { + return false; + } + if (CTheScripts::DbgFlag) { + CTheScripts::DrawDebugSquare(rect); + } + return rect.IsPointInside(GetCharCoordinates(ped)); +} + +bool LocateCharEntity2D(CRunningScript& S, CPed& ped, CEntity& entity, CVector2D radius, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot) { + return LocateChar2D(S, ped, entity.GetPosition(), radius, highlightArea, mustBeInCar, mustBeOnFoot, false); +} + +// +// Char, Pos +// + +// => 2D + +auto LocateCharAnyMeans2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, false, false, false); +} + +auto LocateCharOnFoot2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, false, true, false); +} + +auto LocateCharInCar2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, true, false, false); +} + +auto LocateStoppedCharAnyMeans2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, false, false, true); +} + +auto LocateStoppedCharOnFoot2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, false, true, true); +} + +auto LocateStoppedCharInCar2D(CRunningScript& S, CPed& ped, CVector2D pos, CVector2D radius, bool highlightArea) { + return LocateChar2D(S, ped, pos, radius, highlightArea, true, false, true); +} + +// => 3D + +auto LocateCharAnyMeans3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, false, false, false); +} + +auto LocateCharOnFoot3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, false, true, false); +} + +auto LocateCharInCar3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, true, false, false); +} + +auto LocateStoppedCharAnyMeans3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, false, false, true); +} + +auto LocateStoppedCharOnFoot3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, false, true, true); +} + +auto LocateStoppedCharInCar3D(CRunningScript& S, CPed& ped, CVector pos, CVector radius, bool highlightArea) { + return LocateChar3D(S, ped, pos, radius, highlightArea, true, false, true); +} + +// +// Char, Char +// + +// => 2D + +auto LocateCharAnyMeansChar2D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped1, ped2, radius, highlightArea, false, false); +} + +auto LocateCharOnFootChar2D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped1, ped2, radius, highlightArea, false, true); +} + +auto LocateCharInCarChar2D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped1, ped2, radius, highlightArea, true, false); +} + +// => 3D + +auto LocateCharAnyMeansChar3D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped1, ped2, radius, highlightArea, false, false); +} + +auto LocateCharOnFootChar3D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped1, ped2, radius, highlightArea, false, true); +} + +auto LocateCharInCarChar3D(CRunningScript& S, CPed& ped1, CPed& ped2, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped1, ped2, radius, highlightArea, true, false); +} + +// +// Char, Car +// + +// => 2D + +auto LocateCharAnyMeansCar2D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, veh, radius, highlightArea, false, false); +} + +auto LocateCharOnFootCar2D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, veh, radius, highlightArea, false, true); +} + +auto LocateCharInCarCar2D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, veh, radius, highlightArea, true, false); +} + +// => 3D + +auto LocateCharAnyMeansCar3D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, veh, radius, highlightArea, false, false); +} + +auto LocateCharOnFootCar3D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, veh, radius, highlightArea, false, true); +} + +auto LocateCharInCarCar3D(CRunningScript& S, CPed& ped, CVehicle& veh, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, veh, radius, highlightArea, true, false); +} + +// +// Char, Obj +// + +// => 2D + +auto LocateCharAnyMeansObject2D(CRunningScript& S, CPed& ped, CObject& obj, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, obj, radius, highlightArea, false, false); +} + +auto LocateCharOnFootObject2D(CRunningScript& S, CPed& ped, CVehicle& obj, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, obj, radius, highlightArea, false, true); +} + +auto LocateCharInCarObject2D(CRunningScript& S, CPed& ped, CVehicle& obj, CVector2D radius, bool highlightArea) { + return LocateCharEntity2D(S, ped, obj, radius, highlightArea, true, false); +} + +// => 3D + +auto LocateCharAnyMeansObject3D(CRunningScript& S, CPed& ped, CObject& obj, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, obj, radius, highlightArea, false, false); +} + +auto LocateCharOnFootObject3D(CRunningScript& S, CPed& ped, CObject& obj, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, obj, radius, highlightArea, false, true); +} + +auto LocateCharInCarObject3D(CRunningScript& S, CPed& ped, CObject& obj, CVector radius, bool highlightArea) { + return LocateCharEntity3D(S, ped, obj, radius, highlightArea, true, false); +} + +// +// Char, Angled Area +// + +// => 2D + +bool IsPositionWitihinAngledArea2D_Impl(CVector2D pos, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea, bool otherChecksFailed) { + if (otherChecksFailed) { + if (highlightArea) { + notsa::shapes::AngledRect{ a, b, widthAndDir }.HighlightWithMarkers(); + } + return false; + } + else { + notsa::shapes::AngledRect area{ a, b, widthAndDir }; + if (highlightArea) { + area.HighlightWithMarkers(); + } + return area.IsPointWithin(pos); + } +} + +bool IsPedInAngledArea2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot, bool mustBeStopped) { + return IsPositionWitihinAngledArea2D_Impl( + ped.GetPosition2D(), + a, b, widthAndDir, + highlightArea, + !DoLocateCharChecks(ped, mustBeInCar, mustBeOnFoot, mustBeStopped) + ); +} + +// IS_CHAR_IN_ANGLED_AREA_2D +auto IsCharInAngledArea2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, false, false, false); +} + +// IS_CHAR_IN_ANGLED_AREA_ON_FOOT_2D +auto IsCharInAngledAreaOnFoot2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, false, true, false); +} + +// IS_CHAR_IN_ANGLED_AREA_IN_CAR_2D +auto IsCharInAngledAreaInCar2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, true, false, false); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_2D +auto IsCharStoppedInAngledArea2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, false, false, true); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D +auto IsCharStoppedInAngledAreaOnFoot2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, false, true, true); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_IN_CAR_2D +auto IsCharStoppedInAngledAreaInCar2D(CPed& ped, CVector2D a, CVector2D b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea2D(ped, a, b, widthAndDir, highlightArea, true, false, true); +} + +// => 3D + +bool IsPedInAngledArea3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea, bool mustBeInCar, bool mustBeOnFoot, bool mustBeStopped) { + const auto& pos = ped.GetPosition(); + const auto [minZ, maxZ] = std::minmax(a.z, b.z); + return IsPositionWitihinAngledArea2D_Impl( + pos, + a, b, widthAndDir, + highlightArea, + (pos.z < minZ || pos.z > maxZ) || !DoLocateCharChecks(ped, mustBeInCar, mustBeOnFoot, mustBeStopped) + ); +} + +// IS_CHAR_IN_ANGLED_AREA_3D +auto IsCharInAngledArea3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, false, false, false); +} + +// IS_CHAR_IN_ANGLED_AREA_ON_FOOT_3D +auto IsCharInAngledAreaOnFoot3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, false, true, false); +} + +// IS_CHAR_IN_ANGLED_AREA_IN_CAR_3D +auto IsCharInAngledAreaInCar3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, true, false, false); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_3D +auto IsCharStoppedInAngledArea3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, false, false, true); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D +auto IsCharStoppedInAngledAreaOnFoot3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, false, true, true); +} + +// IS_CHAR_STOPPED_IN_ANGLED_AREA_IN_CAR_3D +auto IsCharStoppedInAngledAreaInCar3D(CPed& ped, CVector a, CVector b, float widthAndDir, bool highlightArea) { + return IsPedInAngledArea3D(ped, a, b, widthAndDir, highlightArea, true, false, true); +} + +auto IsCharDead(CPed* ped) { + return !ped || ped->IsStateDeadForScript(); +} + +// IS_CHAR_IN_ZONE +auto IsCharInZone(CPed& ped, std::string_view zoneName) { + return CTheZones::FindZone(GetCharCoordinates(ped), zoneName, ZONE_TYPE_NAVI); +} + +// GET_CHAR_HEADING +auto GetCharHeading(CPed& ped) { + return FixAngleDegrees(RadiansToDegrees(GetPedOrItsVehicle(ped).GetHeading())); +} + +// SET_CHAR_HEADING +auto SetCharHeading(CPed& ped, float deg) { + if (ped.IsInVehicle()) { + return; + } + + const auto rad = DegreesToRadians(FixAngleDegrees(deg)); + ped.m_fAimingRotation = ped.m_fCurrentRotation = rad; + ped.SetHeading(rad); + ped.UpdateRW(); +} + +// IS_CHAR_TOUCHING_OBJECT +auto IsCharTouchingObject(CPed& ped, CObject& obj) { + return GetPedOrItsVehicle(ped).GetHasCollidedWith(&obj); +} + +// SET_CHAR_AMMO +auto SetCharAmmo(CPed& ped, eWeaponType wep, int32 nammo) { + ped.SetAmmo(wep, nammo); +} + +// IS_CHAR_HEALTH_GREATER +auto IsCharHealthGreater(CPed& ped, float health) { + return ped.m_fHealth >= health; +} + +auto CreatePed(CRunningScript& S, ePedType pedType, eModelID pedModel) -> CPed& { + const auto ped = [&]() -> CPed* { + uint32 typeSpecificModelId = (uint32)(pedModel); + S.GetCorrectPedModelIndexForEmergencyServiceType(pedType, &typeSpecificModelId); + switch (pedType) { + case PED_TYPE_COP: return new CCopPed{ typeSpecificModelId }; + case PED_TYPE_MEDIC: + case PED_TYPE_FIREMAN: return new CEmergencyPed{ pedType, typeSpecificModelId }; + default: return new CCivilianPed{ pedType, typeSpecificModelId }; + } + }(); + ped->SetCharCreatedBy(PED_MISSION); + ped->bAllowMedicsToReviveMe = false; + CPopulation::ms_nTotalMissionPeds++; + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(*ped); + } + return *ped; +} + +//! Creates a character in the driver's seat of the vehicle +CPed& CreateCharInsideCar(CRunningScript& S, CVehicle& veh, ePedType pedType, eModelID pedModel) { + const auto ped = &CreatePed(S, pedType, pedModel); + CTaskSimpleCarSetPedInAsDriver{ &veh, false }.ProcessPed(ped); // Make ped get into da car + return *ped; +} + +// SET_CHAR_HEALTH +auto SetCharHealth(CPed& ped, float health) { + if (health != 0.f) { + if (ped.IsPlayer()) { + ped.m_fHealth = std::min(health, ped.m_fMaxHealth); + } else { + ped.m_fHealth = health; + if (ped.m_fMaxHealth == 100.f) { + ped.m_fMaxHealth = health; + } + } + } else { // Otherwise kill ped + ped.GetIntelligence()->m_eventGroup.Add( + CEventScriptCommand{ + TASK_PRIMARY_PRIMARY, + new CTaskComplexDie{ + WEAPON_UNARMED, + ANIM_GROUP_DEFAULT, + ANIM_ID_KO_SHOT_FRONT_0 + }, + false + } + ); + } +} + +// GET_CHAR_HEALTH +auto GetCharHealth(CPed& ped) { + return ped.m_fHealth; +} + +// IS_CHAR_TOUCHING_OBJECT_ON_FOOT +auto IsCharTouchingObjectOnFoot(CPed& ped, CObject& obj) { + return !ped.IsInVehicle() && ped.GetHasCollidedWith(&obj); +} + +// IS_CHAR_STOPPED +auto IsCharStopped(CPed& ped) { + return CTheScripts::IsPedStopped(&ped); +} + +// SET_CHAR_ONLY_DAMAGED_BY_PLAYER +auto SetCharOnlyDamagedByPlayer(CPed& ped, bool enabled) { + ped.physicalFlags.bInvulnerable = enabled; +} + +// GET_CLOSEST_CHAR_NODE +auto GetClosestCharNode(CVector pos) -> CVector { + CWorld::PutToGroundIfTooLow(pos); + if (const auto node = ThePaths.FindNodeClosestToCoors(pos)) { + return ThePaths.GetPathNode(node)->GetNodeCoors(); + } + return {}; // Can't find anything nearby +} + +// IS_CHAR_ON_SCREEN +auto IsCharOnScreen(CPed& ped) { + return ped.GetIsOnScreen(); +} + +// IS_CHAR_SHOOTING_IN_AREA +auto IsCharShootingInArea(CRunningScript& S, CPed& ped, CRect area, bool highlightArea) { + if (highlightArea) { + S.HighlightImportantArea(area); + } + + if (CTheScripts::DbgFlag) { + CTheScripts::DrawDebugSquare(area); + } + + return ped.bFiringWeapon && area.IsPointInside(ped.GetPosition2D()); +} + +// IS_CURRENT_CHAR_WEAPON +auto IsCurrentCharWeapon(CPed& ped, eWeaponType wep) { + if (wep == WEAPON_ANYMELEE && ped.GetActiveWeapon().IsTypeMelee()) { + return true; + } + return ped.GetActiveWeapon().m_nType == wep; +} + +// GET_RANDOM_CHAR_IN_ZONE +auto GetRandomCharInZone(CRunningScript& S, std::string_view zoneName, bool civillian, bool gang, bool criminal) -> CPed* { // 0x04802D0 + const auto playerPosZ = FindPlayerCoors().z; + for (auto& ped : GetPedPool()->GetAllValid()) { + const auto pedHandle = GetPedPool()->GetRef(&ped); + if ( pedHandle == CTheScripts::LastRandomPedId + || !ped.IsCreatedBy(PED_GAME) + || ped.m_bRemoveFromWorld + || ped.bFadeOut + || ped.IsStateDeadForScript() + || ped.IsInVehicle() + || !S.ThisIsAValidRandomPed(ped.m_nPedType, civillian, gang, criminal) + || ped.GetGroup() + ) { + continue; + } + const auto pos = ped.GetPosition(); + if (!CTheZones::FindZone(ped.GetPosition(), zoneName, ZONE_TYPE_NAVI)) { + continue; + } + if (playerPosZ - 5.f > pos.z || playerPosZ + 5.f < pos.z) { + continue; + } + bool roofZFound{}; + (void)(CWorld::FindRoofZFor3DCoord(pos.x, pos.y, pos.z, &roofZFound)); + if (roofZFound) { + continue; + } + CTheScripts::LastRandomPedId = pedHandle; + ped.SetCharCreatedBy(PED_MISSION); + CPopulation::ms_nTotalMissionPeds++; + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(ped); + } + + // They forgot to return here (did it instead after the loop), so let's do that + return &ped; + } + return nullptr; // No suitable ped found +} + +// IS_CHAR_MODEL +auto IsCharModel(CPed& ped, int8 accuracy) { + ped.m_nWeaponAccuracy = accuracy; +} + +// HAS_CHAR_BEEN_DAMAGED_BY_WEAPON +auto HasCharBeenDamagedByWeapon(CPed* ped, eWeaponType byWepType) { + if (!ped) { + return false; + } + switch (byWepType) { + case WEAPON_ANYWEAPON: + case WEAPON_ANYMELEE: + return CDarkel::CheckDamagedWeaponType((eWeaponType)(ped->m_nLastWeaponDamage), byWepType); + } + return (eWeaponType)(ped->m_nLastWeaponDamage) == byWepType; +} + +// EXPLODE_CHAR_HEAD +auto ExplodeCharHead(CPed& ped) { + CEventDamage dmgEvent{ + nullptr, + CTimer::GetTimeInMS(), + WEAPON_SNIPERRIFLE, + PED_PIECE_HEAD, + 0, + false, + (bool)(ped.bInVehicle) + }; + dmgEvent.ComputeDamageResponseIfAffectsPed( + &ped, + CPedDamageResponseCalculator{ + nullptr, + 1000.f, + WEAPON_SNIPERRIFLE, + PED_PIECE_HEAD, + false + }, + true + ); + ped.GetEventGroup().Add(dmgEvent); +} + +// START_CHAR_FIRE +auto StartCharFire(CPed& ped) { // TODO: return type: ScriptThing + return gFireManager.StartScriptFire(ped.GetPosition(), &ped, 0.8f, true, 0, 1); +} + +// SET_CHAR_VISIBLE +auto SetCharVisible(CPed& ped, bool isVisible) { + if (&ped == FindPlayerPed()) { + gPlayerPedVisible = isVisible; + } + ped.m_bIsVisible = isVisible; +} + +// REMOVE_CHAR_ELEGANTLY +auto RemoveCharElegantly(CRunningScript& S, CPed* ped) { + if (ped && ped->IsCreatedBy(PED_MISSION)) { + if (ped->IsInVehicle()) { + CTheScripts::RemoveThisPed(ped); + } else { + ped->SetCharCreatedBy(PED_GAME); + if (const auto grp = ped->GetGroup()) { + if (grp->GetMembership().IsFollower(ped)) { // TODO: Most likely inlined, this check makes no sense otherwise + grp->GetMembership().RemoveMember(*ped); + } + } + CPopulation::ms_nTotalMissionPeds--; + ped->bFadeOut = true; + CWorld::RemoveReferencesToDeletedObject(ped); + } + } + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.RemoveEntityFromList(*ped); + } +} + +// SET_CHAR_STAY_IN_SAME_PLACE +auto SetCharStayInSamePlace(CPed& ped, bool bStay) { + ped.SetStayInSamePlace(bStay); +} + +// WARP_CHAR_FROM_CAR_TO_COORD +auto WarpCharFromCarToCoord(CPed& ped, CVector pos) { + CWorld::PutToGroundIfTooLow(pos); + ped.GetIntelligence()->FlushImmediately(true); + pos.z += ped.GetDistanceFromCentreOfMassToBaseOfModel(); + ped.Teleport( + pos, + false + ); + CTheScripts::ClearSpaceForMissionEntity(pos, &ped); +} + +// HAS_CHAR_SPOTTED_CHAR +auto HasCharSpottedChar(CPed& ped, CPed& target) { + return ped.OurPedCanSeeThisEntity(&target, true); +} + +// WARP_CHAR_INTO_CAR +auto WarpCharIntoCar(CPed& ped, CVehicle& veh) { + ped.GetIntelligence()->FlushImmediately(false); + CTaskSimpleCarSetPedInAsDriver{ &veh, false }.ProcessPed(&ped); +} + +// SET_CHAR_ANIM_SPEED +auto SetCharAnimSpeed(CPed& ped, const char* animName, float speed) { + if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { + anim->SetSpeed(speed); + } +} + +// IS_CHAR_MALE +auto IsCharMale(CPed& ped) { + return !IsPedTypeFemale(ped.m_nPedType); +} + +// STORE_CAR_CHAR_IS_IN_NO_SAVE +auto StoreCarCharIsInNoSave(CPed& ped) -> CVehicle& { + assert(ped.bInVehicle && ped.m_pVehicle); // Original code had a bug (namely, calling VehiclePool::GetRef() with nullptr, very bad) + return *ped.m_pVehicle; +} + +// SET_CHAR_MONEY +auto SetCharMoney(CPed& ped, int16 money) { + ped.bMoneyHasBeenGivenByScript = true; + ped.m_nMoneyCount = money; +} + +// GET_AMMO_IN_CHAR_WEAPON +auto GetAmmoInCharWeapon(CPed& ped, eWeaponType wtype) -> uint32 { + for (auto& wep : ped.m_aWeapons) { + if (wep.m_nType == wtype) { // Originally they continued looping, but that doesn't make sense (A ped can't have the same weapon _twice_) + return wep.m_nTotalAmmo; + } + } + return 0; +} + +// WARP_CHAR_INTO_CAR_AS_PASSENGER +auto WarpCharIntoCarAsPassenger(CPed& ped, CVehicle& veh, int32 psgrSeatIdx) { + if (psgrSeatIdx >= 0) { + psgrSeatIdx = CCarEnterExit::ComputeTargetDoorToEnterAsPassenger(&veh, psgrSeatIdx); + } + ped.GetIntelligence()->FlushImmediately(false); + CTaskSimpleCarSetPedInAsPassenger{ &veh, (eTargetDoor)(psgrSeatIdx), true }.ProcessPed(&ped); // Warp ped into car +} + +// GET_CHAR_IN_CAR_PASSENGER_SEAT - TODO: Move to `Vehicle` +auto GetCharInCarPassengerSeat(CVehicle& veh, uint32 psgrSeatIdx) -> CPed& { + return *veh.m_apPassengers[psgrSeatIdx]; +} + +bool HasPedSittingInCarTask(CPed& ped) { + return ped.GetTaskManager().IsSimplestActiveTaskOfType({ TASK_SIMPLE_CAR_DRIVE, TASK_SIMPLE_GANG_DRIVEBY }); +} + +// IS_CHAR_SITTING_IN_CAR +auto IsCharSittingInCar(CPed& ped, CVehicle& veh) { + return ped.bInVehicle && ped.m_pVehicle == &veh && HasPedSittingInCarTask(ped); +} + +// IS_CHAR_SITTING_IN_ANY_CAR +auto IsCharSittingInAnyCar(CPed& ped) { + return ped.bInVehicle && HasPedSittingInCarTask(ped); +} + +// IS_CHAR_ON_FOOT +auto IsCharOnFoot(CPed& ped) { + return !ped.bInVehicle && !ped.GetTaskManager().HasAnyOf(); +} + +// ATTACH_CHAR_TO_CAR +auto AttachCharToCar(CPed& ped, CVehicle& veh, CVector offset, int32 pos, float angleLimitDeg, eWeaponType wtype) { + ped.AttachPedToEntity( + &veh, + offset, + pos, + DegreesToRadians(angleLimitDeg), + wtype + ); +} + +// DETACH_CHAR_FROM_CAR +auto DetachCharFromCar(CPed* ped) { + if (ped && ped->m_pAttachedTo) { + ped->DettachPedFromEntity(); + } +} + +// CLEAR_CHAR_LAST_WEAPON_DAMAGE +auto ClearCharLastWeaponDamage(CPed& ped) { + ped.m_nLastWeaponDamage = -1; +} + +// GET_CURRENT_CHAR_WEAPON +auto GetCurrentCharWeapon(CPed& ped) { + return ped.GetActiveWeapon().m_nType; +} + +// CAN_CHAR_SEE_DEAD_CHAR +auto CanCharSeeDeadChar(CPed& ped) { + for (auto& nearbyPed : ped.GetIntelligence()->GetPedScanner().GetEntities()) { + if (!nearbyPed.IsAlive() && CPedGeometryAnalyser::CanPedTargetPed(ped, nearbyPed, true)) { + return true; + } + } + return false; +} + +// SHUT_CHAR_UP +auto ShutCharUp(CPed& ped, bool stfu) { + if (stfu) { + ped.DisablePedSpeech(false); + } else { + ped.EnablePedSpeech(); + } +} + +// REMOVE_ALL_CHAR_WEAPONS +auto RemoveAllCharWeapons(CPed& ped) { + ped.ClearWeapons(); +} + +// HAS_CHAR_GOT_WEAPON +auto HasCharGotWeapon(CPed& ped, eWeaponType wtype) { + return notsa::contains(ped.m_aWeapons, wtype, [](CWeapon& w) { return w.m_nType; }); +} + +// GET_DEAD_CHAR_PICKUP_COORDS +auto GetDeadCharPickupCoords(CPed& ped) { + CVector pos; + ped.CreateDeadPedPickupCoors(pos); + return pos; +} + +auto IsPedInVehicleOfAppearance(CPed& ped, eVehicleAppearance appr) { + return ped.IsInVehicle() && ped.m_pVehicle->GetVehicleAppearance() == appr; +} + +// IS_CHAR_ON_ANY_BIKE +auto IsCharOnAnyBike(CPed& ped) { + return IsPedInVehicleOfAppearance(ped, VEHICLE_APPEARANCE_BIKE); +} + +// IS_CHAR_IN_ANY_BOAT +auto IsCharInAnyBoat(CPed& ped) { + return IsPedInVehicleOfAppearance(ped, VEHICLE_APPEARANCE_BOAT); +} + +// IS_CHAR_IN_ANY_HELI +auto IsCharInAnyHeli(CPed& ped) { + return IsPedInVehicleOfAppearance(ped, VEHICLE_APPEARANCE_HELI); +} + +// IS_CHAR_IN_ANY_PLANE +auto IsCharInAnyPlane(CPed& ped) { + return IsPedInVehicleOfAppearance(ped, VEHICLE_APPEARANCE_PLANE); +} + +// IS_CHAR_IN_WATER +auto IsCharInWater(CPed* ped) { + return ped && ped->physicalFlags.bSubmergedInWater; +} + +// GET_CHAR_WEAPON_IN_SLOT +auto GetCharWeaponInSlot(CPed& ped, eWeaponSlot slut) { + const auto& wep = ped.GetWeaponInSlot(slut); + return notsa::script::return_multiple(wep.m_nType, wep.m_nTotalAmmo, CPickups::ModelForWeapon(wep.m_nType)); +} + +// GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS +auto GetOffsetFromCharInWorldCoords(CPed& ped, CVector offset) { + return ped.GetMatrix() * offset; +} + +// HAS_CHAR_BEEN_PHOTOGRAPHED +auto HasCharBeenPhotographed(CPed* ped) { + if (ped) { + if (ped->bHasBeenPhotographed) { + ped->bHasBeenPhotographed = false; + return true; + } + } + return false; +} + +// IS_CHAR_IN_FLYING_VEHICLE +auto IsCharInFlyingVehicle(CPed& ped) { + return IsCharInAnyHeli(ped) || IsCharInAnyPlane(ped); +} + +// GET_CHAR_ARMOUR +auto GetCharArmour(CPed& ped) { + return ped.m_fArmour; +} + +// ATTACH_CHAR_TO_OBJECT +auto AttachCharToObject(CPed& ped, CObject& obj, CVector offset, int32 orientation, float angleLimitDeg, eWeaponType wtype) { + ped.AttachPedToEntity(&obj, offset, orientation, DegreesToRadians(angleLimitDeg), wtype); +} + +// HAS_CHAR_BEEN_DAMAGED_BY_CHAR +auto HasCharBeenDamagedByChar(CPed* ped, CPed& byPed) { + if (!ped) { + return false; + } + if (!ped->m_pLastEntityDamage) { + return false; + } + if (ped->m_pLastEntityDamage == &byPed) { + return true; + } + if (byPed.bInVehicle && ped->m_pLastEntityDamage == byPed.m_pVehicle) { + return true; + } + return false; +} + +// HAS_CHAR_BEEN_DAMAGED_BY_CAR +auto HasCharBeenDamagedByCar(CPed* ped, CVehicle& veh) { + return ped && ped->m_pLastEntityDamage == &veh; +} + +// IS_CHAR_TOUCHING_VEHICLE +auto IsCharTouchingVehicle(CPed& ped, CVehicle& withVeh) { + return ped.bInVehicle + ? ped.m_pVehicle->GetHasCollidedWith(&withVeh) + : ped.GetHasCollidedWith(&withVeh); +} + +// CLEAR_CHAR_LAST_DAMAGE_ENTITY +auto ClearCharLastDamageEntity(CPed* ped) { + if (ped) { + ped->m_pLastEntityDamage = nullptr; + } +} + +auto CreateRandomCharInVehicle(CRunningScript& S, CVehicle& veh, bool asDriver, int32 psgrSeatIdx = -1) -> CPed& { + const auto ped = CPopulation::AddPedInCar(&veh, asDriver, -1, asDriver ? 0 : psgrSeatIdx, false, false); + ped->SetCharCreatedBy(PED_MISSION); + if (!asDriver) { + ped->GetTaskManager().SetTask(nullptr, TASK_PRIMARY_PRIMARY); + } + ped->bAllowMedicsToReviveMe = false; + CPopulation::ms_nTotalMissionPeds++; + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(*ped); + } + return *ped; +} + +// CREATE_RANDOM_CHAR_AS_DRIVER +auto CreateRandomCharAsDriver(CRunningScript& S, CVehicle& veh) -> CPed& { + return CreateRandomCharInVehicle(S, veh, true); +} + +// CREATE_RANDOM_CHAR_AS_PASSENGER +auto CreateRandomCharAsPassenger(CRunningScript& S, CVehicle& veh, int32 psgrSeatIdx) -> CPed& { + return CreateRandomCharInVehicle(S, veh, false, psgrSeatIdx); +} + +// IS_CHAR_IN_ANY_POLICE_VEHICLE +auto IsCharInAnyPoliceVehicle(CPed& ped) { + if (!ped.IsInVehicle()) { + return false; + } + const auto veh = ped.m_pVehicle; + return veh->IsLawEnforcementVehicle() && veh->m_nModelIndex != eModelID::MODEL_PREDATOR; +} + +// DOES_CHAR_EXIST +auto DoesCharExist(CPed* ped) { + return ped != nullptr; +} + +auto SetCharAccuracy(CPed& ped, uint8 accuracy) { + ped.m_nWeaponAccuracy = accuracy; +} + +void DoSetPedIsWaitingForCollision(CRunningScript& S, CPed& ped) { + if (S.m_bUseMissionCleanup) { + CWorld::Remove(&ped); + ped.m_bIsStaticWaitingForCollision = true; + CWorld::Add(&ped); + } +} + +// FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION +auto FreezeCharPositionAndDontLoadCollision(CRunningScript& S, CPed& ped, bool freeze) { + ped.physicalFlags.bDontApplySpeed = freeze; + if (freeze) { + DoSetPedIsWaitingForCollision(S, ped); + } +} + +// SET_LOAD_COLLISION_FOR_CHAR_FLAG +auto SetLoadCollisionForCharFlag(CRunningScript& S, CPed& ped, bool loadCol) { + ped.physicalFlags.b15 = !loadCol; + if (loadCol) { + DoSetPedIsWaitingForCollision(S, ped); + } else if (ped.m_bIsStaticWaitingForCollision) { + ped.m_bIsStaticWaitingForCollision = false; + if (!ped.IsStatic()) { // TODO: I think this is inlined + ped.AddToMovingList(); + } + } +} + +// TASK_KILL_CHAR_ON_FOOT +auto TaskKillCharOnFoot(CRunningScript& S, eScriptCommands opcode, CPed& ped, CPed& target) { + S.GivePedScriptedTask( + &ped, + new CTaskComplexKillPedOnFoot{ &target, -1, 0, 0, 0, true }, + (int32)(opcode) + ); +} + +// IS_CHAR_IN_TAXI +auto IsCharInTaxi(CPed& ped) { + if (ped.IsInVehicle()) { + switch ((eModelID)(ped.m_pVehicle->m_nModelIndex)) { + case MODEL_CABBIE: + case MODEL_TAXI: + return true; + } + } + return false; +} + +// LOAD_CHAR_DECISION_MAKER +auto LoadCharDecisionMaker(CRunningScript& S, int32 type) { // TODO: return ScriptThing + char pedDMName[1024]; + CDecisionMakerTypesFileLoader::GetPedDMName(type, pedDMName); + const auto id = CDecisionMakerTypesFileLoader::LoadDecisionMaker(pedDMName, DECISION_ON_FOOT, S.m_bUseMissionCleanup); + const auto handle = CTheScripts::GetNewUniqueScriptThingIndex(id, SCRIPT_THING_DECISION_MAKER); + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(handle, MISSION_CLEANUP_ENTITY_TYPE_DECISION_MAKER); + } + return handle; +} + +// SET_CHAR_DECISION_MAKER +auto SetCharDecisionMaker(CPed& ped, int32 scriptHandleOfDM) { // TODO: Use `ScriptThing` instead of `int32` for `scriptHandleOfDM` + ped.GetIntelligence()->SetPedDecisionMakerType( + scriptHandleOfDM == -1 + ? -1 + : CTheScripts::GetActualScriptThingIndex(scriptHandleOfDM, SCRIPT_THING_DECISION_MAKER) + ); +} + +// IS_CHAR_PLAYING_ANIM +auto IsCharPlayingAnim(CPed& ped, const char* animName) { + return RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName) != nullptr; +} + +// SET_CHAR_ANIM_PLAYING_FLAG +auto SetCharAnimPlayingFlag(CPed& ped, const char* animName, bool started) { + if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { + anim->SetFlag(ANIMATION_STARTED, started); + } +} + +// GET_CHAR_ANIM_CURRENT_TIME +auto GetCharAnimCurrentTime(CPed& ped, const char* animName) { + if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { + return anim->m_fCurrentTime / anim->m_pHierarchy->m_fTotalTime; + } + return 0.f; +} + +// SET_CHAR_ANIM_CURRENT_TIME +auto SetCharAnimCurrentTime(CPed& ped, const char* animName, float progress) { + if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { + anim->SetCurrentTime(progress * anim->m_pHierarchy->m_fTotalTime); + } +} + +// GET_CHAR_ANIM_TOTAL_TIME (In seconds) +auto GetCharAnimTotalTime(CPed& ped, const char* animName) { + if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { + return anim->m_pHierarchy->m_fTotalTime * 1000.f; + } + return 0.f; +} + +// CREATE_CHAR_AT_ATTRACTOR +//auto CreateCharAtAttractor(CRunningScript& S, ePedType pedType, eModelID pedModel, int32 taskType, C2dEffectPedAttractor* attractor) -> CPed* { + /* + if (!attractor) { + return nullptr; + } + + auto& ped = CreatePed(S, pedType, pedModel); + + CPedAttractorPedPlacer::PlacePedAtEffect(*attractor, nullptr, &ped, 0.01f); + CTheScripts::ClearSpaceForMissionEntity(ped.GetPosition(), &ped); + if (S.m_bUseMissionCleanup) { + ped.m_bIsStaticWaitingForCollision = true; + } + CWorld::Add(&ped); + + // Set the task specified + + if (const auto task = [&] { + switch ((eScriptCommands)(taskType)) { // Can't take this as an input argument, as it will always put in the current command instead of reading it off the stack + case COMMAND_TASK_STAND_STILL: + return new CTaskSimpleStandStill{} + } + }()) { + + } + + // TODO: Code is so far correct, but we're missing the stub for `CTaskComplexUseEffect` + */ +//} + +// TASK_KILL_CHAR_ON_FOOT_WHILE_DUCKING +auto TaskKillCharOnFootWhileDucking(eScriptCommands command, CRunningScript& S, CPed& ped, CPed& target, int32 flags, int32 actionDelay, int32 actionChance) { + S.GivePedScriptedTask( + &ped, + new CTaskComplexKillPedOnFoot{ + &target, + -1, + flags, + actionDelay, + actionChance, + true + }, + command + ); +} + +// TASK_TURN_CHAR_TO_FACE_CHAR +auto TaskTurnCharToFaceChar(eScriptCommands command, CRunningScript& S, CPed& ped, CPed& target) { + S.GivePedScriptedTask( + &ped, + new CTaskComplexTurnToFaceEntityOrCoord{ &ped }, + command + ); +} + +// IS_CHAR_AT_SCRIPTED_ATTRACTOR +auto IsCharAtScriptedAttractor(CPed* ped, C2dEffectPedAttractor* attractor) { + if (!attractor) { + return false; + } + const auto pedUsingAttractor = GetPedAttractorManager()->GetPedUsingEffect(reinterpret_cast(attractor)); + return ped + ? pedUsingAttractor == ped + : pedUsingAttractor != nullptr; +} + +// GET_CHAR_MODEL +auto GetCharModel(CPed& ped) { + return (eModelID)(ped.m_nModelIndex); +} + +// SET_CURRENT_CHAR_WEAPON +void SetCurrentCharWeapon(CPed& ped, eWeaponType weaponType) { + for (auto&& [slot, weapon] : notsa::enumerate(ped.m_aWeapons)) { + if (weapon.m_nType != weaponType) + continue; + + if (ped.IsPlayer()) { + ped.AsPlayer()->m_pPlayerData->m_nChosenWeapon = slot; + } else { + ped.SetCurrentWeapon(slot); + } + } +} + +// MARK_CHAR_AS_NO_LONGER_NEEDED +void MarkCharAsNoLongerNeeded(CRunningScript& S, int32 handle) { // TODO: Some way to get a CPed* and it's handle too (As this function seems to be called even if the handle is not pointing to a ped anymore) + const auto ped = GetPedPool()->GetAtRef(handle); // This might be null, but we need the handle even if it is, so we can't take `CPed*` either... + CTheScripts::CleanUpThisPed(ped); + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.RemoveEntityFromList(handle, MISSION_CLEANUP_ENTITY_TYPE_PED); + } +} + +// GET_CHAR_SPEED +float GetCharSpeed(CPed& ped) { + return ped.GetMoveSpeed().Magnitude() * 50.0f; +} + +// IS_CHAR_IN_SEARCH_LIGHT +bool IsCharInSearchlight(uint32 searchLightIdx, CPed& ped) { + return CSearchLight::IsSpottedEntity(searchLightIdx, ped); +} + +// REMOVE_CHAR_FROM_GROUP +void RemoveCharFromGroup(CPed& ped) { + if (auto pedGroup = ped.GetGroup(); pedGroup && !pedGroup->GetMembership().IsLeader(&ped)) { + pedGroup->GetMembership().RemoveMember(ped); + pedGroup->Process(); + } +} + +// ATTACH_CHAR_TO_BIKE +void AttachCharToBike(CPed& ped, CVehicle& bike, CVector posn, uint16 heading, float headingLimit, float verticalLimit, eWeaponType weapon) { + // turret on car? + ped.AttachPedToBike(&bike, posn, heading, DegreesToRadians(headingLimit), DegreesToRadians(verticalLimit), weapon); +} + +// GET_CAR_CHAR_IS_USING +CVehicle* GetCarCharIsUsing(CPed& ped) { + if (ped.bInVehicle) { + return ped.m_pVehicle; + } + + auto task = reinterpret_cast([&ped]() -> CTask* { + if (auto driver = ped.GetIntelligence()->FindTaskByType(TASK_COMPLEX_ENTER_CAR_AS_DRIVER)) { + return driver; + } + if (auto passenger = ped.GetIntelligence()->FindTaskByType(TASK_COMPLEX_ENTER_CAR_AS_PASSENGER)) { + return passenger; + } + + return nullptr; + }()); + + return task ? task->m_car : nullptr; +} + +// ENABLE_CHAR_SPEECH +void EnableCharSpeech(CPed& ped) { + ped.EnablePedSpeech(); +} + +// DISABLE_CHAR_SPEECH +void DisableCharSpeech(CPed& ped, bool stopCurrentSpeech) { + ped.DisablePedSpeech(stopCurrentSpeech); +} + +// SET_CHAR_WANTED_BY_POLICE +void SetCharWantedByPolice(CPed& ped) { + ped.bWantedByPolice = true; +} + +// SET_CHAR_SIGNAL_AFTER_KILL +void SetCharSignalAfterKill(CPed& ped) { + ped.bSignalAfterKill = true; +} + +// SET_CHAR_COORDINATES_DONT_WARP_GANG_NO_OFFSET +void SetCharCoordinatesDontWarpGangNoOffset(CPed& ped, CVector posn) { + CRunningScript::SetCharCoordinates(ped, posn, false, false); +} + +// IS_CHAR_USING_MAP_ATTRACTOR +bool IsCharUsingMapAttractor(CPed& ped) { + return GetPedAttractorManager()->IsPedRegisteredWithEffect(&ped); +} + +// todo: move that to somewhere else +eTargetDoor ComputeTargetDoorToExit(const CVehicle& vehicle, const CPed& ped) { + return plugin::CallAndReturn(vehicle, ped); +} + +// REMOVE_CHAR_FROM_CAR_MAINTAIN_POSITION +void RemoveCharFromCarMaintainPosition(CPed& ped, CVehicle& vehicle) { + const auto pedPos = ped.GetPosition(); + CTaskSimpleCarSetPedOut task(&vehicle, ComputeTargetDoorToExit(vehicle, ped), false); + task.ProcessPed(&ped); + ped.SetPosn(pedPos); // ? +} + +// SET_CHAR_SAY_CONTEXT_IMPORTANT +int16 SetCharSayContextImportant(CPed& ped, uint16 phraseId, uint8 arg3, uint8 arg4, uint8 arg5) { + return ped.Say(phraseId, 0u, 1.0f, arg3, arg4, arg5); +} + +// SET_CHAR_SAY_SCRIPT +void SetCharSayScript(CPed& ped, uint8 arg1, uint8 arg2, uint8 arg3, uint8 arg4) { + ped.SayScript(arg1, arg2, arg3, arg4); +} + +// IS_CHAR_GETTING_IN_INTO_A_CAR +bool IsCharGettingInToACar(CPed& ped) { + return ped.GetTaskManager().FindActiveTaskFromList({TASK_COMPLEX_ENTER_CAR_AS_DRIVER, TASK_COMPLEX_ENTER_CAR_AS_PASSENGER, TASK_COMPLEX_GO_TO_CAR_DOOR_AND_STAND_STILL}); +} + +// GET_CHAR_AREA_VISIBLE +uint32 GetCharAreaVisible(CPed& ped) { + return ped.m_nAreaCode; +} + +// HAS_CHAR_SPOTTED_CHAR_IN_FRONT +bool HasCharSpottedCharInFront(CPed& ped, CPed& other) { + return ped.OurPedCanSeeThisEntity(&other, true); +} + +// SHUT_CHAR_UP_FOR_SCRIPTED_SPEECH +void ShutCharUpForScriptedSpeech(CPed* ped, bool disable) { + if (!ped) + return; + + ped->DisablePedSpeechForScriptSpeech(disable); +} + +// IS_CHAR_TOUCHING_CHAR +bool IsCharTouchingChar(CPed& ped, CPed& other) { + const auto GetRelevantEntity = [](CPed& _ped) -> CPhysical* { + return _ped.IsInVehicle() ? _ped.m_pVehicle->AsPhysical() : _ped.AsPhysical(); + }; + return GetRelevantEntity(ped)->GetHasCollidedWith(GetRelevantEntity(other)); +} + +// IS_CHAR_ATTACHED_TO_ANY_CAR +bool IsCharAttachedToAnyCar(CPed* ped) { + return ped && ped->m_nType == ENTITY_TYPE_VEHICLE; +} + +// STORE_CAR_CHAR_IS_ATTACHED_TO_NO_SAVE +CVehicle* StoreCarCharIsAttachedToNoSave(CPed* ped) { + if (ped->m_nType != ENTITY_TYPE_VEHICLE) { + return nullptr; + } + return ped->m_pAttachedTo->AsVehicle(); +} + +void notsa::script::commands::character::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_PROOFS, SetCharProofs); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_VELOCITY, SetCharVelocity); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_VELOCITY, GetCharVelocity); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ROTATION, SetCharRotation); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ALLOWED_TO_DUCK, SetCharAllowedToDuck); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_AREA_VISIBLE, SetCharAreaVisible); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_DROPS_WEAPONS_WHEN_DEAD, SetCharDropsWeaponsWhenDead); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_NEVER_LEAVES_GROUP, SetCharNeverLeavesGroup); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_FX_SYSTEM_TO_CHAR_BONE, AttachFxSystemToCharBone); + REGISTER_COMMAND_HANDLER(COMMAND_GET_DEAD_CHAR_COORDINATES, GetDeadCharCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_COORDINATES, GetCharCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_COORDINATES, SetCharCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_AREA_2D, IsCharInArea2D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_AREA_3D, IsCharInArea3D); + REGISTER_COMMAND_HANDLER(COMMAND_STORE_CAR_CHAR_IS_IN, StoreCarCharIsIn); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_CAR, IsCharInCar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_MODEL, IsCharInModel); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_CAR, IsCharInAnyCar); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_2D, LocateCharAnyMeans2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_2D, LocateCharOnFoot2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_2D, LocateCharInCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D, LocateStoppedCharAnyMeans2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D, LocateStoppedCharOnFoot2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D, LocateStoppedCharInCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_3D, LocateCharAnyMeans3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_3D, LocateCharOnFoot3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_3D, LocateCharInCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D, LocateStoppedCharAnyMeans3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D, LocateStoppedCharOnFoot3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D, LocateStoppedCharInCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D, LocateCharAnyMeansChar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D, LocateCharOnFootChar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D, LocateCharInCarChar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D, LocateCharAnyMeansChar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D, LocateCharOnFootChar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D, LocateCharInCarChar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D, LocateCharAnyMeansCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D, LocateCharOnFootCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D, LocateCharInCarCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D, LocateCharAnyMeansCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D, LocateCharOnFootCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D, LocateCharInCarCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_DEAD, IsCharDead); + REGISTER_COMMAND_HANDLER(COMMAND_CREATE_CHAR_INSIDE_CAR, CreateCharInsideCar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ZONE, IsCharInZone); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_HEADING, GetCharHeading); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_HEADING, SetCharHeading); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_TOUCHING_OBJECT, IsCharTouchingObject); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_AMMO, SetCharAmmo); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_HEALTH_GREATER, IsCharHealthGreater); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_HEALTH, SetCharHealth); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_HEALTH, GetCharHealth); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT, IsCharTouchingObjectOnFoot); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_STOPPED, IsCharStopped); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER, SetCharOnlyDamagedByPlayer); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CLOSEST_CHAR_NODE, GetClosestCharNode); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_ON_SCREEN, IsCharOnScreen); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_SHOOTING_IN_AREA, IsCharShootingInArea); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CURRENT_CHAR_WEAPON, IsCurrentCharWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_CHAR_IN_ZONE, GetRandomCharInZone); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_SHOOTING, IsCharShooting); // bad + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ACCURACY, SetCharAccuracy); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_MODEL, IsCharModel); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON, HasCharBeenDamagedByWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_EXPLODE_CHAR_HEAD, ExplodeCharHead); + REGISTER_COMMAND_HANDLER(COMMAND_START_CHAR_FIRE, StartCharFire); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_BLEEDING, SetCharBleeding); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_VISIBLE, SetCharVisible); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_CHAR_ELEGANTLY, RemoveCharElegantly); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_STAY_IN_SAME_PLACE, SetCharStayInSamePlace); + REGISTER_COMMAND_HANDLER(COMMAND_WARP_CHAR_FROM_CAR_TO_COORD, WarpCharFromCarToCoord); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_SPOTTED_CHAR, HasCharSpottedChar); + REGISTER_COMMAND_HANDLER(COMMAND_WARP_CHAR_INTO_CAR, WarpCharIntoCar); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ANIM_SPEED, SetCharAnimSpeed); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT, SetCharCantBeDraggedOut); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_MALE, IsCharMale); + REGISTER_COMMAND_HANDLER(COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE, StoreCarCharIsInNoSave); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_MONEY, SetCharMoney); + REGISTER_COMMAND_HANDLER(COMMAND_GET_AMMO_IN_CHAR_WEAPON, GetAmmoInCharWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER, WarpCharIntoCarAsPassenger); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT, GetCharInCarPassengerSeat); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL, SetCharIsChrisCriminal); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS, SetCharSuffersCriticalHits); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_SITTING_IN_CAR, IsCharSittingInCar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_SITTING_IN_ANY_CAR, IsCharSittingInAnyCar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_ON_FOOT, IsCharOnFoot); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CHAR_TO_CAR, AttachCharToCar); + REGISTER_COMMAND_HANDLER(COMMAND_DETACH_CHAR_FROM_CAR, DetachCharFromCar); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE, ClearCharLastWeaponDamage); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CURRENT_CHAR_WEAPON, GetCurrentCharWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D, LocateCharAnyMeansObject2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D, LocateCharOnFootObject2D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D, LocateCharAnyMeansObject3D); + REGISTER_COMMAND_HANDLER(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D, LocateCharOnFootObject3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_ON_ANY_BIKE, IsCharOnAnyBike); + REGISTER_COMMAND_HANDLER(COMMAND_CAN_CHAR_SEE_DEAD_CHAR, CanCharSeeDeadChar); + REGISTER_COMMAND_HANDLER(COMMAND_SHUT_CHAR_UP, ShutCharUp); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_ALL_CHAR_WEAPONS, RemoveAllCharWeapons); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_GOT_WEAPON, HasCharGotWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_GET_DEAD_CHAR_PICKUP_COORDS, GetDeadCharPickupCoords); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_BOAT, IsCharInAnyBoat); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_HELI, IsCharInAnyHeli); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_PLANE, IsCharInAnyPlane); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_WATER, IsCharInWater); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_WEAPON_IN_SLOT, GetCharWeaponInSlot); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS, GetOffsetFromCharInWorldCoords); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED, HasCharBeenPhotographed); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_FLYING_VEHICLE, IsCharInFlyingVehicle); + REGISTER_COMMAND_HANDLER(COMMAND_FREEZE_CHAR_POSITION, FreezeCharPosition); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_DROWNS_IN_WATER, SetCharDrownsInWater); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_ARMOUR, GetCharArmour); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION, IsCharWaitingForWorldCollision); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CHAR_TO_OBJECT, AttachCharToObject); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR, HasCharBeenDamagedByChar); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR, HasCharBeenDamagedByCar); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED, SetCharStayInCarWhenJacked); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_TOUCHING_VEHICLE, IsCharTouchingVehicle); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE, SetCharCanBeShotInVehicle); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY, ClearCharLastDamageEntity); + REGISTER_COMMAND_HANDLER(COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER, CreateRandomCharAsDriver); + REGISTER_COMMAND_HANDLER(COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER, CreateRandomCharAsPassenger); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_NEVER_TARGETTED, SetCharNeverTargetted); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE, IsCharInAnyPoliceVehicle); + REGISTER_COMMAND_HANDLER(COMMAND_DOES_CHAR_EXIST, DoesCharExist); + REGISTER_COMMAND_HANDLER(COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION, FreezeCharPositionAndDontLoadCollision); + REGISTER_COMMAND_HANDLER(COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG, SetLoadCollisionForCharFlag); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_DUCKING, IsCharDucking); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_KILL_CHAR_ON_FOOT, TaskKillCharOnFoot); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANGLED_AREA_2D, IsCharInAngledArea2D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANGLED_AREA_IN_CAR_2D, IsCharInAngledAreaInCar2D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANGLED_AREA_3D, IsCharInAngledArea3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANGLED_AREA_ON_FOOT_3D, IsCharInAngledAreaOnFoot3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANGLED_AREA_IN_CAR_3D, IsCharInAngledAreaInCar3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_TAXI, IsCharInTaxi); + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_CHAR_DECISION_MAKER, LoadCharDecisionMaker); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_DECISION_MAKER, SetCharDecisionMaker); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_PLAYING_ANIM, IsCharPlayingAnim); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ANIM_PLAYING_FLAG, SetCharAnimPlayingFlag); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_ANIM_CURRENT_TIME, GetCharAnimCurrentTime); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_ANIM_CURRENT_TIME, SetCharAnimCurrentTime); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_COLLISION, SetCharCollision); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_ANIM_TOTAL_TIME, GetCharAnimTotalTime); + // REGISTER_COMMAND_HANDLER(COMMAND_CREATE_CHAR_AT_ATTRACTOR, CreateCharAtAttractor); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_KILL_CHAR_ON_FOOT_WHILE_DUCKING, TaskKillCharOnFootWhileDucking); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_TURN_CHAR_TO_FACE_CHAR, TaskTurnCharToFaceChar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_AT_SCRIPTED_ATTRACTOR, IsCharAtScriptedAttractor); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_MODEL, GetCharModel); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CURRENT_CHAR_WEAPON, SetCurrentCharWeapon); + REGISTER_COMMAND_HANDLER(COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED, MarkCharAsNoLongerNeeded); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_SPEED, GetCharSpeed); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_SEARCHLIGHT, IsCharInSearchlight); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_CHAR_FROM_GROUP, RemoveCharFromGroup); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CHAR_TO_BIKE, AttachCharToBike); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CAR_CHAR_IS_USING, GetCarCharIsUsing); + REGISTER_COMMAND_HANDLER(COMMAND_DISABLE_CHAR_SPEECH, DisableCharSpeech); + REGISTER_COMMAND_HANDLER(COMMAND_ENABLE_CHAR_SPEECH, EnableCharSpeech); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SIGNAL_AFTER_KILL, SetCharSignalAfterKill); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_WANTED_BY_POLICE, SetCharWantedByPolice); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_COORDINATES_DONT_WARP_GANG_NO_OFFSET, SetCharCoordinatesDontWarpGangNoOffset); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_USING_MAP_ATTRACTOR, IsCharUsingMapAttractor); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_CHAR_FROM_CAR_MAINTAIN_POSITION, RemoveCharFromCarMaintainPosition); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SAY_CONTEXT_IMPORTANT, SetCharSayContextImportant); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SAY_SCRIPT, SetCharSayScript); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_GETTING_IN_TO_A_CAR, IsCharGettingInToACar); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_AREA_VISIBLE, GetCharAreaVisible); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_SPOTTED_CHAR_IN_FRONT, HasCharSpottedCharInFront); + REGISTER_COMMAND_HANDLER(COMMAND_SHUT_CHAR_UP_FOR_SCRIPTED_SPEECH, ShutCharUpForScriptedSpeech); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_TOUCHING_CHAR, IsCharTouchingChar); + REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_ATTACHED_TO_ANY_CAR, IsCharAttachedToAnyCar); + REGISTER_COMMAND_HANDLER(COMMAND_STORE_CAR_CHAR_IS_ATTACHED_TO_NO_SAVE, StoreCarCharIsAttachedToNoSave); + //REGISTER_COMMAND_HANDLER(COMMAND_CREATE_FX_SYSTEM_ON_CHAR_WITH_DIRECTION, CreateFxSystemOnCharWithDirection); + //REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_CAMERA_TO_CHAR_LOOK_AT_CHAR, AttachCameraToCharLookAtChar); + //REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_TASKS, ClearCharTasks); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_GOTO_CHAR_OFFSET, TaskGotoCharOffset); + //REGISTER_COMMAND_HANDLER(COMMAND_HIDE_CHAR_WEAPON_FOR_SCRIPTED_CUTSCENE, HideCharWeaponForScriptedCutscene); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_TURN_CHAR_TO_FACE_COORD, TaskTurnCharToFaceCoord); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_CHAR_ARREST_CHAR, TaskCharArrestChar); + //REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_DECISION_MAKER_EVENT_RESPONSE, ClearCharDecisionMakerEventResponse); + //REGISTER_COMMAND_HANDLER(COMMAND_ADD_CHAR_DECISION_MAKER_EVENT_RESPONSE, AddCharDecisionMakerEventResponse); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_WARP_CHAR_INTO_CAR_AS_DRIVER, TaskWarpCharIntoCarAsDriver); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_WARP_CHAR_INTO_CAR_AS_PASSENGER, TaskWarpCharIntoCarAsPassenger); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_HOLDING_OBJECT, IsCharHoldingObject); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_CHAR_IN_SPHERE, GetRandomCharInSphere); + //REGISTER_COMMAND_HANDLER(COMMAND_HAS_CHAR_BEEN_ARRESTED, HasCharBeenArrested); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_RESPONDING_TO_EVENT, IsCharRespondingToEvent); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_FLEE_CHAR_ANY_MEANS, TaskFleeCharAnyMeans); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_IS_TARGET_PRIORITY, SetCharIsTargetPriority); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_RELATIONSHIP, SetCharRelationship); + //REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_RELATIONSHIP, ClearCharRelationship); + //REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_ALL_CHAR_RELATIONSHIPS, ClearAllCharRelationships); + //REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_CHAR_TASKS_IMMEDIATELY, ClearCharTasksImmediately); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_GOTO_CHAR_AIMING, TaskGotoCharAiming); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_KILL_CHAR_ON_FOOT_TIMED, TaskKillCharOnFootTimed); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_SEARCHLIGHT, IsCharInAnySearchlight); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_SET_CHAR_DECISION_MAKER, TaskSetCharDecisionMaker); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_CHAR_SLIDE_TO_COORD, TaskCharSlideToCoord); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SHOOT_RATE, SetCharShootRate); + //REGISTER_COMMAND_HANDLER(COMMAND_COPY_CHAR_DECISION_MAKER, CopyCharDecisionMaker); + //REGISTER_COMMAND_HANDLER(COMMAND_TASK_CHAR_SLIDE_TO_COORD_AND_PLAY_ANIM, TaskCharSlideToCoordAndPlayAnim); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_HIGHEST_PRIORITY_EVENT, GetCharHighestPriorityEvent); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_KINDA_STAY_IN_SAME_PLACE, SetCharKindaStayInSamePlace); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_AIR, IsCharInAir); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_HEIGHT_ABOVE_GROUND, GetCharHeightAboveGround); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_WEAPON_SKILL, SetCharWeaponSkill); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_CHAR_IN_SPHERE_ONLY_DRUGS_BUYERS, GetRandomCharInSphereOnlyDrugsBuyers); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_HAS_USED_ENTRY_EXIT, SetCharHasUsedEntryExit); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_MAX_HEALTH, SetCharMaxHealth); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_CAN_BE_KNOCKED_OFF_BIKE, SetCharCanBeKnockedOffBike); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_COORDINATES_DONT_WARP_GANG, SetCharCoordinatesDontWarpGang); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_CHAR_IN_SPHERE_NO_BRAIN, GetRandomCharInSphereNoBrain); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_BULLETPROOF_VEST, SetCharBulletproofVest); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_FIRE_DAMAGE_MULTIPLIER, SetCharFireDamageMultiplier); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_USES_UPPERBODY_DAMAGE_ANIMS_ONLY, SetCharUsesUpperbodyDamageAnimsOnly); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_SAY_CONTEXT, SetCharSayContext); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_NAME_OF_ENTRY_EXIT_CHAR_USED, GetNameOfEntryExitCharUsed); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_POSITION_OF_ENTRY_EXIT_CHAR_USED, GetPositionOfEntryExitCharUsed); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_TALKING, IsCharTalking); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_STUCK_UNDER_CAR, IsCharStuckUnderCar); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_KEEP_TASK, SetCharKeepTask); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_SWIMMING, IsCharSwimming); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_CHAR_SWIM_STATE, GetCharSwimState); + //REGISTER_COMMAND_HANDLER(COMMAND_START_CHAR_FACIAL_TALK, StartCharFacialTalk); + //REGISTER_COMMAND_HANDLER(COMMAND_STOP_CHAR_FACIAL_TALK, StopCharFacialTalk); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_COORDINATES_NO_OFFSET, SetCharCoordinatesNoOffset); + //REGISTER_COMMAND_HANDLER(COMMAND_COPY_SHARED_CHAR_DECISION_MAKER, CopySharedCharDecisionMaker); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_FORCE_DIE_IN_CAR, SetCharForceDieInCar); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_USES_COLLISION_CLOSEST_OBJECT_OF_TYPE, SetCharUsesCollisionClosestObjectOfType); + //REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_DRUGGED_UP, SetCharDruggedUp); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_HEAD_MISSING, IsCharHeadMissing); + //REGISTER_COMMAND_HANDLER(COMMAND_IS_CHAR_IN_ANY_TRAIN, IsCharInAnyTrain); + //REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_CHAR_IN_AREA_OFFSET_NO_SAVE, GetRandomCharInAreaOffsetNoSave); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_GRAPHIC_TYPE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STOP_CHAR_DRIVING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OCCUPATION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_GANG_ZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_BLIP_FOR_CHAR_OLD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STILL_ALIVE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_THREAT_SEARCH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_THREAT_REACTION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_NO_OBJ); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ORDER_CHAR_TO_DRIVE_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CHAR_SPOTTED_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ORDER_CHAR_TO_BACKDOOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_CHAR_TO_GANG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_OBJECTIVE_PASSED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_DRIVE_AGGRESSION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_MAX_DRIVESPEED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_CHAR_DO_NOTHING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_INVINCIBLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GUARD_SPOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GUARD_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TURN_CHAR_TO_FACE_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CREATE_CHAR_AS_PASSENGER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_LEAVE_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_DESTROY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GUARD_ATTACK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_AS_LEADER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_THREAT_SEARCH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TURN_CHAR_TO_FACE_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TURN_CHAR_TO_FACE_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STOP_CHAR_LOOKING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_RUN_TO_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_RUN_TO_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_PERSONALITY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_HEED_THREATS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_RANDOM_CHAR_IN_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_WANDER_DIR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_WANDER_RANGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_FOLLOW_PATH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_SET_IDLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_RUNNING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_CHARS_GROUP); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_PLAYERS_GROUP); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_CATCH_TRAIN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_HAIL_TAXI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_WAIT_STATE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_CONTROL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_INCREASE_CHAR_MONEY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_USE_PEDNODE_SEEK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_SAY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_FLEE_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_LYING_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_CEASE_ATTACK_TIMER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_FOOT_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NTH_CLOSEST_CHAR_NODE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_ARMOUR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_AS_PLAYER_FRIEND); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_OBJ_NO_OBJ); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_WANDER_PATH_CLEAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_DROWNING_IN_WATER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_ANSWERING_MOBILE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_WAIT_STATE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_FOLLOW_PATH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STUCK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_ALL_CHAR_ANIMS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHAR_LOOK_AT_OBJECT_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_IN_DISGUISE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_IN_ANGLED_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_ANGLED_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_ANGLED_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_THREAT_LIST); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_THREATS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_ZONE_DISTANCE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLACE_CHAR_AT_ATTRACTOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CHAR_TOUCHING_ANY_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CHAR_AT_SCRIPTED_ATTRACTOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ATTACH_CAMERA_TO_CHAR_LOOK_AT_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_CHAR_FRIENDS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_FRIEND_LIST); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ATTACH_CAMERA_TO_CHAR_LOOK_AT_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CHAR_LIGHTING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_SPECIAL_EVENT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_TYRES_CAN_BE_BURST); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TASK_DRAG_CHAR_FROM_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ATTACH_CHAR_TO_ROPE_FOR_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TASK_KILL_CHAR_ON_FOOT_PATROL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CHAR_SPOTTED_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CHAR_BREATH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_BREATH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CHAR_MAP_ATTRACTOR_STATUS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PICK_UP_CHAR_WITH_WINCH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_BEEN_PHOTOGRAPHED_FLAG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOAD_SHARED_CHAR_DECISION_MAKER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_AIR_RESISTANCE_MULTIPLIER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CHAR_CAN_CLIMB_OUT_WATER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_FORCE_RANDOM_PED_TYPE); +} diff --git a/source/game_sa/Scripts/Commands/Clock.hpp b/source/game_sa/Scripts/Commands/Clock.cpp similarity index 53% rename from source/game_sa/Scripts/Commands/Clock.hpp rename to source/game_sa/Scripts/Commands/Clock.cpp index 57f577e107..c8a32847e3 100644 --- a/source/game_sa/Scripts/Commands/Clock.hpp +++ b/source/game_sa/Scripts/Commands/Clock.cpp @@ -1,4 +1,7 @@ -#pragma once +#include + +#include "./Commands.hpp" +#include #include "CommandParser/Parser.hpp" using namespace notsa::script; @@ -11,24 +14,27 @@ using namespace notsa::script; MultiRet GetTimeOfDay() { return { CClock::ms_nGameClockHours, CClock::ms_nGameClockMinutes }; } -REGISTER_COMMAND_HANDLER(COMMAND_GET_TIME_OF_DAY, GetTimeOfDay); void SetTimeOfDay(uint8 hours, uint8 minutes) { CClock::SetGameClock(hours, minutes, 0); } -REGISTER_COMMAND_HANDLER(COMMAND_SET_TIME_OF_DAY, SetTimeOfDay); uint16 GetMinutesToTimeOfDay(uint8 hours, uint8 minutes) { return CClock::GetGameClockMinutesUntil(hours, minutes); } -REGISTER_COMMAND_HANDLER(COMMAND_GET_MINUTES_TO_TIME_OF_DAY, GetMinutesToTimeOfDay); void StoreClock() { CClock::StoreClock(); } -REGISTER_COMMAND_HANDLER(COMMAND_STORE_CLOCK, StoreClock); void RestoreClock() { CClock::RestoreClock(); } -REGISTER_COMMAND_HANDLER(COMMAND_RESTORE_CLOCK, RestoreClock); + +void notsa::script::commands::clock::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_GET_TIME_OF_DAY, GetTimeOfDay); + REGISTER_COMMAND_HANDLER(COMMAND_SET_TIME_OF_DAY, SetTimeOfDay); + REGISTER_COMMAND_HANDLER(COMMAND_GET_MINUTES_TO_TIME_OF_DAY, GetMinutesToTimeOfDay); + REGISTER_COMMAND_HANDLER(COMMAND_STORE_CLOCK, StoreClock); + REGISTER_COMMAND_HANDLER(COMMAND_RESTORE_CLOCK, RestoreClock); +} diff --git a/source/game_sa/Scripts/Commands/Commands.hpp b/source/game_sa/Scripts/Commands/Commands.hpp new file mode 100644 index 0000000000..932b022238 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Commands.hpp @@ -0,0 +1,27 @@ +#pragma once + +namespace notsa { +namespace script { +namespace commands { +namespace basic { void RegisterHandlers(); }; +namespace camera { void RegisterHandlers(); }; +namespace character { void RegisterHandlers(); }; +namespace clock { void RegisterHandlers(); }; +namespace comparasion { void RegisterHandlers(); }; +namespace game { void RegisterHandlers(); }; +namespace generic { void RegisterHandlers(); }; +namespace math { void RegisterHandlers(); }; +namespace mission { void RegisterHandlers(); }; +namespace object { void RegisterHandlers(); }; +namespace pad { void RegisterHandlers(); }; +namespace ped { void RegisterHandlers(); }; +namespace player { void RegisterHandlers(); }; +namespace script { void RegisterHandlers(); }; +namespace sequence { void RegisterHandlers(); }; +namespace text { void RegisterHandlers(); }; +namespace unused { void RegisterHandlers(); }; +namespace utility { void RegisterHandlers(); }; +namespace vehicle { void RegisterHandlers(); }; +}; // namespace commands +}; // namespace notsa +}; // namespace script diff --git a/source/game_sa/Scripts/Commands/Comparasion.cpp b/source/game_sa/Scripts/Commands/Comparasion.cpp new file mode 100644 index 0000000000..6fb8a4729a --- /dev/null +++ b/source/game_sa/Scripts/Commands/Comparasion.cpp @@ -0,0 +1,11 @@ +#include + +#include "./Commands.hpp" +#include + +/*! +* Comparasion commands +*/ + +void notsa::script::commands::comparasion::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/Comparasion.hpp b/source/game_sa/Scripts/Commands/Comparasion.hpp deleted file mode 100644 index 7d1e52e7c1..0000000000 --- a/source/game_sa/Scripts/Commands/Comparasion.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -/*! -* Comparasion commands -*/ diff --git a/source/game_sa/Scripts/Commands/Game.cpp b/source/game_sa/Scripts/Commands/Game.cpp new file mode 100644 index 0000000000..249eddb4a4 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Game.cpp @@ -0,0 +1,376 @@ +#include + +#include "./Commands.hpp" +#include +#include "AudioEngine.h" +#include "CullZones.h" +#include "MenuManager.h" +#include "World.h" +#include "Stats.h" +#include "TaskComplexProstituteSolicit.h" +#include "Population.h" +#include "PostEffects.h" +#include "Restart.h" +#include "Garages.h" +#include "MenuSystem.h" +#include "CommandParser/Parser.hpp" + +/*! +* Various game realted commands +*/ + +void SetAllTaxisHaveNitro(bool enabled) { + (enabled ? CCheat::ApplyCheat : CCheat::Disable)(CHEAT_ALL_TAXIS_NITRO); +} + +void ActivatePimpCheat(bool enabled) { + (enabled ? CCheat::ApplyCheat : CCheat::Disable)(CHEAT_PROSTITUTES_PAY_YOU); +} + +bool AreAnyCarCheatsActivated() { + return rng::any_of(std::array{CHEAT_PERFECT_HANDLING, CHEAT_CARS_ON_WATER, CHEAT_BOATS_FLY}, CCheat::IsActive); +} + +void FailCurrentMission() { + CTheScripts::FailCurrentMission = 2; +} + +float GetGroundZFor3DCoord(CVector coord) { + return CWorld::FindGroundZFor3DCoord(coord, nullptr, nullptr); +} + +void PlayerMadeProgress() { + return CStats::IncrementStat(STAT_PROGRESS_MADE); +} + +void RegisterMissionGiven() { + CStats::IncrementStat(STAT_MISSION_ATTEMPTS); +} + +void SetPedDensityMultiplier(float mult) { + CPopulation::PedDensityMultiplier = mult; +} + +bool IsJapaneseVersion() { + return false; // TODO: make this configurable perhaps? +} + +bool IsWidescreenOnInOptions() { + return FrontEndMenuManager.m_bWidescreenOn; +} + +void GetRidOfPlayerProstitute() { + CTaskComplexProstituteSolicit::GetRidOfPlayerProstitute(); +} + +void SetAreaName(const char* key) { + CHud::SetZoneName(TheText.Get(key), true); +} + +void SetRadioToPlayersFavorite() { + AudioEngine.RetuneRadio(CStats::FindMostFavoriteRadioStation()); +} + +void SetDisableMilitaryZones(bool disable) { + CCullZones::bMilitaryZonesDisabled = disable; +} + +void IncrementFloatStatNoMessage(eStats stat, float value) { + CStats::IncrementStat(stat, value); +} + +void TakePhoto(bool save) { + CWeapon::ms_bTakePhoto = true; + CPostEffects::m_bSavePhotoFromScript = save; +} + +void SetNoResprays(bool enabled) { + CGarages::NoResprays = enabled; + CGarages::AllRespraysCloseOrOpen(enabled); +} + +void SetRespawnPointForDurationOfMission(CVector point) { + CRestart::SetRespawnPointForDurationOfMission(point); +} + +eLanguage GetCurrentLanguage() { + return FrontEndMenuManager.m_nPrefsLanguage; +} + +void DoWeaponStuffAtStartOf2PlayerGame() { + CGameLogic::DoWeaponStuffAtStartOf2PlayerGame(true); +} + +void DisplayRadar(bool enable) { + // izzotop: CTheScripts::HideAllFrontEndMapBlips = enable; + CHud::bScriptDontDisplayRadar = !enable; +} + +void RegisterBestPosition(uint8 stat, int32 position) { + CStats::RegisterBestPosition(static_cast(stat), position); +} + +void SetMenuHeaderOrientation() { + // NOP + + // CMenuSystem::SetHeaderOrientation(); +} + +void SetFloatStat(eStats stat, float value) { + CStats::SetStatValue(stat, value); + CStats::DisplayScriptStatUpdateMessage(STAT_UPDATE_INCREASE, stat, value); +} + +// vvv Originally unsupported +void RegisterJumpDistance(float distance) { + CStats::SetStatValue(STAT_MAXIMUM_INSANE_JUMP_DISTANCE, std::max( + CStats::GetStatValue(STAT_MAXIMUM_INSANE_JUMP_DISTANCE), + distance + )); +} + +void RegisterJumpHeight(float height) { + CStats::SetStatValue(STAT_MAXIMUM_INSANE_JUMP_FLIPS, std::max( + CStats::GetStatValue(STAT_MAXIMUM_INSANE_JUMP_FLIPS), + height + )); +} + +void RegisterJumpSpins(float rotation) { + CStats::SetStatValue(STAT_MAXIMUM_INSANE_JUMP_ROTATION, std::max( + CStats::GetStatValue(STAT_MAXIMUM_INSANE_JUMP_ROTATION), + rotation + )); +} + +void RegisterJumpStunt(float stunt) { + CStats::SetStatValue(STAT_BEST_INSANE_STUNT_AWARDED, std::max( + CStats::GetStatValue(STAT_BEST_INSANE_STUNT_AWARDED), + stunt + )); +} + +void RegisterUniqueJumpFound() { + CStats::IncrementStat(STAT_UNIQUE_JUMPS_FOUND); +} + +void SetUniqueJumpsTotal(float amount) { + CStats::SetStatValue(STAT_UNIQUE_JUMPS_DONE, amount); +} + +void RegisterPassengerDroppedOffTaxi() { + CStats::IncrementStat(STAT_PASSENGERS_DROPPED_OFF); +} + +void RegisterMoneyMadeTaxi(float amount) { + CStats::SetStatValue(STAT_CASH_MADE_IN_A_TAXI, amount); +} + +void SaveIntToDebugFile(int32 value) { /* DEBUG */ } +void SaveFloatToDebugFile(float value) { /* DEBUG */ } + +void notsa::script::commands::game::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_SET_ALL_TAXIS_HAVE_NITRO, SetAllTaxisHaveNitro); + REGISTER_COMMAND_HANDLER(COMMAND_ACTIVATE_PIMP_CHEAT, ActivatePimpCheat); + REGISTER_COMMAND_HANDLER(COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED, AreAnyCarCheatsActivated); + REGISTER_COMMAND_HANDLER(COMMAND_FAIL_CURRENT_MISSION, FailCurrentMission); + REGISTER_COMMAND_HANDLER(COMMAND_GET_GROUND_Z_FOR_3D_COORD, GetGroundZFor3DCoord); + REGISTER_COMMAND_HANDLER(COMMAND_PLAYER_MADE_PROGRESS, PlayerMadeProgress); + REGISTER_COMMAND_HANDLER(COMMAND_REGISTER_MISSION_GIVEN, RegisterMissionGiven); + REGISTER_COMMAND_HANDLER(COMMAND_SET_PED_DENSITY_MULTIPLIER, SetPedDensityMultiplier); + REGISTER_COMMAND_HANDLER(COMMAND_IS_JAPANESE_VERSION, IsJapaneseVersion); + REGISTER_COMMAND_HANDLER(COMMAND_IS_WIDESCREEN_ON_IN_OPTIONS, IsWidescreenOnInOptions); + REGISTER_COMMAND_HANDLER(COMMAND_GET_RID_OF_PLAYER_PROSTITUTE, GetRidOfPlayerProstitute); + REGISTER_COMMAND_HANDLER(COMMAND_SET_AREA_NAME, SetAreaName); + REGISTER_COMMAND_HANDLER(COMMAND_SET_RADIO_TO_PLAYERS_FAVOURITE_STATION, SetRadioToPlayersFavorite); + REGISTER_COMMAND_HANDLER(COMMAND_SET_DISABLE_MILITARY_ZONES, SetDisableMilitaryZones); + REGISTER_COMMAND_HANDLER(COMMAND_INCREMENT_FLOAT_STAT_NO_MESSAGE, IncrementFloatStatNoMessage); + REGISTER_COMMAND_HANDLER(COMMAND_INCREMENT_INT_STAT_NO_MESSAGE, IncrementFloatStatNoMessage); + REGISTER_COMMAND_HANDLER(COMMAND_TAKE_PHOTO, TakePhoto); + REGISTER_COMMAND_HANDLER(COMMAND_SET_NO_RESPRAYS, SetNoResprays); + REGISTER_COMMAND_HANDLER(COMMAND_SET_RESPAWN_POINT_FOR_DURATION_OF_MISSION, SetRespawnPointForDurationOfMission); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CURRENT_LANGUAGE, GetCurrentLanguage); + REGISTER_COMMAND_HANDLER(COMMAND_DO_WEAPON_STUFF_AT_START_OF_2P_GAME, DoWeaponStuffAtStartOf2PlayerGame); + REGISTER_COMMAND_HANDLER(COMMAND_DISPLAY_RADAR, DisplayRadar); + REGISTER_COMMAND_HANDLER(COMMAND_REGISTER_BEST_POSITION, RegisterBestPosition); + REGISTER_COMMAND_HANDLER(COMMAND_SET_MENU_HEADER_ORIENTATION, SetMenuHeaderOrientation); + REGISTER_COMMAND_HANDLER(COMMAND_SET_FLOAT_STAT, SetFloatStat); + + REGISTER_COMMAND_HANDLER(COMMAND_SAVE_INT_TO_DEBUG_FILE, SaveIntToDebugFile); + REGISTER_COMMAND_HANDLER(COMMAND_SAVE_FLOAT_TO_DEBUG_FILE, SaveFloatToDebugFile); + + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_JUMP_DISTANCE, RegisterJumpDistance); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_JUMP_HEIGHT, RegisterJumpHeight); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_JUMP_SPINS, RegisterJumpSpins); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_JUMP_STUNT, RegisterJumpStunt); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_UNIQUE_JUMP_FOUND, RegisterUniqueJumpFound); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_SET_UNIQUE_JUMPS_TOTAL, SetUniqueJumpsTotal); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI, RegisterPassengerDroppedOffTaxi); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_REGISTER_MONEY_MADE_TAXI, RegisterMoneyMadeTaxi); + + // VC leftovers + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_SWAT_REQUIRED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_FBI_REQUIRED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ARMY_REQUIRED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_POWER_PILL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_ICECREAM_JINGLE_ON); + // III leftovers + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_PACMAN_RACE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_PACMAN_RECORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_PACMAN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_PACMAN_SCRAMBLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MESSAGE_WAIT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COMEDY_CONTROLS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ACTIVATE_CRUSHER_CRANE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_2_NUMBERS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_2_NUMBERS_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_3_NUMBERS_NOW); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_3_NUMBERS_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_4_NUMBERS_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_5_NUMBERS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_5_NUMBERS_NOW); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_5_NUMBERS_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_6_NUMBERS_NOW); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_6_NUMBERS_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ZONE_GROUP); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_DRUG_RUN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SAVE_PLAYER_FROM_FIRES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TEXT_BACKGROUND_COLOUR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_INDUSTRIAL_PASSED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_COMMERCIAL_PASSED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SUBURBAN_PASSED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_NASTY_GAME); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_UNDRESS_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DRESS_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_CHASE_SCENE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STOP_CHASE_SCENE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_EXPLOSION_IN_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_EXPLOSION_IN_ZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_DRUG_DROP_OFF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_FIND_DROP_OFF_PLANE_COORDINATES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CREATE_FLOATING_PACKAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_AUDIO_STREAM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_3_NUMBERS_BIG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_4_NUMBERS_BIG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_5_NUMBERS_BIG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_WITH_6_NUMBERS_BIG); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_MOTION_BLUR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_STRING_IN_STRING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_2_REPEATED_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_2_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_3_REPEATED_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_3_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_4_REPEATED_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_4_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_SNIPER_BULLET_IN_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_STRING_IN_STRING_SOON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_5_REPEATED_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_5_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_6_REPEATED_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_6_PHONE_MESSAGES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_PED_ROADS_ON_ANGLED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_PED_ROADS_OFF_ANGLED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_ROADS_ON_ANGLED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_ROADS_OFF_ANGLED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CRANE_LIFTING_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_REMOVE_ROUTE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_CATALINA_HELI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CATALINA_HELI_TAKE_OFF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_REMOVE_CATALINA_HELI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_REMOVE_ALL_PLAYER_WEAPONS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GRAB_CATALINA_HELI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ROTATING_GARAGE_DOOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CATALINA_HELI_FLY_AWAY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PHONE_DISPLAYING_MESSAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_BLIP_FOR_PICKUP_OLD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_COLLECTABLE1S_COLLECTED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_0); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_1); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_2); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_3); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_4); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_5); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_6); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SPECIAL_7); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PRINT_SOON); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_AMMO_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_AMMO_TO_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STILL_ALIVE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ORDER_DRIVER_OUT_OF_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_PATROL_POINT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_GANGZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_INVINCIBLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_GRAPHIC_TYPE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_PLAYER_BEEN_ARRESTED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHANGE_CAR_LOCK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SHAKE_CAM_WITH_POINT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_REMAP); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CAR_JUST_SUNK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_NO_COLLIDE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_DEAD_IN_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_DEAD_IN_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_TRAILER_ATTACHED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_ON_TRAILER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CAR_GOT_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PARK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_PARK_FINISHED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_KILL_ALL_PASSENGERS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_BULLETPROOF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_FLAMEPROOF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_ROCKETPROOF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CARBOMB_ACTIVE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_CAR_ALARM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PUT_CAR_ON_TRAILER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_CRUSHED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CREATE_GANG_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_PAGER_MESSAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DISPLAY_ONSCREEN_COUNTER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ZONE_CAR_INFO); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_DENSITY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PED_DENSITY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_POINT_CAMERA_AT_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SHAKE_PAD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ZONE_PED_INFO); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CAR_IN_AIR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_BLIP_FOR_OBJECT_OLD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DRAW_SHADOW); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_AMMO); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_AMMO); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOAD_CAMERA_SPLINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MOVE_CAMERA_ALONG_SPLINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_XBOX_PLAYER2_PRESSING_START); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_FINISHED_WITH_XBOX_PLAYER2); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RESTORE_PLAYER_AFTER_2P_GAME); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_2PLAYER_GAME_GOING_ON); +} diff --git a/source/game_sa/Scripts/Commands/Game.hpp b/source/game_sa/Scripts/Commands/Game.hpp deleted file mode 100644 index 1ec182f9a1..0000000000 --- a/source/game_sa/Scripts/Commands/Game.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "CommandParser/Parser.hpp" - -/*! -* Various game realted commands -*/ - -void SetAllTaxisHaveNitro(bool enabled) { - (enabled ? CCheat::ApplyCheat : CCheat::Disable)(CHEAT_ALL_TAXIS_NITRO); -} -REGISTER_COMMAND_HANDLER(COMMAND_SET_ALL_TAXIS_HAVE_NITRO, SetAllTaxisHaveNitro); - diff --git a/source/game_sa/Scripts/Commands/Generic.hpp b/source/game_sa/Scripts/Commands/Generic.cpp similarity index 65% rename from source/game_sa/Scripts/Commands/Generic.hpp rename to source/game_sa/Scripts/Commands/Generic.cpp index f4600f93af..0ed6a12198 100644 --- a/source/game_sa/Scripts/Commands/Generic.hpp +++ b/source/game_sa/Scripts/Commands/Generic.cpp @@ -1,4 +1,7 @@ -#include "StdInc.h" +#include + +#include "./Commands.hpp" +#include #include "TaskSimplePlayerOnFoot.h" #include "eScriptCommands.h" #include "RunningScript.h" @@ -9,3 +12,6 @@ #include "TheCarGenerators.h" #include "FireManager.h" #include "CommandParser/Parser.hpp" + +void notsa::script::commands::generic::RegisterHandlers() { +} diff --git a/source/game_sa/Scripts/Commands/Math.cpp b/source/game_sa/Scripts/Commands/Math.cpp new file mode 100644 index 0000000000..83e7d0f654 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Math.cpp @@ -0,0 +1,47 @@ +#include + +#include "./Commands.hpp" +#include +#include "CommandParser/Parser.hpp" + +/*! +* Various utility commands +*/ + +auto GetDistanceBetweenCoords2d(CVector2D a, CVector2D b) { + return DistanceBetweenPoints2D(a, b); +} + +auto GetDistanceBetweenCoords3d(CVector a, CVector b) { + return DistanceBetweenPoints(a, b); +} + +auto GenerateRandomInt(int32& var) { + var = CGeneral::GetRandomNumber(); +} + +auto GenerateRandomIntInRange(int32 a, int32 b) { + return CGeneral::GetRandomNumberInRange(a, b); +} + +auto GenerateRandomFloatInRange(float a, float b) { + return CGeneral::GetRandomNumberInRange(a, b); +} + +auto Sin(float deg) { + return std::sinf(DegreesToRadians(deg)); +} + +auto Cos(float deg) { + return std::cosf(DegreesToRadians(deg)); +} + +void notsa::script::commands::math::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D, GetDistanceBetweenCoords2d); + REGISTER_COMMAND_HANDLER(COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D, GetDistanceBetweenCoords3d); + REGISTER_COMMAND_HANDLER(COMMAND_GENERATE_RANDOM_INT, GenerateRandomInt); + REGISTER_COMMAND_HANDLER(COMMAND_GENERATE_RANDOM_INT_IN_RANGE, GenerateRandomIntInRange); + REGISTER_COMMAND_HANDLER(COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE, GenerateRandomFloatInRange); + REGISTER_COMMAND_HANDLER(COMMAND_SIN, Sin); + REGISTER_COMMAND_HANDLER(COMMAND_COS, Cos); +} diff --git a/source/game_sa/Scripts/Commands/Math.hpp b/source/game_sa/Scripts/Commands/Math.hpp deleted file mode 100644 index 99e3a6d592..0000000000 --- a/source/game_sa/Scripts/Commands/Math.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -/*! -* Various utility commands -*/ - -void GenerateRandomInt(CRunningScript* S) { - S->GetPointerToScriptVariable(VAR_GLOBAL)->iParam = CGeneral::GetRandomNumber(); -} -REGISTER_COMMAND_HANDLER(COMMAND_GENERATE_RANDOM_INT, GenerateRandomInt); diff --git a/source/game_sa/Scripts/Commands/Mission.hpp b/source/game_sa/Scripts/Commands/Mission.cpp similarity index 78% rename from source/game_sa/Scripts/Commands/Mission.hpp rename to source/game_sa/Scripts/Commands/Mission.cpp index cb94d0e8a3..4585fbb1c6 100644 --- a/source/game_sa/Scripts/Commands/Mission.hpp +++ b/source/game_sa/Scripts/Commands/Mission.cpp @@ -1,6 +1,9 @@ -#pragma once +#include -#include "CommandParser/Parser.hpp" +#include "./Commands.hpp" +#include + +#include /*! * Various mission commands @@ -17,7 +20,6 @@ void LoadAndLaunchMissionInternal(int32 missionId) { CTimer::Suspend(); - const auto StartScriptFromFile = [missionId](const char* filePath) { if (auto* file = CFileMgr::OpenFile(filePath, "rb")) { const int32 offsetToMission = CTheScripts::MultiScriptArray[missionId]; @@ -56,21 +58,27 @@ void LoadAndLaunchMissionInternal(int32 missionId) { CTimer::Resume(); } -REGISTER_COMMAND_HANDLER(COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, LoadAndLaunchMissionInternal); void ScriptName(CRunningScript* S, std::string_view name) { char lowered[8]{0}; rng::transform(name, lowered, [](char c) { return (char)std::tolower(c); }); S->SetName(name); } -REGISTER_COMMAND_HANDLER(COMMAND_SCRIPT_NAME, ScriptName); int32 StartScriptFire(CVector pos, int8 propagation, int32 size) { return gFireManager.StartScriptFire(pos, nullptr, 0.8f, 1, propagation, size); } -REGISTER_COMMAND_HANDLER(COMMAND_START_SCRIPT_FIRE, StartScriptFire); void LaunchMission(uint32 label) { CTheScripts::StartNewScript(&CTheScripts::ScriptSpace[label]); } -REGISTER_COMMAND_HANDLER(COMMAND_LAUNCH_MISSION, LaunchMission); + +void notsa::script::commands::mission::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, LoadAndLaunchMissionInternal); + REGISTER_COMMAND_HANDLER(COMMAND_SCRIPT_NAME, ScriptName); + REGISTER_COMMAND_HANDLER(COMMAND_START_SCRIPT_FIRE, StartScriptFire); + REGISTER_COMMAND_HANDLER(COMMAND_LAUNCH_MISSION, LaunchMission); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CREATE_CUTSCENE_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CUTSCENE_ANIM); +} diff --git a/source/game_sa/Scripts/Commands/Object.cpp b/source/game_sa/Scripts/Commands/Object.cpp new file mode 100644 index 0000000000..3b09d6fd74 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Object.cpp @@ -0,0 +1,296 @@ +#include + +#include "./Commands.hpp" +#include + +#include "Object.h" +#include "SetPieces.h" +#include "Fx.h" +#include "PointLights.h" +#include "CommandParser/Parser.hpp" + +using namespace notsa; +using namespace notsa::script; +/*! +* Various object commands +*/ + +namespace Object { +CObject& CreateObject(CRunningScript& S, script::Model model, CVector posn) { + const auto mi = CModelInfo::GetModelInfo(model); + auto* object = CObject::Create(model, false); + CWorld::PutToGroundIfTooLow(posn); + posn.z += object->GetDistanceFromCentreOfMassToBaseOfModel(); + object->SetPosn(posn); + object->SetOrientation(CVector{0.0f}); + object->UpdateRW(); + object->UpdateRwFrame(); + if (mi->AsLodAtomicModelInfoPtr()) { + object->SetupBigBuilding(); + } + + CTheScripts::ClearSpaceForMissionEntity(posn, object); + CWorld::Add(object); + + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.AddEntityToList(*object); + } + return *object; +} + +void RemoveObject(CRunningScript& S, CObject& object) { + CWorld::Remove(&object); + CWorld::RemoveReferencesToDeletedObject(&object); + delete &object; + + if (S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.RemoveEntityFromList(object); + } +} + +bool DoesObjectExists(CObject* object) { + return object != nullptr; +} + +void MarkObjectNoLongerNeeded(CRunningScript& S, CObject* object) { + CTheScripts::CleanUpThisObject(object); + + if (object && S.m_bUseMissionCleanup) { + CTheScripts::MissionCleanUp.RemoveEntityFromList(*object); + } +} + +void DoNotRemoveObject(CObject& object) { + CTheScripts::MissionCleanUp.RemoveEntityFromList(object); +} + +CVector GetObjectCoordinates(CObject& object) { + return object.GetPosition(); +} + +void SetObjectCoordinates(CObject& object, CVector posn) { + object.SetPosn(posn); +} + +CVector GetObjectVelocity(CObject& object) { + return object.GetMoveSpeed() * 50.0f; +} + +void SetObjectVelocity(CObject& object, CVector velocity) { + object.SetVelocity(velocity / 50.0f); +} + +float GetObjectHeading(CObject& object) { + return FixAngleDegrees(RadiansToDegrees(object.GetHeading())); +} + +void SetObjectHeading(CObject& object, float heading) { + object.SetHeading(DegreesToRadians(FixAngleDegrees(heading))); +} + +void SetObjectScale(CObject& object, float scale) { + object.m_fScale = scale; +} + +void SetObjectCollision(CObject& object, bool enable) { + object.m_bUsesCollision = enable; +} + +bool DoesObjectHaveThisModel(CObject& object, script::Model model) { + return object.m_nModelIndex == model; +} +} // namespace Object + +namespace Model { +void LoadModel(CRunningScript* S, script::Model model) { + CStreaming::RequestModel(model, STREAMING_MISSION_REQUIRED | STREAMING_KEEP_IN_MEMORY); + CTheScripts::ScriptResourceManager.AddToResourceManager(model, RESOURCE_TYPE_MODEL_OR_SPECIAL_CHAR, S); +} + +bool HasModelLoaded(script::Model model) { + return CStreaming::IsModelLoaded(model); +} + +void MarkModelNotNeeded(CRunningScript* S, script::Model model) { + if (CTheScripts::ScriptResourceManager.RemoveFromResourceManager(model, RESOURCE_TYPE_MODEL_OR_SPECIAL_CHAR, S)) { + CStreaming::SetMissionDoesntRequireModel(model); + } +} + +void LoadAllModelsNow() { + CTimer::Suspend(); + CStreaming::LoadAllRequestedModels(false); + CTimer::Resume(); +} + +bool IsModelAvailable(eModelID model) { + return CModelInfo::GetModelInfo(model) != nullptr; +} +} // namespace Model + +namespace Fx { +void DrawLight(CVector pos, CVector color) { + color /= 255.0f; + CPointLights::AddLight(ePointLightType::PLTYPE_POINTLIGHT, pos, {}, 12.0f, color.x, color.y, color.z, 0, false, nullptr); +} + +void AddBigGunFlash(CVector origin, CVector target) { + CPointLights::AddLight(ePointLightType::PLTYPE_POINTLIGHT, origin, {}, 5.0f, 1.0f, 0.8f, 0.0f, 0, false, nullptr); + g_fx.TriggerGunshot(nullptr, origin, (target - origin).Normalized(), true); +} +} // namespace Fx + +namespace Attractor { +void EnableDisabledAttractorOnObject(CObject& object, bool enabled) { + object.objectFlags.bEnableDisabledAttractors = enabled; // todo: rename obj->objectFlags.b0x1000000 +} +} // namespace Attractor + +namespace Animation { +void SetObjectAnimSpeed(CObject& obj, const char* animName, float speed) { + if (const auto anim = RpAnimBlendClumpGetAssociation(obj.m_pRwClump, animName)) { + anim->m_fSpeed = speed; + } +} + +bool IsObjectPlayingAnim(CObject& obj, const char* animName) { + if (!obj.m_pRwClump) { + return false; + } + if (RwObjectGetType(obj.m_pRwClump) != rpCLUMP) { + return false; + } + if (!RpAnimBlendClumpIsInitialized(obj.m_pRwClump)) { + return false; + } + return RpAnimBlendClumpGetAssociation(obj.m_pRwClump, animName) != nullptr; +} + +auto GetObjectAnimCurrentTime(CObject& obj, const char* animName) { + if (const auto anim = RpAnimBlendClumpGetAssociation(obj.m_pRwClump, animName)) { + return anim->m_fCurrentTime / anim->m_pHierarchy->m_fTotalTime; + } + return 0.f; +} + +auto SetObjectAnimCurrentTime(CObject& obj, const char* animName, float progress) { + if (const auto anim = RpAnimBlendClumpGetAssociation(obj.m_pRwClump, animName)) { + anim->SetCurrentTime(anim->m_fSpeed * progress); + } +} +} // namespace Animation + +void notsa::script::commands::object::RegisterHandlers() { + using namespace Object; + using namespace Model; + using namespace Fx; + using namespace Attractor; + using namespace Animation; + + REGISTER_COMMAND_HANDLER(COMMAND_CREATE_OBJECT, CreateObject); + REGISTER_COMMAND_HANDLER(COMMAND_DELETE_OBJECT, RemoveObject); + REGISTER_COMMAND_HANDLER(COMMAND_DOES_OBJECT_EXIST, DoesObjectExists); + REGISTER_COMMAND_HANDLER(COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED, MarkObjectNoLongerNeeded); + REGISTER_COMMAND_HANDLER(COMMAND_DONT_REMOVE_OBJECT, DoNotRemoveObject); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_COORDINATES, GetObjectCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_COORDINATES, SetObjectCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_VELOCITY, GetObjectVelocity); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_VELOCITY, SetObjectVelocity); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_HEADING, GetObjectHeading); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_HEADING, SetObjectHeading); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_SCALE, SetObjectScale); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_COLLISION, SetObjectCollision); + REGISTER_COMMAND_HANDLER(COMMAND_DOES_OBJECT_HAVE_THIS_MODEL, DoesObjectHaveThisModel); + + REGISTER_COMMAND_HANDLER(COMMAND_REQUEST_MODEL, LoadModel); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_MODEL_LOADED, HasModelLoaded); + REGISTER_COMMAND_HANDLER(COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED, MarkModelNotNeeded); + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_ALL_MODELS_NOW, LoadAllModelsNow); + REGISTER_COMMAND_HANDLER(COMMAND_IS_MODEL_AVAILABLE, IsModelAvailable); + + REGISTER_COMMAND_HANDLER(COMMAND_ADD_SET_PIECE, CSetPieces::AddOne); + REGISTER_COMMAND_HANDLER(COMMAND_DRAW_LIGHT, DrawLight); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_BIG_GUN_FLASH, AddBigGunFlash); + + REGISTER_COMMAND_HANDLER(COMMAND_ENABLE_DISABLED_ATTRACTORS_ON_OBJECT, EnableDisabledAttractorOnObject); + + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_ANIM_SPEED, SetObjectAnimSpeed); + REGISTER_COMMAND_HANDLER(COMMAND_IS_OBJECT_PLAYING_ANIM, IsObjectPlayingAnim); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_ANIM_CURRENT_TIME, GetObjectAnimCurrentTime); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_ANIM_CURRENT_TIME, SetObjectAnimCurrentTime); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_FLASH_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_NO_OBJ); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GUARD_SPOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GUARD_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_WAIT_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_LEAVE_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_DESTROY_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_DESTROY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GUARD_ATTACK); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_RUN_TO_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_RUN_TO_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_PEDS_IN_AREA_TO_COLL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_COLL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_IN_CARS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ANY_MEANS_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_IN_CAR_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_IN_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_STOPPED_IN_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUMBER_OF_PEDS_IN_COLL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_PARTICLE_EFFECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_SPRITE_BLIP_FOR_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_SPRITE_BLIP_FOR_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DROP_MINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DROP_NAUTICAL_MINE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOAD_SPECIAL_MODEL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CREATE_CUTSCENE_HEAD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CUTSCENE_HEAD_ANIM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_CATCH_TRAIN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_MOVING_PARTICLE_EFFECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_COLLISION_IN_MEMORY); +} diff --git a/source/game_sa/Scripts/Commands/Pad.cpp b/source/game_sa/Scripts/Commands/Pad.cpp new file mode 100644 index 0000000000..12715bfb96 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Pad.cpp @@ -0,0 +1,40 @@ +#include + +#include "./Commands.hpp" +#include +#include "MenuManager.h" +#include "Pad.h" +#include "CommandParser/Parser.hpp" +using namespace notsa::script; +/*! +* Various player pad commands +*/ + +bool IsButtonPressed(CRunningScript* S, int16 playerIndex, eButtonId button) { + return S->GetPadState(playerIndex, button) && !CPad::GetPad(0)->JustOutOfFrontEnd; +} + +MultiRet GetPCMouseMovement() { + // TODO(izzotop): check ASM + return {CPad::NewMouseControllerState.X, CPad::NewMouseControllerState.Y}; +} + +bool IsPCUsingJoyPad() { + return FrontEndMenuManager.m_nController != 0; +} + +bool IsMouseUsingVerticalInversion() { + return CMenuManager::bInvertMouseY != 0; +} + +bool HasGameJustReturnedFromFrontend() { + return CPad::GetPad(0)->JustOutOfFrontEnd; +} + +void notsa::script::commands::pad::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_IS_BUTTON_PRESSED, IsButtonPressed); + REGISTER_COMMAND_HANDLER(COMMAND_GET_PC_MOUSE_MOVEMENT, GetPCMouseMovement); + REGISTER_COMMAND_HANDLER(COMMAND_IS_PC_USING_JOYPAD, IsPCUsingJoyPad); + REGISTER_COMMAND_HANDLER(COMMAND_IS_MOUSE_USING_VERTICAL_INVERSION, IsMouseUsingVerticalInversion); + REGISTER_COMMAND_HANDLER(COMMAND_HAS_GAME_JUST_RETURNED_FROM_FRONTEND, HasGameJustReturnedFromFrontend); +} diff --git a/source/game_sa/Scripts/Commands/Pad.hpp b/source/game_sa/Scripts/Commands/Pad.hpp deleted file mode 100644 index 4ae89567d1..0000000000 --- a/source/game_sa/Scripts/Commands/Pad.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -/*! -* Various player pad commands -*/ - -/**/ -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x00E1 (Pad touch, TouchPoints touchPoints) - CollectParameters(2); - return OR_CONTINUE; -} - -/* -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0AB0 (KeyCode keyCode) - CollectParameters(1); - return OR_CONTINUE; -} -*/ diff --git a/source/game_sa/Scripts/Commands/Ped.cpp b/source/game_sa/Scripts/Commands/Ped.cpp new file mode 100644 index 0000000000..52c514923b --- /dev/null +++ b/source/game_sa/Scripts/Commands/Ped.cpp @@ -0,0 +1,32 @@ +#include + +#include "./Commands.hpp" +#include +#include "Ped.h" +#include "CommandParser/Parser.hpp" +/*! +* Various ped commands +*/ + +void SetDeathWeaponPersist(CPed& ped, bool enable) { + ped.bDeathPickupsPersist = enable; +} + +void IgnoreHeightDifferenceFollowingNodes(CPed& ped, bool ignore) { + ped.bIgnoreHeightDifferenceFollowingNodes = ignore; +} + +void ShutAllCharsUp(bool disable) { + (disable ? CAEPedSpeechAudioEntity::DisableAllPedSpeech : CAEPedSpeechAudioEntity::EnableAllPedSpeech)(); +} + +void SetCharGetOutUpsideDownCar(CPed& ped, bool enable) { + ped.bGetOutUpsideDownCar = enable; +} + +void notsa::script::commands::ped::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_SET_DEATH_WEAPONS_PERSIST, SetDeathWeaponPersist); + REGISTER_COMMAND_HANDLER(COMMAND_IGNORE_HEIGHT_DIFFERENCE_FOLLOWING_NODES, IgnoreHeightDifferenceFollowingNodes); + REGISTER_COMMAND_HANDLER(COMMAND_SHUT_ALL_CHARS_UP, ShutAllCharsUp); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CHAR_GET_OUT_UPSIDE_DOWN_CAR, SetCharGetOutUpsideDownCar); +} diff --git a/source/game_sa/Scripts/Commands/Player.cpp b/source/game_sa/Scripts/Commands/Player.cpp new file mode 100644 index 0000000000..c0d5f25330 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Player.cpp @@ -0,0 +1,285 @@ +#include + +#include "./Commands.hpp" +#include + +#include +#include + +#include + +using namespace notsa::script; +/*! +* Various player commands +*/ + +/*! +* @brief Create a player at the given world position +* @param playerId Player's id (0 or 1) +* @param pos World position +*/ +int32 CreatePlayer(int32 playerId, CVector pos) { + if (!CStreaming::IsModelLoaded(MODEL_PLAYER)) { + CStreaming::RequestSpecialModel(0, "player", STREAMING_GAME_REQUIRED | STREAMING_KEEP_IN_MEMORY | STREAMING_PRIORITY_REQUEST); + CStreaming::LoadAllRequestedModels(true); + } + + // Create + CPlayerPed::SetupPlayerPed(playerId); + auto player = FindPlayerPed(playerId); + player->SetCharCreatedBy(PED_MISSION); + CPlayerPed::DeactivatePlayerPed(playerId); + + // Position in the world + if (pos.z <= MAP_Z_LOW_LIMIT) { + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + } + pos.z += player->GetDistanceFromCentreOfMassToBaseOfModel(); + player->SetPosn(pos); + CTheScripts::ClearSpaceForMissionEntity(pos, player); + CPlayerPed::ReactivatePlayerPed(playerId); + + // Set task + player->GetTaskManager().SetTask(new CTaskSimplePlayerOnFoot(), TASK_PRIMARY_DEFAULT); + + return playerId; +} + +/// Get the position of a player +CVector GetPlayerCoordinates(CPlayerInfo& pinfo) { + return pinfo.GetPos(); +} + +bool IsPlayerInArea2D(CRunningScript* S, CPlayerPed& player, CRect area, bool highlightArea) { + if (highlightArea) { + CTheScripts::HighlightImportantArea((uint32)S + (uint32)S->m_IP, area, MAP_Z_LOW_LIMIT); + } + + if (CTheScripts::DbgFlag) { + CTheScripts::DrawDebugSquare(area); + } + + return player.bInVehicle + ? player.m_pVehicle->IsWithinArea(area.left, area.top, area.right, area.bottom) + : player.IsWithinArea(area.left, area.top, area.right, area.bottom); +} + +bool IsPlayerInArea3D(CRunningScript* S, CPlayerPed& player, CVector p1, CVector p2, bool highlightArea) { + if (highlightArea) { + CTheScripts::HighlightImportantArea((uint32)S + (uint32)S->m_IP, p1.x, p1.y, p2.x, p2.y, (p1.z + p2.z) / 2.0f); + } + + if (CTheScripts::DbgFlag) { + CTheScripts::DrawDebugCube(p1, p2); + } + + return player.bInVehicle + ? player.m_pVehicle->IsWithinArea(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z) + : player.IsWithinArea(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); +} + +auto IsPlayerPlaying(CPlayerInfo& player) -> notsa::script::CompareFlagUpdate { + return { player.m_nPlayerState == PLAYERSTATE_PLAYING }; +} + +bool IsPlayerClimbing(CPlayerPed& player) { + return player.GetIntelligence()->GetTaskClimb(); +} + +void SetSwimSpeed(CPlayerPed& player, float speed) { + if (auto swim = player.GetIntelligence()->GetTaskSwim()) + swim->m_fAnimSpeed = speed; +} + +void SetPlayerGroupToFollowAlways(CPlayerPed& player, bool enable) { + player.ForceGroupToAlwaysFollow(enable); +} + +void SetPlayerAbleToUseCrouch(uint32 playerIdx, bool enable) { + CPad::GetPad(playerIdx)->bDisablePlayerDuck = !enable; +} + +// Originally unsupported +bool IsPlayerTouchingObject(CPlayerPed& player, CObject& object) { + return GetPedOrItsVehicle(player).GetHasCollidedWith(&object); +} + +// Originally unsupported +bool IsPlayerTouchingObjectOnFoot(CPlayerPed& player, CObject& object) { + return player.GetHasCollidedWith(&object); +} + +void notsa::script::commands::player::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_CREATE_PLAYER, CreatePlayer); + REGISTER_COMMAND_HANDLER(COMMAND_GET_PLAYER_COORDINATES, GetPlayerCoordinates); + REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_IN_AREA_2D, IsPlayerInArea2D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_IN_AREA_3D, IsPlayerInArea3D); + REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_PLAYING, IsPlayerPlaying); + REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_CLIMBING, IsPlayerClimbing); + REGISTER_COMMAND_HANDLER(COMMAND_SET_SWIM_SPEED, SetSwimSpeed); + REGISTER_COMMAND_HANDLER(COMMAND_SET_PLAYER_GROUP_TO_FOLLOW_ALWAYS, SetPlayerGroupToFollowAlways); + REGISTER_COMMAND_HANDLER(COMMAND_SET_PLAYER_DUCK_BUTTON, SetPlayerAbleToUseCrouch); + + + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_IS_PLAYER_TOUCHING_OBJECT, IsPlayerTouchingObject); + REGISTER_UNSUPPORTED_COMMAND_HANDLER(COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT, IsPlayerTouchingObjectOnFoot); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_HEED_THREATS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_SHOOTING_IN_AREA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_CURRENT_PLAYER_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_TAXI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_SHOOTING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_EXPLODE_PLAYER_HEAD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_VISIBLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ADD_ARMOUR_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_OBJECT_BEEN_DAMAGED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_START_KILL_FRENZY_HEADSHOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ACTIVATE_MILITARY_CRANE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_WARP_PLAYER_INTO_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_PLAYER_DETONATOR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_COORDINATES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STORE_CAR_PLAYER_IS_IN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_MODEL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_DEAD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_PRESSING_HORN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYER_HEADING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_HEADING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_HEALTH_GREATER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_WEAPON_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CURRENT_PLAYER_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TURN_PLAYER_TO_FACE_COORD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_AS_LEADER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_POLICE_IGNORE_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TURN_PLAYER_TO_FACE_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_HEALTH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYER_HEALTH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_STOP_PLAYER_LOOKING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_GANG_PLAYER_ATTITUDE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_REMOTE_MODE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_ANIM_GROUP_FOR_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_NEVER_GETS_TIRED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FAST_RELOAD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_EVERYONE_IGNORE_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_UNSAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_SAFE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_AMMO_IN_PLAYER_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_HOOKER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_SITTING_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_LIFTING_A_PHONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENABLE_PLAYER_CONTROL_CAMERA); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_OBJECT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CURRENT_PLAYER_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_ON_ANY_BIKE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_PLAYER_GOT_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_FACING_CHAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_FOOT_DOWN); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANY_BOAT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANY_HELI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_ANY_PLANE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_HAVOC_CAUSED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_FLYING_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SHUT_PLAYER_UP); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_MOOD); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_WEARING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_DRUNKENNESS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYER_DRUNKENNESS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_DRUG_LEVEL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYER_DRUG_LEVEL); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_AUTO_AIM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TOUCHING_VEHICLE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_FIRE_PROOF); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_INCREASE_PLAYER_MAX_HEALTH); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_INCREASE_PLAYER_MAX_ARMOUR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_INFO_ZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TASK_PLAYER_ON_FOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_TASK_PLAYER_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_ANYTHING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CLOSEST_BUYABLE_OBJECT_TO_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DISABLE_PLAYER_SPRINT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_DELETE_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TWO_PLAYER_CAMERA_MODE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_LIMIT_TWO_PLAYER_DISTANCE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RELEASE_TWO_PLAYER_DISTANCE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_PLAYER_TARGETTING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_CLEAR_TWO_PLAYER_CAMERA_MODE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_PASSENGER_CAN_SHOOT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYERS_CAN_BE_IN_SEPARATE_CARS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_PLAYER_CROSSHAIR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLANE_ATTACK_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HELI_ATTACK_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_PLAYER_TATTOO); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TWO_PLAYER_CAM_MODE_SEPARATE_CARS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TWO_PLAYER_CAM_MODE_SAME_CAR_SHOOTING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TWO_PLAYER_CAM_MODE_SAME_CAR_NO_SHOOTING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_TWO_PLAYER_CAM_MODE_NOT_BOTH_IN_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENABLE_ENTRY_EXIT_PLAYER_GROUP_WARPING); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_OBJECT_ONLY_DAMAGED_BY_PLAYER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FIRE_BUTTON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_POSITION_FOR_CONVERSATION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLANE_ATTACK_PLAYER_USING_DOG_FIGHT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_GANG_DISAPPEAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_GANG_REAPPEAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_JUMP_BUTTON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_CAN_BE_DAMAGED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYERS_GANG_IN_CAR_ACTIVE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYERS_GANG_IN_CAR_ACTIVE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_PLAYER_BOUGHT_ITEM); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_PLAYER_MAX_ARMOUR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FIRE_WITH_SHOULDER_BUTTON); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLAYER_PUT_ON_GOGGLES); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_RENDER_PLAYER_WEAPON); +} diff --git a/source/game_sa/Scripts/Commands/Player.hpp b/source/game_sa/Scripts/Commands/Player.hpp deleted file mode 100644 index 65b30ef2ad..0000000000 --- a/source/game_sa/Scripts/Commands/Player.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "RunningScript.h" -#include "CommandParser/Parser.hpp" - -/*! -* Various player commands -*/ - -/*! -* @brief Create a player at the given world position -* @param playerId Player's id (0 or 1) -* @param pos World position -*/ -int32 CreatePlayer(int32 playerId, CVector pos) { - if (!CStreaming::IsModelLoaded(0 /*MI_PLAYER*/)) // todo (Izzotop): rename MODEL_NULL -> MI_PLAYER - { - CStreaming::RequestSpecialModel(0, "player", STREAMING_GAME_REQUIRED | STREAMING_KEEP_IN_MEMORY | STREAMING_PRIORITY_REQUEST); - CStreaming::LoadAllRequestedModels(true); - } - - // Create - CPlayerPed::SetupPlayerPed(playerId); - auto player = FindPlayerPed(playerId); - player->SetCharCreatedBy(PED_MISSION); - CPlayerPed::DeactivatePlayerPed(playerId); - - // Position in the world - if (pos.z <= MAP_Z_LOW_LIMIT) { - pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); - } - pos.z += player->GetDistanceFromCentreOfMassToBaseOfModel(); - player->SetPosn(pos); - CTheScripts::ClearSpaceForMissionEntity(pos, player); - CPlayerPed::ReactivatePlayerPed(playerId); - - // Set task - player->GetTaskManager().SetTask(new CTaskSimplePlayerOnFoot(), TASK_PRIMARY_DEFAULT); - - return playerId; -} -REGISTER_COMMAND_HANDLER(COMMAND_CREATE_PLAYER, CreatePlayer); - -/// Get the position of a player -CVector GetPlayerCoordinates(CPlayerInfo& pinfo) { - return pinfo.GetPos(); -} -REGISTER_COMMAND_HANDLER(COMMAND_GET_PLAYER_COORDINATES, GetPlayerCoordinates); - -bool IsPlyerInArea2D(CRunningScript* S, CPlayerPed& player, CRect area, bool highlightArea) { - if (highlightArea) { - CTheScripts::HighlightImportantArea((uint32)S + (uint32)S->m_pCurrentIP, area, MAP_Z_LOW_LIMIT); - } - - if (CTheScripts::DbgFlag) { - CTheScripts::DrawDebugSquare(area); - } - - return player.bInVehicle - ? player.m_pVehicle->IsWithinArea(area.left, area.top, area.right, area.bottom) - : player.IsWithinArea(area.left, area.top, area.right, area.bottom); -} -REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_IN_AREA_2D, IsPlyerInArea2D); - -bool IsPlyerInArea3D(CRunningScript* S, CPlayerPed& player, CVector p1, CVector p2, bool highlightArea) { - if (highlightArea) { - CTheScripts::HighlightImportantArea((uint32)S + (uint32)S->m_pCurrentIP, p1.x, p1.y, p2.x, p2.y, (p1.z + p2.z) / 2.0f); - } - - if (CTheScripts::DbgFlag) { - CTheScripts::DrawDebugCube(p1, p2); - } - - return player.bInVehicle - ? player.m_pVehicle->IsWithinArea(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z) - : player.IsWithinArea(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); -} -REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_IN_AREA_3D, IsPlyerInArea3D); - -auto IsPlayerPlaying(int32 playerId) -> notsa::script::CompareFlagUpdate { - return { FindPlayerInfo(playerId).m_nPlayerState == PLAYERSTATE_PLAYING }; -} -REGISTER_COMMAND_HANDLER(COMMAND_IS_PLAYER_PLAYING, IsPlayerPlaying); diff --git a/source/game_sa/Scripts/Commands/Script.cpp b/source/game_sa/Scripts/Commands/Script.cpp new file mode 100644 index 0000000000..b58b6c61d6 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Script.cpp @@ -0,0 +1,124 @@ +#include + +#include "./Commands.hpp" +#include +#include "AudioEngine.h" +#include "FireManager.h" +#include "Timer.h" +#include "Streaming.h" +#include "TheScripts.h" +#include "Conversations.h" +#include "CommandParser/Parser.hpp" + +/*! +* Various Script commands +*/ + +void TerminateAllScriptsWithThisName(const char* name) { + std::string scriptName{name}; + rng::transform(scriptName, scriptName.begin(), [](char c) { return std::tolower(c); }); + + for (auto* script = CTheScripts::pActiveScripts; script; script = script->m_pNext) { + if (!strcmp(scriptName.c_str(), script->m_szName)) { + script->RemoveScriptFromList(&CTheScripts::pActiveScripts); + script->AddScriptToList(&CTheScripts::pIdleScripts); + script->ShutdownThisScript(); + } + } +} + +void RemoveAllScriptFires() { + gFireManager.RemoveAllScriptFires(); +} + +void LoadScene(CVector point) { + CTimer::Stop(); + CStreaming::LoadScene(point); + CTimer::Update(); +} + +void LoadSceneInDirection(CVector point, float heading) { + CTimer::Stop(); + CRenderer::RequestObjectsInDirection(point, heading, STREAMING_LOADING_SCENE); + CStreaming::LoadScene(point); + CTimer::Update(); +} + +void AddStuckCarCheck(int32 carHandle, float distance, uint32 time) { + CTheScripts::StuckCars.AddCarToCheck(carHandle, distance, time, 0, false, false, false, 0); +} + +void RemoveStuckCarCheck(int32 carHandle) { + CTheScripts::StuckCars.RemoveCarFromCheck(carHandle); +} + +void LoadMissionAudio(uint32 slotId, int32 sampleId) { + AudioEngine.PreloadMissionAudio(slotId - 1, sampleId); +} + +void AttachMissionAudioToCar(uint32 slotId, CVehicle& veh) { + AudioEngine.AttachMissionAudioToPhysical(slotId - 1, &veh); +} + +void ReportMissionAudioEventAtChar(CPlayerPed& player, int32 eventId) { + AudioEngine.ReportMissionAudioEvent(eventId, &player); +} + +void ReportMissionAudioEventAtCar(CVehicle& vehicle, int eventId) { + AudioEngine.ReportMissionAudioEvent(eventId, &vehicle); +} + + +void PlayMissionAudio(uint32 slotId) { + AudioEngine.PlayLoadedMissionAudio(slotId - 1); +} + +void DisplayNonMinigameHelpMessages(bool enable) { + CTheScripts::bDisplayNonMiniGameHelpMessages = enable; +} + +void SetPhotoCameraEffect(bool enable) { + CTheScripts::bDrawCrossHair = enable ? eCrossHairType::FIXED_DRAW_1STPERSON_WEAPON : eCrossHairType::NONE; +} + +void DrawOddJobTitleBeforeFade(bool enable) { + CTheScripts::bDrawOddJobTitleBeforeFade = enable; +} + +void DrawSubtitlesBeforeFade(bool enable) { + CTheScripts::bDrawSubtitlesBeforeFade = enable; +} + +void SetPlayerInStadium(bool enable) { + CTheScripts::bPlayerIsOffTheMap = enable; +} + +void SetUpConversationNodeWithScriptedSpeech( + const char* questionKey, + const char* answerYesKey, + const char* answerNoKey, + int32 questionWAV, + int32 answerYesWAV, + int32 answerNoWAV) { + CConversations::SetUpConversationNode(questionKey, answerYesKey, answerNoKey, questionWAV, answerYesWAV, answerNoWAV); +} + +void notsa::script::commands::script::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, TerminateAllScriptsWithThisName); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_ALL_SCRIPT_FIRES, RemoveAllScriptFires); + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_SCENE, LoadScene); + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_SCENE_IN_DIRECTION, LoadSceneInDirection); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_STUCK_CAR_CHECK, AddStuckCarCheck); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_STUCK_CAR_CHECK, RemoveStuckCarCheck); + REGISTER_COMMAND_HANDLER(COMMAND_LOAD_MISSION_AUDIO, LoadMissionAudio); + REGISTER_COMMAND_HANDLER(COMMAND_ATTACH_MISSION_AUDIO_TO_CAR, AttachMissionAudioToCar); + REGISTER_COMMAND_HANDLER(COMMAND_REPORT_MISSION_AUDIO_EVENT_AT_CHAR, ReportMissionAudioEventAtChar); + REGISTER_COMMAND_HANDLER(COMMAND_REPORT_MISSION_AUDIO_EVENT_AT_CAR, ReportMissionAudioEventAtCar); + REGISTER_COMMAND_HANDLER(COMMAND_PLAY_MISSION_AUDIO, PlayMissionAudio); + REGISTER_COMMAND_HANDLER(COMMAND_DISPLAY_NON_MINIGAME_HELP_MESSAGES, DisplayNonMinigameHelpMessages); + REGISTER_COMMAND_HANDLER(COMMAND_SET_PHOTO_CAMERA_EFFECT, SetPhotoCameraEffect); + REGISTER_COMMAND_HANDLER(COMMAND_DRAW_ODDJOB_TITLE_BEFORE_FADE, DrawOddJobTitleBeforeFade); + REGISTER_COMMAND_HANDLER(COMMAND_DRAW_SUBTITLES_BEFORE_FADE, DrawSubtitlesBeforeFade); + REGISTER_COMMAND_HANDLER(COMMAND_SET_PLAYER_IS_IN_STADIUM, SetPlayerInStadium); + REGISTER_COMMAND_HANDLER(COMMAND_SET_UP_CONVERSATION_NODE_WITH_SCRIPTED_SPEECH, SetUpConversationNodeWithScriptedSpeech); +} diff --git a/source/game_sa/Scripts/Commands/Script.hpp b/source/game_sa/Scripts/Commands/Script.hpp deleted file mode 100644 index c34af36594..0000000000 --- a/source/game_sa/Scripts/Commands/Script.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*! -* Various Script commands -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { - char str[8]; - ReadTextLabelFromScript(str, 8); - - for (int i = 0; i < 8; i++) - str[i] = tolower(str[i]); - - CRunningScript* script = CTheScripts::pActiveScripts; - while (script) { - CRunningScript* next = script->m_pNext; - if (!strcmp(script->m_szName, str)) { - script->RemoveScriptFromList(&CTheScripts::pActiveScripts); - script->AddScriptToList(&CTheScripts::pIdleScripts); - script->ShutdownThisScript(); - } - script = next; - } - return OR_CONTINUE; -} diff --git a/source/game_sa/Scripts/Commands/Sequence.cpp b/source/game_sa/Scripts/Commands/Sequence.cpp new file mode 100644 index 0000000000..aea17396db --- /dev/null +++ b/source/game_sa/Scripts/Commands/Sequence.cpp @@ -0,0 +1,21 @@ +#include + +#include "./Commands.hpp" +#include + +/*! +* Various Sequence commands +*/ + +void SetSequenceToRepeat(int32 index, bool repeat) { + const auto actualIndex = CTheScripts::GetActualScriptThingIndex(index, SCRIPT_THING_SEQUENCE_TASK); + if (actualIndex < 0 || actualIndex >= MAX_NUM_SCRIPT_SEQUENCE_TASKS) { + DEV_LOG("COMMAND_SET_SEQUENCE_TO_REPEAT: Index should be in [0, 64], it is {}", actualIndex); + return; + } + CTaskSequences::GetActiveSequence().m_bRepeatSequence = repeat; +} + +void notsa::script::commands::sequence::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_SET_SEQUENCE_TO_REPEAT, SetSequenceToRepeat); +} diff --git a/source/game_sa/Scripts/Commands/Sequence.hpp b/source/game_sa/Scripts/Commands/Sequence.hpp deleted file mode 100644 index 566870e83f..0000000000 --- a/source/game_sa/Scripts/Commands/Sequence.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -/*! -* Various Sequence commands -*/ - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x643 - CollectParameters(2); - auto index = CTheScripts::GetActualScriptThingIndex(ScriptParams[0].iParam, SCRIPT_THING_SEQUENCE_TASK); - if (index < 0 || index >= 64) { - DEV_LOG("COMMAND_SET_SEQUENCE_TO_REPEAT: Index should be in [0, 64]"); - return OR_CONTINUE; - } - CTaskSequences::GetActiveSequence().m_bRepeatSequence = ScriptParams[1].bParam; - return OR_CONTINUE; -} diff --git a/source/game_sa/Scripts/Commands/Text.cpp b/source/game_sa/Scripts/Commands/Text.cpp new file mode 100644 index 0000000000..76e78e603a --- /dev/null +++ b/source/game_sa/Scripts/Commands/Text.cpp @@ -0,0 +1,62 @@ +#include + +#include "./Commands.hpp" +#include +#include "Messages.h" +#include "Hud.h" +#include "CommandParser/Parser.hpp" + +/*! +* Various text commands +*/ + +void ClearSmallPrints() { + CMessages::ClearSmallMessagesOnly(); +} + +void ClearHelp() { + CHud::SetHelpMessage(nullptr, true, false, false); +} + +void FlashHudObject(eHudItem item) { + CHud::m_ItemToFlash = item; +} + +// NOTSA: time: int32 -> uint32 +void PrintBig(const char* key, uint32 time, uint32 flags) { + const auto text = TheText.Get(key); + CMessages::AddBigMessage(text, time, static_cast(flags - 1)); +} + +void Print(const char* key, uint32 time, uint32 flags) { + const auto text = TheText.Get(key); + if (!text || strncmp(text, "~z~", 3u) != 0 || FrontEndMenuManager.m_bShowSubtitles) + CMessages::AddMessageQ(text, time, flags, CTheScripts::bAddNextMessageToPreviousBriefs); + CTheScripts::bAddNextMessageToPreviousBriefs = true; +} + +void PrintNow(const char* key, uint32 time, uint32 flags) { + const auto text = TheText.Get(key); + if (!text || strncmp(text, "~z~", 3u) != 0 || FrontEndMenuManager.m_bShowSubtitles) + CMessages::AddMessageJump(text, time, flags, CTheScripts::bAddNextMessageToPreviousBriefs); + CTheScripts::bAddNextMessageToPreviousBriefs = true; +} + +void ClearThisPrintBigNow(uint16 item) { + CMessages::ClearThisPrintBigNow(static_cast(item - 1)); +} + +void ClearPrints() { + CMessages::ClearMessages(false); +} + +void notsa::script::commands::text::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_SMALL_PRINTS, ClearSmallPrints); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_HELP, ClearHelp); + REGISTER_COMMAND_HANDLER(COMMAND_FLASH_HUD_OBJECT, FlashHudObject); + REGISTER_COMMAND_HANDLER(COMMAND_PRINT_BIG, PrintBig); + REGISTER_COMMAND_HANDLER(COMMAND_PRINT, Print); + REGISTER_COMMAND_HANDLER(COMMAND_PRINT_NOW, PrintNow); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_THIS_PRINT_BIG_NOW, ClearThisPrintBigNow); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_PRINTS, ClearPrints); +} diff --git a/source/game_sa/Scripts/Commands/Text.hpp b/source/game_sa/Scripts/Commands/Text.hpp deleted file mode 100644 index fdd81de44e..0000000000 --- a/source/game_sa/Scripts/Commands/Text.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -/*! -* Various text commands -*/ - - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0BA - char label[8]; - ReadTextLabelFromScript(label, 8); - auto text = TheText.Get(label); - CollectParameters(2); - CMessages::AddBigMessage(text, ScriptParams[0].iParam, (eMessageStyle)(ScriptParams[1].iParam - 1)); - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0BB - char label[8]; - ReadTextLabelFromScript(label, 8); - auto text = TheText.Get(label); - CollectParameters(2); - if (!text || text[0] != '~' || text[1] != 'z' || text[2] != '~' || FrontEndMenuManager.m_bShowSubtitles) - CMessages::AddMessageQ(text, ScriptParams[0].iParam, ScriptParams[1].iParam, CTheScripts::bAddNextMessageToPreviousBriefs); - CTheScripts::bAddNextMessageToPreviousBriefs = true; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0BC - char label[8]; - ReadTextLabelFromScript(label, 8); - auto text = TheText.Get(label); - CollectParameters(2); - if (!text || text[0] != '~' || text[1] != 'z' || text[2] != '~' || FrontEndMenuManager.m_bShowSubtitles) - CMessages::AddMessageJump(text, ScriptParams[0].iParam, ScriptParams[1].iParam, CTheScripts::bAddNextMessageToPreviousBriefs); - CTheScripts::bAddNextMessageToPreviousBriefs = true; - return OR_CONTINUE; -} - -template<> -OpcodeResult CRunningScript::ProcessCommand() { // 0x0BE - CMessages::ClearMessages(false); - return OR_CONTINUE; -} diff --git a/source/game_sa/Scripts/Commands/Unused.cpp b/source/game_sa/Scripts/Commands/Unused.cpp new file mode 100644 index 0000000000..87047a2a23 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Unused.cpp @@ -0,0 +1,46 @@ +#include + +#include "./Commands.hpp" +#include + +#include "CommandParser/Parser.hpp" + +/* +* Unused commands +*/ + +void NoOp() {} + + +void notsa::script::commands::unused::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_CASE, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_BREAK, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_SWITCH, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_DEFAULT, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_ENDSWITCH, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CONST_INT, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CONST_FLOAT, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_GROUP_LEADER, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_DRAW_RECT_WITH_TITLE, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_SET_ATTRACTOR_RADIUS, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_ALL_BREAKPOINTS, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_ALL_WATCHPOINTS, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_IS_THIS_MODEL_A_TRAIN, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_SET_TIME_ONE_DAY_BACK, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CLOSEST_ATTRACTOR, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_VEHICLE_DIRT_LEVEL, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_SIT_IN_RESTAURANT, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_SET_WATER_CONFIGURATION, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_ALL_VIEW_VARIABLES, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_OBJECT_ANIM_TOTAL_TIME, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_USE_NEARBY_ENTRY_EXIT, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_USE_ATTRACTOR_ADVANCED, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_SET_OBJECT_ANIM_PLAYING_FLAG, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_MODEL_NAME_FOR_DEBUG_ONLY, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_VEHICLE_CHAR_IS_STANDING_ON, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_ADD_INTERESTING_ENTITY_FOR_CHAR, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_INTERESTING_ENTITIES_FOR_CHAR, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_TASK_FOLLOW_PATH_NODES_TO_COORD_SHOOTING, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_START_PLAYBACK_RECORDED_CAR_USING_AI_LOOPED, NoOp); + REGISTER_COMMAND_HANDLER(COMMAND_GET_RANDOM_ATTRACTOR_ON_CLOSEST_OBJECT_OF_TYPE, NoOp); +} diff --git a/source/game_sa/Scripts/Commands/Utility.cpp b/source/game_sa/Scripts/Commands/Utility.cpp new file mode 100644 index 0000000000..c4dc9a6b76 --- /dev/null +++ b/source/game_sa/Scripts/Commands/Utility.cpp @@ -0,0 +1,10 @@ +#include + +#include "./Commands.hpp" +#include + +/*! +* Various utility commands +*/ + +void notsa::script::commands::utility::RegisterHandlers() {} diff --git a/source/game_sa/Scripts/Commands/Utility.hpp b/source/game_sa/Scripts/Commands/Utility.hpp deleted file mode 100644 index 96f7868f9a..0000000000 --- a/source/game_sa/Scripts/Commands/Utility.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -/*! -* Various utility commands -*/ - diff --git a/source/game_sa/Scripts/Commands/Vehicle.cpp b/source/game_sa/Scripts/Commands/Vehicle.cpp new file mode 100644 index 0000000000..46d5591e2c --- /dev/null +++ b/source/game_sa/Scripts/Commands/Vehicle.cpp @@ -0,0 +1,124 @@ +#include + +#include "./Commands.hpp" +#include +#include + +#include "PlayerInfo.h" +#include "World.h" +#include "CarGenerator.h" +#include "TheCarGenerators.h" +#include "CommandParser/Parser.hpp" +using namespace notsa::script; + +/*! +* Various vehicle commands +*/ + +void ClearHeliOrientation(CHeli& heli) { + heli.ClearHeliOrientation(); +} + +void RemoveRCBuggy() { + FindPlayerInfo().BlowUpRCBuggy(false); +} + +void SetCarProofs(CVehicle& veh, bool bullet, bool fire, bool explosion, bool collision, bool melee) { + auto& flags = veh.physicalFlags; + flags.bBulletProof = bullet; + flags.bFireProof = fire; + flags.bExplosionProof = explosion; + flags.bCollisionProof = collision; + flags.bMeleeProof = melee; +} + +void SwitchCarGenerator(int32 generatorId, int32 count) { + const auto generator = CTheCarGenerators::Get(generatorId); + if (count) { + generator->SwitchOn(); + if (count <= 100) { + generator->m_nGenerateCount = count; + } + } else { + generator->SwitchOff(); + } +} + +void SetHasBeenOwnedForCarGenerator(int32 generatorId, bool alreadyOwned) { + CTheCarGenerators::Get(generatorId)->bPlayerHasAlreadyOwnedCar = alreadyOwned; +} + +float GetCarSpeed(CVehicle& veh) { + return veh.m_vecMoveSpeed.Magnitude() * 50.f; +} + +void SetCarDrivingStyle(CVehicle& veh, eCarDrivingStyle style) { + veh.m_autoPilot.m_nCarDrivingStyle = style; +} + +bool IsFirstCarColor(CVehicle& veh, int32 color) { + return veh.m_nPrimaryColor == color; +} + +bool IsSecondCarColor(CVehicle& veh, int32 color) { + return veh.m_nSecondaryColor == color; +} +MultiRet GetExtraCarColors(CVehicle& veh) { + return {veh.m_nTertiaryColor, veh.m_nQuaternaryColor}; +} + +//void FixCar(CVehicle& vehicle) { +// +//} +//REGISTER_COMMAND_HANDLER(COMMAND_IMPROVE_CAR_BY_CHEATING, ImproveCarByCheating); + +void PopCarBootUsingPhysics(CAutomobile& automobile) { + automobile.PopBootUsingPhysics(); +} + +void SkipToNextAllowedStation(CTrain& train) { + CTrain::SkipToNextAllowedStation(&train); +} + + +void SetRailTrackResistanceMult(float value) { + CVehicle::ms_fRailTrackResistance = CVehicle::ms_fRailTrackResistanceDefault * (value > 0.0f ? value : 1.0f); +} + +void DisableHeliAudio(CVehicle& vehicle, bool enable) { + if (enable) { + vehicle.m_vehicleAudio.EnableHelicoptor(); + } else { + vehicle.m_vehicleAudio.DisableHelicoptor(); + } +} + +void notsa::script::commands::vehicle::RegisterHandlers() { + REGISTER_COMMAND_HANDLER(COMMAND_CLEAR_HELI_ORIENTATION, ClearHeliOrientation); + REGISTER_COMMAND_HANDLER(COMMAND_REMOVE_RC_BUGGY, RemoveRCBuggy); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CAR_PROOFS, SetCarProofs); + REGISTER_COMMAND_HANDLER(COMMAND_SWITCH_CAR_GENERATOR, SwitchCarGenerator); + REGISTER_COMMAND_HANDLER(COMMAND_SET_HAS_BEEN_OWNED_FOR_CAR_GENERATOR, SetHasBeenOwnedForCarGenerator); + REGISTER_COMMAND_HANDLER(COMMAND_GET_CAR_SPEED, GetCarSpeed); + REGISTER_COMMAND_HANDLER(COMMAND_SET_CAR_DRIVING_STYLE, SetCarDrivingStyle); + REGISTER_COMMAND_HANDLER(COMMAND_IS_FIRST_CAR_COLOUR, IsFirstCarColor); + REGISTER_COMMAND_HANDLER(COMMAND_IS_SECOND_CAR_COLOUR, IsSecondCarColor); + REGISTER_COMMAND_HANDLER(COMMAND_GET_EXTRA_CAR_COLOURS, GetExtraCarColors); + REGISTER_COMMAND_HANDLER(COMMAND_POP_CAR_BOOT_USING_PHYSICS, PopCarBootUsingPhysics); + REGISTER_COMMAND_HANDLER(COMMAND_SKIP_TO_NEXT_ALLOWED_STATION, SkipToNextAllowedStation); + REGISTER_COMMAND_HANDLER(COMMAND_SET_RAILTRACK_RESISTANCE_MULT, SetRailTrackResistanceMult); + REGISTER_COMMAND_HANDLER(COMMAND_DISABLE_HELI_AUDIO, DisableHeliAudio); + + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_TAXI); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_TAXI_TIMER); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_BOAT); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_HAS_RESPRAY_HAPPENED); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_RAM_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_BLOCK_CAR); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_FUNNY_SUSPENSION); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_BIG_WHEELS); + REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SWITCH_CAR_RADIO); +} diff --git a/source/game_sa/Scripts/RunningScript.cpp b/source/game_sa/Scripts/RunningScript.cpp index 70e5f6ece4..2c04ce6f8f 100644 --- a/source/game_sa/Scripts/RunningScript.cpp +++ b/source/game_sa/Scripts/RunningScript.cpp @@ -4,47 +4,33 @@ #include "TheScripts.h" #include "CarGenerator.h" #include "Hud.h" -#include "ReversibleHooks/ReversibleHook/ScriptCommand.h" -// Commands stuff -#include "CommandParser/Parser.hpp" +//! Define it to dump out all commands that don't have a custom handler (that is, they aren't reversed) +//! Makes compilation slow, so don't enable unless necessary! +//#define DUMP_CUSTOM_COMMAND_HANDLERS_TO_FILE -/*! -* Make sure to include the command headers here, otherwise they won't be registered, -* and the default GTA handler will be called. -* -* Currently we don't include the Commands/CLEO headers at all. -* -* Eventually we'll get rid of this header based approach, and switch to using cpp files instead, -* currently it's not possible because of the way it's set up. -* (Once all old functions are reworked to use the parser) -*/ - -#include "Commands/Basic.hpp" -#include "Commands/Car.hpp" -#include "Commands/Comparasion.hpp" -#include "Commands/Generic.hpp" -#include "Commands/Mission.hpp" -#include "Commands/Player.hpp" -#include "Commands/Sequence.hpp" -#include "Commands/Utility.hpp" -#include "Commands/Camera.hpp" -#include "Commands/Char.hpp" -#include "Commands/Clock.hpp" -#include "Commands/Game.hpp" -#include "Commands/Math.hpp" -#include "Commands/Pad.hpp" -#include "Commands/Script.hpp" -#include "Commands/Text.hpp" - -// Must be included after the commands +#ifdef DUMP_CUSTOM_COMMAND_HANDLERS_TO_FILE +#include +#endif + +#include "CommandParser/Parser.hpp" #include "CommandParser/LUTGenerator.hpp" +#include "ReversibleHooks/ReversibleHook/ScriptCommand.h" + +#include "Commands/Commands.hpp" +#ifdef NOTSA_USE_CLEO_COMMANDS // TODO: Add premake/cmake option for this define +#include "Commands/CLEO/Commands.hpp" +#include "Commands/CLEO/Extensions/Commands.hpp" +#endif // https://library.sannybuilder.com/#/sa -static auto s_CommandHandlerLUT = notsa::script::GenerateLUT(); +//! Holds all custom command handlers (or null for commands with no custom handler) +static inline std::array s_CustomCommandHandlerTable{}; void CRunningScript::InjectHooks() { + InjectCustomCommandHooks(); + RH_ScopedClass(CRunningScript); RH_ScopedCategory("Scripts"); @@ -77,7 +63,6 @@ void CRunningScript::InjectHooks() { RH_ScopedInstall(GetPointerToScriptVariable, 0x464790, { .stackArguments = 1 }); RH_ScopedInstall(DoDeathArrestCheck, 0x485A50); RH_ScopedInstall(SetCharCoordinates, 0x464DC0); - RH_ScopedInstall(GivePedScriptedTask, 0x465C20); RH_ScopedInstall(AddScriptToList, 0x464C00, { .stackArguments = 1 }); RH_ScopedInstall(RemoveScriptFromList, 0x464BD0, { .stackArguments = 1 }); RH_ScopedInstall(ShutdownThisScript, 0x465AA0, { .reversed = false }); @@ -88,17 +73,80 @@ void CRunningScript::InjectHooks() { RH_ScopedInstall(UpdatePC, 0x464DA0, { .stackArguments = 1 }); RH_ScopedInstall(ProcessOneCommand, 0x469EB0); RH_ScopedInstall(Process, 0x469F00); + RH_ScopedOverloadedInstall(GivePedScriptedTask, "OG", 0x465C20, void(CRunningScript::*)(int32, CTask*, int32)); +} + +//! Register our custom script command handlers +void CRunningScript::InjectCustomCommandHooks() { + // Uncommenting any call will prevent it from being hooked, so + // feel free to do so when debugging (Just don't forget to undo the changes!) + + using namespace notsa::script::commands; + + basic::RegisterHandlers(); + camera::RegisterHandlers(); + character::RegisterHandlers(); + clock::RegisterHandlers(); + comparasion::RegisterHandlers(); + game::RegisterHandlers(); + generic::RegisterHandlers(); + math::RegisterHandlers(); + mission::RegisterHandlers(); + object::RegisterHandlers(); + pad::RegisterHandlers(); + ped::RegisterHandlers(); + player::RegisterHandlers(); + script::RegisterHandlers(); + sequence::RegisterHandlers(); + text::RegisterHandlers(); + unused::RegisterHandlers(); + utility::RegisterHandlers(); + vehicle::RegisterHandlers(); + +#ifdef NOTSA_USE_CLEO_COMMANDS + cleo::audiostream::RegisterHandlers(); + cleo::character::RegisterHandlers(); + cleo::dynamiclibrary::RegisterHandlers(); + cleo::fs::RegisterHandlers(); + cleo::game::RegisterHandlers(); + cleo::generic::RegisterHandlers(); + cleo::memory::RegisterHandlers(); + cleo::pad::RegisterHandlers(); + cleo::script::RegisterHandlers(); + cleo::vehicle::RegisterHandlers(); + cleo::world::RegisterHandlers(); + + cleo::extensions::cleoplus::RegisterHandlers(); + cleo::extensions::clipboard::RegisterHandlers(); + cleo::extensions::fs::RegisterHandlers(); + cleo::extensions::imgui::RegisterHandlers(); + cleo::extensions::intoperations::RegisterHandlers(); +#endif - // Enable in `StdInc.h` if needed (Don't forget to disabled it when committing) + // To enable use premake: `./premake5.exe vs2022 --allow-script-cmd-hooks` #ifdef ENABLE_SCRIPT_COMMAND_HOOKS - const auto HookCommand = []() { - using namespace ReversibleHooks::ReversibleHook; + // After injecting all hooks, we can create their reversible hook + for (auto&& [idx, cmd] : notsa::enumerate(s_CustomCommandHandlerTable)) { + const auto id = (eScriptCommands)(idx); + ReversibleHooks::AddItemToCategory( "Scripts/Commands", - std::make_shared>() + std::make_shared(id) ); - }; - notsa::script::IterateCommandIDs(HookCommand); + } +#endif + +#ifdef DUMP_CUSTOM_COMMAND_HANDLERS_TO_FILE + auto reversed{0}, total{0}; + std::ofstream ofsrev{ "reversed_script_command_handlers.txt" }, ofsnotrev{ "NOT_reversed_script_command_handlers.txt" }; + for (auto&& [idx, handler] : notsa::enumerate(s_CustomCommandHandlerTable)) { + const auto id = (eScriptCommands)(idx); + ++total; + if (handler) ++reversed; + (handler ? ofsrev : ofsnotrev) << ::notsa::script::GetScriptCommandName(id) << '\n'; + } + DEV_LOG("Script cmds dumped! Find them in `/Scripts`!"); + DEV_LOG("Script cmds reverse progress: {}/{} ({:.2f}% done)", reversed, total, 100.0f * ((float)reversed / (float)total)); #endif } @@ -108,9 +156,9 @@ void CRunningScript::Init() { m_pBaseIP = nullptr; m_pPrev = nullptr; m_pNext = nullptr; - m_pCurrentIP = nullptr; - memset(m_apStack, 0, sizeof(m_apStack)); - m_nSP = 0; + m_IP = nullptr; + memset(m_IPStack, 0, sizeof(m_IPStack)); + m_StackDepth = 0; m_nWakeTime = 0; m_bIsActive = false; m_bCondResult = false; @@ -201,6 +249,10 @@ void CRunningScript::GivePedScriptedTask(int32 pedHandle, CTask* task, int32 opc } } +void CRunningScript::GivePedScriptedTask(CPed* ped, CTask* task, int32 opcode) { + GivePedScriptedTask(GetPedPool()->GetRef(ped), task, opcode); // Must do it like this, otherwise unhooking of the original `GivePedScriptedTask` will do nothing +} + // 0x470150 void CRunningScript::PlayAnimScriptCommand(int32 commandId) { plugin::CallMethod<0x470150, CRunningScript*, int32>(this, commandId); @@ -308,13 +360,15 @@ void CRunningScript::DoDeathArrestCheck() { if (!playerInfo.IsRestartingAfterDeath() && !playerInfo.IsRestartingAfterArrest()) return; - if (m_nSP > 1u) { // todo: refactor + // TODO/NOTE: This is buggy, it will decrease SP to 0, and then `--m_nSP` will underflow :D + NOTSA_UNREACHABLE(); // Prevent random bugs + if (m_StackDepth > 1u) { // todo: refactor do - --m_nSP; - while (m_nSP > 1u); + --m_StackDepth; + while (m_StackDepth > 1u); } - m_pCurrentIP = m_apStack[--m_nSP]; + m_IP = m_IPStack[--m_StackDepth]; CMessages::ClearSmallMessagesOnly(); CTheScripts::ScriptSpace[CTheScripts::OnAMissionFlag] = 0; m_bDeathArrestExecuted = true; @@ -322,34 +376,34 @@ void CRunningScript::DoDeathArrestCheck() { } // 0x464F50 -void CRunningScript::GetCorrectPedModelIndexForEmergencyServiceType(ePedType pedType, int32* outModelId) { - switch (*outModelId) { +void CRunningScript::GetCorrectPedModelIndexForEmergencyServiceType(ePedType pedType, uint32* typeSpecificModelId) { + switch (*typeSpecificModelId) { case MODEL_LAPD1: case MODEL_SFPD1: case MODEL_LVPD1: case MODEL_LAPDM1: if (pedType == PED_TYPE_COP) { - *outModelId = COP_TYPE_CITYCOP; + *typeSpecificModelId = COP_TYPE_CITYCOP; } break; case MODEL_CSHER: if (pedType == PED_TYPE_COP) { - *outModelId = COP_TYPE_CSHER; + *typeSpecificModelId = COP_TYPE_CSHER; } break; case MODEL_SWAT: if (pedType == PED_TYPE_COP) { - *outModelId = COP_TYPE_SWAT1; + *typeSpecificModelId = COP_TYPE_SWAT1; } break; case MODEL_FBI: if (pedType == PED_TYPE_COP) { - *outModelId = COP_TYPE_FBI; + *typeSpecificModelId = COP_TYPE_FBI; } break; case MODEL_ARMY: if (pedType == PED_TYPE_COP) { - *outModelId = COP_TYPE_ARMY; + *typeSpecificModelId = COP_TYPE_ARMY; } break; default: @@ -392,23 +446,22 @@ void CRunningScript::ScriptTaskPickUpObject(int32 commandId) { } // 0x464DC0 -void CRunningScript::SetCharCoordinates(CPed* ped, float x, float y, float z, bool bWarpGang, bool bOffset) { - if (z <= MAP_Z_LOW_LIMIT) - z = CWorld::FindGroundZForCoord(x, y); +void CRunningScript::SetCharCoordinates(CPed& ped, CVector posn, bool warpGang, bool offset) { + CWorld::PutToGroundIfTooLow(posn); - CVehicle* vehicle = ped->bInVehicle ? ped->m_pVehicle : nullptr; + CVehicle* vehicle = ped.bInVehicle ? ped.m_pVehicle : nullptr; if (vehicle) { - CVector pos = { x, y, vehicle->GetDistanceFromCentreOfMassToBaseOfModel() + z }; - vehicle->Teleport(pos, false); - CTheScripts::ClearSpaceForMissionEntity(&pos, vehicle); + posn.z += vehicle->GetDistanceFromCentreOfMassToBaseOfModel(); + vehicle->Teleport(posn, false); + CTheScripts::ClearSpaceForMissionEntity(&posn, vehicle); } else { - CVector pos = { x, y, bOffset ? ped->GetDistanceFromCentreOfMassToBaseOfModel() + z : z }; - CTheScripts::ClearSpaceForMissionEntity(&pos, ped); - auto* group = CPedGroups::GetPedsGroup(ped); - if (group && group->GetMembership().IsLeader(ped) && bWarpGang) { - group->Teleport(&pos); + posn.z += offset ? ped.GetDistanceFromCentreOfMassToBaseOfModel() : 0.0f; + CTheScripts::ClearSpaceForMissionEntity(&posn, &ped); + auto* group = CPedGroups::GetPedsGroup(&ped); + if (group && group->GetMembership().IsLeader(&ped) && warpGang) { + group->Teleport(&posn); } else { - ped->Teleport(pos, false); + ped.Teleport(posn, false); } } } @@ -422,37 +475,40 @@ tScriptParam* CRunningScript::GetPointerToLocalVariable(int32 varIndex) { } /*! - * Returns pointer to local variable pointed by offset and array index as well as multiplier. * @addr 0x463CC0 + * @brief Returns pointer to a local script variable. + * + * @param arrayBaseOffset The offset of the array (In terms of the number of `tScriptParam`s before it) + * @param index Index of the variable inside the array + * @param arrayEntriesSize Size of 1 variable in the array (In terms of `tScriptParam`'s - So for a regular `int` (or float, etc) variable this will be `1`, for long strings it's `4` and for short one's it's `2`) */ -tScriptParam* CRunningScript::GetPointerToLocalArrayElement(int32 arrVarOffset, uint16 arrElemIdx, uint8 arrElemSize) { - int32 index = arrVarOffset + arrElemSize * arrElemIdx; - return GetPointerToLocalVariable(index); +tScriptParam* CRunningScript::GetPointerToLocalArrayElement(int32 arrayBaseOffset, uint16 index, uint8 arrayEntriesSize) { + return GetPointerToLocalVariable(arrayBaseOffset + arrayEntriesSize * index); } /*! * Returns pointer to script variable of any type. * @addr 0x464790 */ -tScriptParam* CRunningScript::GetPointerToScriptVariable(eScriptVariableType variableType) { +tScriptParam* CRunningScript::GetPointerToScriptVariable(eScriptVariableType) { uint8 arrElemSize; uint16 arrVarOffset; int32 arrElemIdx; - int8 type = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + int8 type = CTheScripts::Read1ByteFromScript(m_IP); switch (type) { case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: case SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE: case SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); return reinterpret_cast(&CTheScripts::ScriptSpace[index]); } case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: case SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE: case SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); return GetPointerToLocalVariable(index); } @@ -464,7 +520,7 @@ tScriptParam* CRunningScript::GetPointerToScriptVariable(eScriptVariableType var return reinterpret_cast(&CTheScripts::ScriptSpace[LONG_STRING_SIZE * arrElemIdx + arrVarOffset]); else if (type == SCRIPT_PARAM_GLOBAL_SHORT_STRING_ARRAY) return reinterpret_cast(&CTheScripts::ScriptSpace[SHORT_STRING_SIZE * arrElemIdx + arrVarOffset]); - else + else // SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY return reinterpret_cast(&CTheScripts::ScriptSpace[4 * arrElemIdx + arrVarOffset]); case SCRIPT_PARAM_LOCAL_NUMBER_ARRAY: @@ -475,7 +531,7 @@ tScriptParam* CRunningScript::GetPointerToScriptVariable(eScriptVariableType var arrElemSize = 4; else if (type == SCRIPT_PARAM_LOCAL_SHORT_STRING_ARRAY) arrElemSize = 2; - else + else // SCRIPT_PARAM_LOCAL_NUMBER_ARRAY arrElemSize = 1; return GetPointerToLocalArrayElement(arrVarOffset, arrElemIdx, arrElemSize); @@ -492,9 +548,9 @@ uint16 CRunningScript::GetIndexOfGlobalVariable() { uint16 arrVarOffset; int32 arrElemIdx; - switch (CTheScripts::Read1ByteFromScript(m_pCurrentIP)) { + switch (CTheScripts::Read1ByteFromScript(m_IP)) { case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: - return CTheScripts::Read2BytesFromScript(m_pCurrentIP); + return CTheScripts::Read2BytesFromScript(m_IP); case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: ReadArrayInformation(true, &arrVarOffset, &arrElemIdx); return arrVarOffset + 4 * arrElemIdx; @@ -510,30 +566,30 @@ void CRunningScript::CollectParameters(int16 count) { int32 arrElemIdx; for (auto i = 0; i < count; i++) { - switch (CTheScripts::Read1ByteFromScript(m_pCurrentIP)) { + switch (CTheScripts::Read1ByteFromScript(m_IP)) { case SCRIPT_PARAM_STATIC_INT_32BITS: - ScriptParams[i].iParam = CTheScripts::Read4BytesFromScript(m_pCurrentIP); + ScriptParams[i].iParam = CTheScripts::Read4BytesFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); ScriptParams[i].iParam = *reinterpret_cast(&CTheScripts::ScriptSpace[index]); break; } case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); ScriptParams[i] = *GetPointerToLocalVariable(index); break; } case SCRIPT_PARAM_STATIC_INT_8BITS: - ScriptParams[i].iParam = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + ScriptParams[i].iParam = CTheScripts::Read1ByteFromScript(m_IP); break; case SCRIPT_PARAM_STATIC_INT_16BITS: - ScriptParams[i].iParam = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + ScriptParams[i].iParam = CTheScripts::Read2BytesFromScript(m_IP); break; case SCRIPT_PARAM_STATIC_FLOAT: - ScriptParams[i].fParam = CTheScripts::ReadFloatFromScript(m_pCurrentIP); + ScriptParams[i].fParam = CTheScripts::ReadFloatFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: ReadArrayInformation(true, &arrVarOffset, &arrElemIdx); @@ -554,31 +610,31 @@ void CRunningScript::CollectParameters(int16 count) { int32 CRunningScript::CollectNextParameterWithoutIncreasingPC() { uint16 arrVarOffset; int32 arrElemIdx; - uint8* ip = m_pCurrentIP; + uint8* ip = m_IP; int32 result = -1; - switch (CTheScripts::Read1ByteFromScript(m_pCurrentIP)) { + switch (CTheScripts::Read1ByteFromScript(m_IP)) { case SCRIPT_PARAM_STATIC_INT_32BITS: case SCRIPT_PARAM_STATIC_FLOAT: - result = CTheScripts::Read4BytesFromScript(m_pCurrentIP); + result = CTheScripts::Read4BytesFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); result = *reinterpret_cast(&CTheScripts::ScriptSpace[index]); break; } case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); result = GetPointerToLocalVariable(index)->iParam; break; } case SCRIPT_PARAM_STATIC_INT_8BITS: - result = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + result = CTheScripts::Read1ByteFromScript(m_IP); break; case SCRIPT_PARAM_STATIC_INT_16BITS: - result = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + result = CTheScripts::Read2BytesFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: ReadArrayInformation(false, &arrVarOffset, &arrElemIdx); @@ -590,7 +646,7 @@ int32 CRunningScript::CollectNextParameterWithoutIncreasingPC() { break; } - m_pCurrentIP = ip; + m_IP = ip; return result; } @@ -602,16 +658,16 @@ void CRunningScript::StoreParameters(int16 count) { int32 arrElemIdx; for (auto i = 0; i < count; i++) { - switch (CTheScripts::Read1ByteFromScript(m_pCurrentIP)) { + switch (CTheScripts::Read1ByteFromScript(m_IP)) { case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); *reinterpret_cast(&CTheScripts::ScriptSpace[index]) = ScriptParams[i].iParam; break; } case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); *GetPointerToLocalVariable(index) = ScriptParams[i]; break; } @@ -630,7 +686,7 @@ void CRunningScript::StoreParameters(int16 count) { // Reads array var base offset and element index from index variable. // 0x463CF0 void CRunningScript::ReadArrayInformation(int32 updateIp, uint16* outArrVarOffset, int32* outArrElemIdx) { - auto* ip = m_pCurrentIP; + auto* ip = m_IP; *outArrVarOffset = CTheScripts::Read2BytesFromScript(ip); uint16 arrayIndexVar = CTheScripts::Read2BytesFromScript(ip); @@ -642,7 +698,7 @@ void CRunningScript::ReadArrayInformation(int32 updateIp, uint16* outArrVarOffse *outArrElemIdx = GetPointerToLocalVariable(arrayIndexVar)->iParam; if (updateIp) - m_pCurrentIP = ip; + m_IP = ip; } // Collects parameters and puts them to local variables of new script @@ -650,33 +706,33 @@ void CRunningScript::ReadArrayInformation(int32 updateIp, uint16* outArrVarOffse void CRunningScript::ReadParametersForNewlyStartedScript(CRunningScript* newScript) { uint16 arrVarOffset; int32 arrElemIdx; - int8 type = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + int8 type = CTheScripts::Read1ByteFromScript(m_IP); - for (int i = 0; type != SCRIPT_PARAM_END_OF_ARGUMENTS; type = CTheScripts::Read1ByteFromScript(m_pCurrentIP), i++) { + for (int i = 0; type != SCRIPT_PARAM_END_OF_ARGUMENTS; type = CTheScripts::Read1ByteFromScript(m_IP), i++) { switch (type) { case SCRIPT_PARAM_STATIC_INT_32BITS: - newScript->m_aLocalVars[i].iParam = CTheScripts::Read4BytesFromScript(m_pCurrentIP); + newScript->m_aLocalVars[i].iParam = CTheScripts::Read4BytesFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); newScript->m_aLocalVars[i].iParam = *reinterpret_cast(&CTheScripts::ScriptSpace[index]); break; } case SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); newScript->m_aLocalVars[i] = *GetPointerToLocalVariable(index); break; } case SCRIPT_PARAM_STATIC_INT_8BITS: - newScript->m_aLocalVars[i].iParam = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + newScript->m_aLocalVars[i].iParam = CTheScripts::Read1ByteFromScript(m_IP); break; case SCRIPT_PARAM_STATIC_INT_16BITS: - newScript->m_aLocalVars[i].iParam = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + newScript->m_aLocalVars[i].iParam = CTheScripts::Read2BytesFromScript(m_IP); break; case SCRIPT_PARAM_STATIC_FLOAT: - newScript->m_aLocalVars[i].fParam = CTheScripts::ReadFloatFromScript(m_pCurrentIP); + newScript->m_aLocalVars[i].fParam = CTheScripts::ReadFloatFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY: ReadArrayInformation(true, &arrVarOffset, &arrElemIdx); @@ -698,23 +754,23 @@ void CRunningScript::ReadTextLabelFromScript(char* buffer, uint8 nBufferLength) uint16 arrVarOffset; int32 arrElemIdx; - int8 type = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + int8 type = CTheScripts::Read1ByteFromScript(m_IP); switch (type) { case SCRIPT_PARAM_STATIC_SHORT_STRING: for (auto i = 0; i < SHORT_STRING_SIZE; i++) - buffer[i] = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + buffer[i] = CTheScripts::Read1ByteFromScript(m_IP); break; case SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); - strncpy_s(buffer, SHORT_STRING_SIZE, (char*) & CTheScripts::ScriptSpace[index], SHORT_STRING_SIZE); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); + strncpy_s(buffer, SHORT_STRING_SIZE, (char*)&CTheScripts::ScriptSpace[index], SHORT_STRING_SIZE); break; } case SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); strncpy_s(buffer, SHORT_STRING_SIZE, (char*) GetPointerToLocalVariable(index), SHORT_STRING_SIZE); break; } @@ -741,9 +797,9 @@ void CRunningScript::ReadTextLabelFromScript(char* buffer, uint8 nBufferLength) case SCRIPT_PARAM_STATIC_PASCAL_STRING: { - int16 nStringLen = CTheScripts::Read1ByteFromScript(m_pCurrentIP); // sign extension. max size = 127, not 255 + int16 nStringLen = CTheScripts::Read1ByteFromScript(m_IP); // sign extension. max size = 127, not 255 for (auto i = 0; i < nStringLen; i++) - buffer[i] = CTheScripts::Read1ByteFromScript(m_pCurrentIP); + buffer[i] = CTheScripts::Read1ByteFromScript(m_IP); if (nStringLen < nBufferLength) memset(&buffer[(uint8)nStringLen], 0, (uint8)(nBufferLength - nStringLen)); @@ -753,20 +809,20 @@ void CRunningScript::ReadTextLabelFromScript(char* buffer, uint8 nBufferLength) case SCRIPT_PARAM_STATIC_LONG_STRING: // slightly changed code: original code is a bit messy and calls Read1ByteFromScript // in a loop and does some additional checks to ensure that buffer can hold the data - strncpy_s(buffer, LONG_STRING_SIZE, (char*) m_pCurrentIP, std::min(nBufferLength, LONG_STRING_SIZE)); - m_pCurrentIP += LONG_STRING_SIZE; + strncpy_s(buffer, LONG_STRING_SIZE, (char*)m_IP, std::min(nBufferLength, LONG_STRING_SIZE)); + m_IP += LONG_STRING_SIZE; break; case SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); strncpy_s(buffer, LONG_STRING_SIZE, (char*) & CTheScripts::ScriptSpace[index], std::min(nBufferLength, LONG_STRING_SIZE)); break; } case SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE: { - uint16 index = CTheScripts::Read2BytesFromScript(m_pCurrentIP); + uint16 index = CTheScripts::Read2BytesFromScript(m_IP); strncpy_s(buffer, LONG_STRING_SIZE, (char*) GetPointerToLocalVariable(index), std::min(nBufferLength, LONG_STRING_SIZE)); break; } @@ -812,9 +868,9 @@ void CRunningScript::UpdateCompareFlag(bool state) { // 0x464DA0 void CRunningScript::UpdatePC(int32 newIP) { if (newIP >= 0) - m_pCurrentIP = &CTheScripts::ScriptSpace[newIP]; + m_IP = &CTheScripts::ScriptSpace[newIP]; else - m_pCurrentIP = &m_pBaseIP[-newIP]; + m_IP = m_pBaseIP + std::abs(newIP); } // 0x469EB0, inlined @@ -827,13 +883,15 @@ OpcodeResult CRunningScript::ProcessOneCommand() { uint16 command : 15; uint16 notFlag : 1; }; - } op = { CTheScripts::Read2BytesFromScript(m_pCurrentIP) }; + } op = { CTheScripts::Read2BytesFromScript(m_IP) }; m_bNotFlag = op.notFlag; - //return std::invoke(CommandHandlerTable[(size_t)op.command / 100], this, (eScriptCommands)op.command); - - return std::invoke(s_CommandHandlerLUT[(size_t)op.command], this); + if (const auto handler = CustomCommandHandlerOf((eScriptCommands)(op.command))) { + return std::invoke(handler, this); + } else { + return std::invoke(s_OriginalCommandHandlerTable[(size_t)op.command / 100], this, (eScriptCommands)(op.command)); + } } // 0x469F00 @@ -849,22 +907,38 @@ OpcodeResult CRunningScript::Process() { DoDeathArrestCheck(); if (m_bIsMission && CTheScripts::FailCurrentMission == 1) { - while (m_nSP > 1) // // todo: refactor | inline(?): while (stack.size > 1) { stack.pop() } - --m_nSP; + while (m_StackDepth > 1) // // todo: refactor | inline(?): while (stack.size > 1) { stack.pop() } + --m_StackDepth; - if (m_nSP == 1) { - m_nSP = 0; - m_pCurrentIP = m_apStack[0]; + if (m_StackDepth == 1) { + m_StackDepth = 0; + m_IP = m_IPStack[0]; } } CTheScripts::ReinitialiseSwitchStatementData(); if (CTimer::GetTimeInMS() >= (uint32)m_nWakeTime) { - while (!ProcessOneCommand()); // Process commands + while (ProcessOneCommand() == OR_CONTINUE); // Process commands } return OR_CONTINUE; } -void CRunningScript::SetCommandHandler(eScriptCommands cmd, OpcodeResult(*handler)(CRunningScript*)) { - s_CommandHandlerLUT[(size_t)cmd] = handler; +void CRunningScript::HighlightImportantArea(CVector2D from, CVector2D to, float z) { + CTheScripts::HighlightImportantArea(reinterpret_cast(this) + reinterpret_cast(m_IP), from.x, from.y, to.x, to.y, z); +} + +void CRunningScript::HighlightImportantArea(CRect area, float z) { + HighlightImportantArea(area.GetTopLeft(), area.GetBottomRight(), z); +} + +void CRunningScript::HighlightImportantArea(CVector from, CVector to) { + HighlightImportantArea(CVector2D{ from }, CVector2D{ to }, (from.z + to.z) / 2.f); +} + +void CRunningScript::HighlightImportantAngledArea(uint32 id, CVector2D a, CVector2D b, CVector2D c, CVector2D d) { + NOTSA_UNREACHABLE(); // Fuck this, we dont need it! +} + +notsa::script::CommandHandlerFunction& CRunningScript::CustomCommandHandlerOf(eScriptCommands command) { + return s_CustomCommandHandlerTable[(size_t)(command)]; } diff --git a/source/game_sa/Scripts/RunningScript.h b/source/game_sa/Scripts/RunningScript.h index c51112ef78..2737b66fcc 100644 --- a/source/game_sa/Scripts/RunningScript.h +++ b/source/game_sa/Scripts/RunningScript.h @@ -10,14 +10,16 @@ #include "OpcodeResult.h" #include "eWeaponType.h" #include "Ped.h" +#include "CommandParser/Utility.hpp" enum ePedType : uint32; -enum eScriptParameterType { - SCRIPT_PARAM_END_OF_ARGUMENTS, +enum eScriptParameterType : int8 { + SCRIPT_PARAM_END_OF_ARGUMENTS, //< Special type used for vararg stuff + SCRIPT_PARAM_STATIC_INT_32BITS, - SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE, - SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE, + SCRIPT_PARAM_GLOBAL_NUMBER_VARIABLE, //< Global int32 variable + SCRIPT_PARAM_LOCAL_NUMBER_VARIABLE, //< Local int32 variable SCRIPT_PARAM_STATIC_INT_8BITS, SCRIPT_PARAM_STATIC_INT_16BITS, SCRIPT_PARAM_STATIC_FLOAT, @@ -25,25 +27,25 @@ enum eScriptParameterType { // Types below are only available in GTA SA // Number arrays - SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY, - SCRIPT_PARAM_LOCAL_NUMBER_ARRAY, + SCRIPT_PARAM_GLOBAL_NUMBER_ARRAY, //< Global array of numbers (always int32) + SCRIPT_PARAM_LOCAL_NUMBER_ARRAY, //< Local array of numbers (always int32) - SCRIPT_PARAM_STATIC_SHORT_STRING, + SCRIPT_PARAM_STATIC_SHORT_STRING, //< Static 8 byte string - SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE, - SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE, + SCRIPT_PARAM_GLOBAL_SHORT_STRING_VARIABLE, //< Local 8 byte string + SCRIPT_PARAM_LOCAL_SHORT_STRING_VARIABLE, //< Local 8 byte string - SCRIPT_PARAM_GLOBAL_SHORT_STRING_ARRAY, - SCRIPT_PARAM_LOCAL_SHORT_STRING_ARRAY, + SCRIPT_PARAM_GLOBAL_SHORT_STRING_ARRAY, //< Global 8 byte string array + SCRIPT_PARAM_LOCAL_SHORT_STRING_ARRAY, //< Local 8 byte string array - SCRIPT_PARAM_STATIC_PASCAL_STRING, - SCRIPT_PARAM_STATIC_LONG_STRING, + SCRIPT_PARAM_STATIC_PASCAL_STRING, //< Pascal string is a sequence of characters with optional size specification. (So says Google) + SCRIPT_PARAM_STATIC_LONG_STRING, //< 16 byte string - SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE, - SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE, + SCRIPT_PARAM_GLOBAL_LONG_STRING_VARIABLE, //< Global 16 byte string + SCRIPT_PARAM_LOCAL_LONG_STRING_VARIABLE, //< Local 16 byte string - SCRIPT_PARAM_GLOBAL_LONG_STRING_ARRAY, - SCRIPT_PARAM_LOCAL_LONG_STRING_ARRAY, + SCRIPT_PARAM_GLOBAL_LONG_STRING_ARRAY, //< Global array of 16 byte strings + SCRIPT_PARAM_LOCAL_LONG_STRING_ARRAY, //< Local array of 16 byte strings }; enum eScriptVariableType : uint8 { @@ -104,6 +106,7 @@ constexpr auto SHORT_STRING_SIZE = 8; constexpr auto LONG_STRING_SIZE = 16; class CRunningScript { +public: /*! * Needed for compound if statements. * Basically, an `if` translates to: @@ -125,7 +128,7 @@ class CRunningScript { * result and the ANDOR state is decremented until it reaches the lower bound, * meaning that all conditions were tested. */ - enum { + enum LogicalOpType { ANDOR_NONE = 0, ANDS_1 = 1, ANDS_2, @@ -146,36 +149,36 @@ class CRunningScript { }; public: - CRunningScript* m_pNext; - CRunningScript* m_pPrev; - char m_szName[8]; - uint8* m_pBaseIP; // base instruction pointer - uint8* m_pCurrentIP; // current instruction pointer - uint8* m_apStack[MAX_STACK_DEPTH]; - uint16 m_nSP; // Stack Pointer - tScriptParam m_aLocalVars[NUM_LOCAL_VARS]; - int32 m_anTimers[NUM_TIMERS]; - bool m_bIsActive; - bool m_bCondResult; ///< Used for `COMMAND_GOTO_IF_FALSE` - bool m_bUseMissionCleanup; + CRunningScript *m_pNext, *m_pPrev; //< Linked list shit + char m_szName[8]; //< Name of the script + uint8* m_pBaseIP; //< Base instruction pointer + uint8* m_IP; //< current instruction pointer + uint8* m_IPStack[MAX_STACK_DEPTH]; //< Stack of instruction pointers (Usually saved on function call, then popped on return) + uint16 m_StackDepth; //< Depth (size) of the stack + tScriptParam m_aLocalVars[NUM_LOCAL_VARS]; //< This script's local variables (Also see `GetPointerToLocalVariable`) + int32 m_anTimers[NUM_TIMERS]; //< Active timers (Unsure) + bool m_bIsActive; //< Is the script active (Unsure) + bool m_bCondResult; //< (See `COMMAND_GOTO_IF_FALSE`) (Unsure) + bool m_bUseMissionCleanup; //< If mission cleanup is needed after this script has finished bool m_bIsExternal; bool m_bTextBlockOverride; int8 m_nExternalType; - int32 m_nWakeTime; - uint16 m_nLogicalOp; - bool m_bNotFlag; + int32 m_nWakeTime; //< Used for sleep-like comamands (like `COMMAND_WAIT`) - The script halts execution until the time is reached + uint16 m_nLogicalOp; //< Next logical OP type (See `COMMAND_ANDOR`) + bool m_bNotFlag; //< Condition result is to be negated (Unsure) bool m_bDeathArrestEnabled; bool m_bDeathArrestExecuted; - uint8* m_pSceneSkipIP; // scene skip instruction pointer - bool m_bIsMission; + uint8* m_pSceneSkipIP; //< Scene skip instruction pointer (Unsure) + bool m_bIsMission; //< Is (this script) a mission script +public: using CommandHandlerFn_t = OpcodeResult(__thiscall CRunningScript::*)(int32); using CommandHandlerTable_t = std::array; - static inline CommandHandlerTable_t& CommandHandlerTable = *(CommandHandlerTable_t*)0x8A6168; - + static inline CommandHandlerTable_t& s_OriginalCommandHandlerTable = *(CommandHandlerTable_t*)0x8A6168; public: static void InjectHooks(); + static void InjectCustomCommandHooks(); void Init(); @@ -203,7 +206,7 @@ class CRunningScript { void ReadArrayInformation(int32 updateIp, uint16* outArrVarOffset, int32* outArrElemIdx); void ReadParametersForNewlyStartedScript(CRunningScript* newScript); void ReadTextLabelFromScript(char* buffer, uint8 nBufferLength); - void GetCorrectPedModelIndexForEmergencyServiceType(ePedType pedType, int32* outModelId); + void GetCorrectPedModelIndexForEmergencyServiceType(ePedType pedType, uint32* typeSpecificModelId); int16 GetPadState(uint16 playerIndex, eButtonId buttonId); tScriptParam* GetPointerToLocalVariable(int32 varId); @@ -213,8 +216,9 @@ class CRunningScript { void DoDeathArrestCheck(); // original name DoDeatharrestCheck - void SetCharCoordinates(CPed* ped, float x, float y, float z, bool bWarpGang, bool bOffset); + static void SetCharCoordinates(CPed& ped, CVector posn, bool warpGang, bool offset); void GivePedScriptedTask(int32 pedHandle, CTask* task, int32 opcode); + void GivePedScriptedTask(CPed* ped, CTask* task, int32 opcode); // NOTSA overload void AddScriptToList(CRunningScript** queueList); void RemoveScriptFromList(CRunningScript** queueList); @@ -233,17 +237,32 @@ class CRunningScript { void SetName(const char* name) { strcpy_s(m_szName, name); } void SetName(std::string_view name) { assert(name.size() < sizeof(m_szName)); strncpy_s(m_szName, name.data(), name.size()); } void SetBaseIp(uint8* ip) { m_pBaseIP = ip; } - void SetCurrentIp(uint8* ip) { m_pCurrentIP = ip; } + void SetCurrentIp(uint8* ip) { m_IP = ip; } void SetActive(bool active) { m_bIsActive = active; } void SetExternal(bool external) { m_bIsExternal = external; } - template - OpcodeResult ProcessCommand() { - // By default call original GTA handler - return std::invoke(CommandHandlerTable[(size_t)Command / 100], this, Command); + //! Highlight an important area 2D + void HighlightImportantArea(CVector2D from, CVector2D to, float z = -100.f); + + //! Highlight an important area 2D + void HighlightImportantArea(CRect area, float z = -100.f); + + //! Highlight an important area 3D + void HighlightImportantArea(CVector from, CVector to); + + //! Refer to `IsPositionWithinQuad2D` for information on how this works + void HighlightImportantAngledArea(uint32 id, CVector2D a, CVector2D b, CVector2D c, CVector2D d); + + //! Read a value from at the current IP then increase IP by the number of bytes read. + template + T ReadAtIPAs() { + const auto ret = *reinterpret_cast(m_IP); + m_IP += sizeof(T); + return ret; } - static void SetCommandHandler(eScriptCommands cmd, OpcodeResult(*handler)(CRunningScript*)); + //! Return the custom command handler of a function (or null) as a reference + static notsa::script::CommandHandlerFunction& CustomCommandHandlerOf(eScriptCommands command); // Returning a ref here for convinience (instead of having to make a `Set` function too) }; VALIDATE_SIZE(CRunningScript, 0xE0); diff --git a/source/game_sa/Scripts/Scripted2dEffects.cpp b/source/game_sa/Scripts/Scripted2dEffects.cpp index 6634e67ad6..161fb7273d 100644 --- a/source/game_sa/Scripts/Scripted2dEffects.cpp +++ b/source/game_sa/Scripts/Scripted2dEffects.cpp @@ -51,9 +51,9 @@ void CScripted2dEffects::ReturnScripted2DEffect(int32 index) { } auto CScripted2dEffects::IndexOfEffect(const C2dEffect* effect) -> std::optional { - const auto idx = std::distance(ms_effects.data(), effect); - if (idx >= 0 && static_cast(idx) <= ms_effects.size()) { - return static_cast(idx); + const auto idx = effect - ms_effects.data(); + if (idx >= 0 && idx <= (ptrdiff_t)ms_effects.size()) { + return (size_t)idx; } return std::nullopt; } diff --git a/source/game_sa/Scripts/Scripted2dEffects.h b/source/game_sa/Scripts/Scripted2dEffects.h index 89aecfe611..146391997f 100644 --- a/source/game_sa/Scripts/Scripted2dEffects.h +++ b/source/game_sa/Scripts/Scripted2dEffects.h @@ -52,7 +52,7 @@ VALIDATE_SIZE(tUserList, 0x24); class CScripted2dEffects { public: - static inline std::array& ms_effects = *(std::array*)0xC3AB00; // class maybe + static inline std::array& ms_effects = *(std::array*)0xC3AB00; // class maybe static inline std::array& ms_effectPairs = *(std::array*)0xC3BB00; static inline std::array& ms_userLists = *(std::array*)0xC3A200; // class maybe static inline std::array& ms_activated = *(std::array*)0xC3A1A0; diff --git a/source/game_sa/Scripts/TheScripts.cpp b/source/game_sa/Scripts/TheScripts.cpp index 288b40afbc..de45c6b451 100644 --- a/source/game_sa/Scripts/TheScripts.cpp +++ b/source/game_sa/Scripts/TheScripts.cpp @@ -25,6 +25,7 @@ void CTheScripts::InjectHooks() { RH_ScopedInstall(StartTestScript, 0x464D40); RH_ScopedInstall(AddToBuildingSwapArray, 0x481140); RH_ScopedInstall(UndoBuildingSwaps, 0x481290); + RH_ScopedInstall(IsPedStopped, 0x486110); RH_ScopedInstall(HasCarModelBeenSuppressed, 0x46A810); RH_ScopedInstall(HasVehicleModelBeenBlockedByScript, 0x46A890); } @@ -307,6 +308,28 @@ void CTheScripts::UndoBuildingSwaps() { } } +// 0x486110 +bool CTheScripts::IsPedStopped(CPed* ped) { + if (ped->IsInVehicle()) { + return CTimer::GetTimeStep() / 100.f >= ped->m_pVehicle->m_fMovingSpeed; + } + if (!ped->IsPedStandingInPlace()) { + return false; + } + if (ped->IsPlayer()) { + if (RpAnimBlendClumpGetAssociation(ped->m_pRwClump, { ANIM_ID_RUN_STOP, ANIM_ID_RUN_STOPR, ANIM_ID_JUMP_LAUNCH, ANIM_ID_JUMP_GLIDE })) { + return false; + } + } + if (ped->bIsLanding || ped->bIsInTheAir || !ped->bIsStanding) { + return false; + } + if (ped->m_vecAnimMovingShiftLocal.IsZero()) { + return true; + } + return false; +} + // 0x464D50 bool CTheScripts::IsPlayerOnAMission() { return plugin::CallAndReturn(); diff --git a/source/game_sa/Scripts/TheScripts.h b/source/game_sa/Scripts/TheScripts.h index 9eae22146c..9460ccfc43 100644 --- a/source/game_sa/Scripts/TheScripts.h +++ b/source/game_sa/Scripts/TheScripts.h @@ -18,9 +18,8 @@ #include "StuckCarCheck.h" #include "UpsideDownCarCheck.h" #include "ScriptsForBrains.h" -class CCheckpoint; -enum eScriptParameterType; +class CCheckpoint; enum class eCrossHairType : uint32 { NONE, @@ -79,6 +78,12 @@ struct tScriptCheckpoint { m_nId = 1; m_Checkpoint = nullptr; } + + //! Get script thing ID + auto GetId() const { return m_nId; } + + //! If `*this` is currently in use + auto IsActive() const { return m_bUsed; } }; VALIDATE_SIZE(tScriptCheckpoint, 0x8); @@ -92,6 +97,12 @@ struct tScriptEffectSystem { m_nId = 1; m_pFxSystem = nullptr; } + + //! Get script thing ID + auto GetId() const { return m_nId; } + + //! If `*this` is currently in use + auto IsActive() const { return m_bUsed; } }; VALIDATE_SIZE(tScriptEffectSystem, 0x8); @@ -103,6 +114,12 @@ struct tScriptSequence { m_bUsed = false; m_nId = 1; } + + //! Get script thing ID + auto GetId() const { return m_nId; } + + //! If `*this` is currently in use + auto IsActive() const { return m_bUsed; } }; VALIDATE_SIZE(tScriptSequence, 0x4); @@ -216,6 +233,12 @@ struct tScriptSearchlight { CVector m_TargetSpot{}; CVector vf64{}; CVector vf70{}; + + //! Script thing ID + auto GetId() { return m_nId; } + + //! If `*this` is currently in use + auto IsActive() const { return m_bUsed; } }; VALIDATE_SIZE(tScriptSearchlight, 0x7C); @@ -242,6 +265,12 @@ struct tScriptSphere { m_nId = 0; m_fRadius = 0.0f; } + + //! Get script thing ID + auto GetId() const { return m_nUniqueId; } + + //! If `*this` is currently in use + auto IsActive() const { return m_bUsed; } }; VALIDATE_SIZE(tScriptSphere, 0x18); @@ -320,11 +349,7 @@ class CTheScripts { static inline uint16& NumberOfEntriesInSwitchTable = *reinterpret_cast(0xA43F50); static inline uint16& NumberOfEntriesStillToReadForSwitch = *reinterpret_cast(0xA43F60); - static inline std::array& ScriptCheckpointArray = *(std::array*)0xA44070; - static inline uint16& NumberOfScriptCheckpoints = *reinterpret_cast(0xA44068); - static inline std::array& ScriptEffectSystemArray = *(std::array*)0xA44110; - static inline std::array& ScriptSequenceTaskArray = *(std::array*)0xA43F68; static inline std::array& CardStack = *(std::array*)0xA44218; static inline std::array& MultiScriptArray = *(std::array*)0xA444C8; static inline std::array& ScriptConnectLodsObjects = *(std::array*)0xA44800; @@ -343,8 +368,6 @@ class CTheScripts { static inline std::array& EntitiesWaitingForScriptBrain = *(std::array*)0xA476B0; static inline std::array& ScriptsArray = *(std::array*)0xA8B430; - static inline std::array& ScriptSphereArray = *(std::array*)0xA91268; - static inline std::array& IntroTextLines = *(std::array*)0xA913E8; static inline uint16& NumberOfIntroTextLinesThisFrame = *reinterpret_cast(0xA44B68); @@ -353,12 +376,23 @@ class CTheScripts { static inline std::array& ScriptSprites = *(std::array*)0xA94B68; - static inline std::array& ScriptSearchLightArray = *(std::array*)0xA94D68; - static inline uint16& NumberOfScriptSearchLights = *reinterpret_cast(0xA90830); - static inline uint16& NumberOfExclusiveMissionScripts = *reinterpret_cast(0xA444B8); static inline uint16& NumberOfMissionScripts = *reinterpret_cast(0xA444BC); + // + // Script things + // + + static inline std::array& ScriptSphereArray = *(std::array*)0xA91268; + static inline std::array& ScriptEffectSystemArray = *(std::array*)0xA44110; + static inline std::array& ScriptSearchLightArray = *(std::array*)0xA94D68; + + static inline std::array& ScriptSequenceTaskArray = *(std::array*)0xA43F68; + static inline uint16& NumberOfScriptSearchLights = *reinterpret_cast(0xA90830); + + static inline std::array& ScriptCheckpointArray = *(std::array*)0xA44070; + static inline uint16& NumberOfScriptCheckpoints = *reinterpret_cast(0xA44068); + static inline bool& DbgFlag = *reinterpret_cast(0x859CF8); static inline void*& SwitchDefaultAddress = *reinterpret_cast(0xA43F54); static inline bool& SwitchDefaultExists = *reinterpret_cast(0xA43F58); diff --git a/source/game_sa/SearchLight.cpp b/source/game_sa/SearchLight.cpp new file mode 100644 index 0000000000..20300770f9 --- /dev/null +++ b/source/game_sa/SearchLight.cpp @@ -0,0 +1,29 @@ +#include "StdInc.h" +#include "SearchLight.h" + +void CSearchLight::InjectHooks() {} + +void CSearchLight::SetTravelToPoint() { + assert(0); +} + +void CSearchLight::SetFollowEntity() { + assert(0); +} + +void CSearchLight::SetPathBetween() { + assert(0); +} + +void CSearchLight::IsLookingAtPos() { + assert(0); +} + +void CSearchLight::GetOnEntity() { + assert(0); +} + +// 0x493900 +bool CSearchLight::IsSpottedEntity(uint32 index, const CEntity& entity) { + return plugin::CallAndReturn < bool, 0x493900, uint32, const CEntity&>(index, entity); +} diff --git a/source/game_sa/SearchLight.h b/source/game_sa/SearchLight.h new file mode 100644 index 0000000000..547c8d5153 --- /dev/null +++ b/source/game_sa/SearchLight.h @@ -0,0 +1,15 @@ +#pragma once +#include "Base.h" +class CEntity; + +class CSearchLight { +public: + + static void InjectHooks(); + static void SetTravelToPoint(); + static void SetFollowEntity(); + static void SetPathBetween(); + static void IsLookingAtPos(); + static void GetOnEntity(); + static bool IsSpottedEntity(uint32 index, const CEntity& entity); +}; diff --git a/source/game_sa/Tasks/TaskManager.h b/source/game_sa/Tasks/TaskManager.h index 36c98cae48..17e436ff88 100644 --- a/source/game_sa/Tasks/TaskManager.h +++ b/source/game_sa/Tasks/TaskManager.h @@ -220,6 +220,17 @@ class CTaskManager { return nullptr; } + /*! + * @notsa + * @brief Check if the simplest active task is any of the given types + */ + bool IsSimplestActiveTaskOfType(std::initializer_list types) { + if (const auto task = GetSimplestActiveTask()) { + return notsa::contains(types, task->GetTaskType()); + } + return false; + } + /*! * @notsa * @brief Find an active task from the give types and return the first one. diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexUseAttractor.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexUseAttractor.cpp index 2abdd08832..2b6f4ff1c3 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexUseAttractor.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexUseAttractor.cpp @@ -83,7 +83,7 @@ CTask* CTaskComplexUseAttractor::CreateFirstSubTask(CPed* ped) { } if (const auto entity = m_attractor->m_pEntity) { - if (!entity->IsObject() || entity->AsObject()->objectFlags.b0x1000000) { + if (!entity->IsObject() || entity->AsObject()->objectFlags.bEnableDisabledAttractors) { CTheScripts::ScriptsForBrains.StartAttractorScriptBrainWithThisName(m_attractor->m_szScriptName, ped, ped->bUseAttractorInstantly); CScriptedBrainTaskStore::Clear(ped); return new CTaskComplexUseScriptedBrain{ m_attractor->m_szScriptName }; diff --git a/source/game_sa/TheZones.cpp b/source/game_sa/TheZones.cpp index 8c3c2fecb8..8b7e5492c1 100644 --- a/source/game_sa/TheZones.cpp +++ b/source/game_sa/TheZones.cpp @@ -136,6 +136,27 @@ bool CTheZones::FindZone(CVector* point, int32 zonename_part1, int32 zonename_pa return ((bool(__cdecl*)(CVector*, int32, int32, eZoneType))0x572B80)(point, zonename_part1, zonename_part2, type); } +//! Find zone by name `name` and of type `type` and check if `point` lies within it +bool CTheZones::FindZone(const CVector& point, std::string_view name, eZoneType type) { + switch (type) { + case ZONE_TYPE_INFO: + case ZONE_TYPE_NAVI: + break; + default: + return false; + } + + for (auto& zone : GetNavigationZones()) { + if ((type == ZONE_TYPE_INFO ? zone.GetInfoLabel() : zone.GetNaviLabel()) == name) { + if (PointLiesWithinZone(&point, &zone)) { + return true; + } + } + } + + return false; +} + // Returns pointer to zone by index // 0x572C40 int16 CTheZones::FindZoneByLabel(const char* name, eZoneType type) { diff --git a/source/game_sa/TheZones.h b/source/game_sa/TheZones.h index 2310d6ef81..08d1740881 100644 --- a/source/game_sa/TheZones.h +++ b/source/game_sa/TheZones.h @@ -49,7 +49,11 @@ class CTheZones { static void Init(); static void SetCurrentZoneAsUnlocked(); static void CreateZone(const char* name, eZoneType type, float posX1, float posY1, float posZ1, float posX2, float posY2, float posZ2, eLevelName island, const char* GXT_key); + static bool FindZone(CVector* point, int32 zonename_part1, int32 zonename_part2, eZoneType type); + + + static bool FindZone(const CVector& point, std::string_view name, eZoneType type); static int16 FindZoneByLabel(const char* name, eZoneType type); static void SetZoneRadarColours(int16 index, char flag, uint8 red, uint8 green, uint8 blue); @@ -70,6 +74,9 @@ class CTheZones { return &ZoneInfoArray[idx]; } + [[deprecated]] + static auto GetNaviZones() { return std::span{ NavigationZoneArray, (size_t)(TotalNumberOfNavigationZones) }; } + static auto GetNavigationZones() { return std::span{NavigationZoneArray, (size_t)TotalNumberOfNavigationZones}; } diff --git a/source/game_sa/TrafficLights.cpp b/source/game_sa/TrafficLights.cpp index 3f4947419f..dff463dded 100644 --- a/source/game_sa/TrafficLights.cpp +++ b/source/game_sa/TrafficLights.cpp @@ -187,10 +187,10 @@ void CTrafficLights::DisplayActualLight(CEntity* entity) { CVector vecCenter(0.0F, 0.0F, 0.0F); for (int32 iFxInd = 0; iFxInd < mi->m_n2dfxCount; ++iFxInd) { auto effect = mi->Get2dEffect(iFxInd); - if (effect->m_nType != e2dEffectType::EFFECT_LIGHT) + if (effect->m_type != e2dEffectType::EFFECT_LIGHT) continue; - auto vecLightPos = entity->GetMatrix() * effect->m_vecPosn; + auto vecLightPos = entity->GetMatrix() * effect->m_pos; vecCenter += vecLightPos; int32 iColorState = eTrafficLightsState::LIGHT_GREEN; if (effect->light.m_color.red > 200) { @@ -200,7 +200,7 @@ void CTrafficLights::DisplayActualLight(CEntity* entity) { iColorState = eTrafficLightsState::LIGHT_RED; } - if (bSameDir == effect->m_vecPosn.y > 0.0F || iColorState != iLightState) + if (bSameDir == effect->m_pos.y > 0.0F || iColorState != iLightState) continue; auto fBrightness = CTimeCycle::m_CurrentColours.m_fSpriteBrightness * 0.07F; diff --git a/source/game_sa/World.h b/source/game_sa/World.h index c126239828..318a34096c 100644 --- a/source/game_sa/World.h +++ b/source/game_sa/World.h @@ -264,6 +264,12 @@ class CWorld { std::forward(fn) ); } + // @notsa + static void PutToGroundIfTooLow(CVector& pos) { + if (pos.z <= MAP_Z_LOW_LIMIT) { + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + } + } }; extern uint32 &FilledColPointIndex; diff --git a/source/game_sa/Zone.cpp b/source/game_sa/Zone.cpp index 869485dd68..7b26e23fea 100644 --- a/source/game_sa/Zone.cpp +++ b/source/game_sa/Zone.cpp @@ -5,5 +5,5 @@ // Returns pointer to GXT name string. // 0x5720C0 const char* CZone::GetTranslatedName() const { - return TheText.Get(m_szTextKey); + return TheText.Get(m_TextLabel); } diff --git a/source/game_sa/Zone.h b/source/game_sa/Zone.h index b6a2bcc716..b6880dcaa1 100644 --- a/source/game_sa/Zone.h +++ b/source/game_sa/Zone.h @@ -10,14 +10,21 @@ #include "eLevelName.h" enum eZoneType : uint8 { - ZONE_TYPE_NAVI = 0, // controls text s - ZONE_TYPE_MAP = 3 + ZONE_TYPE_NAVI, + ZONE_TYPE_LOCAL_NAVI, + ZONE_TYPE_INFO, + ZONE_TYPE_MAP, }; class CZone { public: - uint32 m_nLabel[2]; - char m_szTextKey[8]; + auto GetTranslatedName() const -> const char*; + auto GetInfoLabel() const { return std::string_view{ m_InfoLabel }; } + auto GetNaviLabel() const { return std::string_view{ m_TextLabel }; } + auto GetRect() const { return CRect{ (float)m_fX1, (float)m_fY1, (float)m_fX2, (float)m_fY2 }; } +public: + char m_InfoLabel[8]; // Zone info `TheText.Get` key (Unsure) + char m_TextLabel[8]; // Display name `TheText.Get` key int16 m_fX1; int16 m_fY1; int16 m_fZ1; @@ -28,12 +35,6 @@ class CZone { eZoneType m_nType; eLevelName m_nLevel; - const char* GetTranslatedName() const; - // NOTSA - CRect GetRect() const { - return {(float)m_fX1, (float)m_fY1, (float)m_fX2, (float)m_fY2}; - } }; - VALIDATE_SIZE(CZone, 0x20); diff --git a/source/reversiblehooks/ReversibleHook/ScriptCommand.h b/source/reversiblehooks/ReversibleHook/ScriptCommand.h index 0c60eb1ba4..6e3cb4fab1 100644 --- a/source/reversiblehooks/ReversibleHook/ScriptCommand.h +++ b/source/reversiblehooks/ReversibleHook/ScriptCommand.h @@ -2,38 +2,36 @@ #include "Base.h" #include "eScriptCommands.h" -#include "Scripts/CommandParser/LUTGenerator.hpp" #ifdef ENABLE_SCRIPT_COMMAND_HOOKS namespace ReversibleHooks { namespace ReversibleHook { -template struct ScriptCommand : Base { - ScriptCommand(bool locked = false, bool enabledByDefault = true) : - Base{ std::string{::notsa::script::GetScriptCommandName(Command)}, Base::HookType::ScriptCommand, locked } + ScriptCommand(eScriptCommands command, bool locked = false, bool enabledByDefault = true) : + Base{ std::string{::notsa::script::GetScriptCommandName(command)}, Base::HookType::ScriptCommand, locked }, + m_cmd{command}, + m_originalHandler{CRunningScript::CustomCommandHandlerOf(command)} { - Switch(); // Install hook - - if (!enabledByDefault) { - Switch(); // Uninstall + m_bIsHooked = true; // Enabled by default + if (m_bIsHooked && !enabledByDefault) { + Switch(); // Uninstall it } } + ~ScriptCommand() override = default; void Switch() override { using namespace ::notsa::script; m_bIsHooked = !m_bIsHooked; - CRunningScript::SetCommandHandler( - Command, - m_bIsHooked - ? GetHandlerOfCommand() // When hooked use custom handler (Which might still fallback to the GTA handler in case we have no custom handler) - : >AProcessCommand // When unhooked use GTA handler - ); + CRunningScript::CustomCommandHandlerOf(m_cmd) = m_bIsHooked ? m_originalHandler : nullptr; } void Check() override { /* nop */ } const char* Symbol() const override { return "C"; } +private: + eScriptCommands m_cmd{}; + ::notsa::script::CommandHandlerFunction m_originalHandler{}; }; }; }; diff --git a/source/toolsmenu/DebugModules/DebugModules.cpp b/source/toolsmenu/DebugModules/DebugModules.cpp index 61983cb880..6229ba6907 100644 --- a/source/toolsmenu/DebugModules/DebugModules.cpp +++ b/source/toolsmenu/DebugModules/DebugModules.cpp @@ -22,6 +22,7 @@ #include "./TextDebugModule.h" #include "./Spawner/SpawnerDebugModule.hpp" #include "./ImGuiDebugModule.hpp" +#include "./ScriptDebugModule.hpp" DebugModules::DebugModules(ImGuiContext* ctx) : m_ImCtx(ctx) @@ -79,6 +80,7 @@ void DebugModules::CreateModules() { Add(); Add(); Add(); + Add(); // Stuff that is present in multiple menus Add(); // Visualization + Extra diff --git a/source/toolsmenu/DebugModules/ScriptDebugModule.cpp b/source/toolsmenu/DebugModules/ScriptDebugModule.cpp new file mode 100644 index 0000000000..503853d75a --- /dev/null +++ b/source/toolsmenu/DebugModules/ScriptDebugModule.cpp @@ -0,0 +1,94 @@ +#include +#include "ScriptDebugModule.hpp" +#include "TheScripts.h" + +namespace notsa { +namespace debugmodules { +void ScriptDebugModule::RenderWindow() { + const ::notsa::ui::ScopedWindow window{ "Scripts", {}, m_IsOpen }; + if (!m_IsOpen) { + return; + } + using namespace ImGui; + Checkbox("DbgFlag", &CTheScripts::DbgFlag); +} + +void ScriptDebugModule::RenderMenuEntry() { + notsa::ui::DoNestedMenuIL({ "Extra" }, [&] { + ImGui::MenuItem("Scripts", nullptr, &m_IsOpen); + }); +} + +void ScriptDebugModule::Render3D() { + /* + constexpr struct { + CVector2D a, b; + float rot; + } areas[]{ + //{{0.f, 0.f}, {5.f, 10.f}, 5.f} + + {{ 2198.686 / 10.f, -2180.832 / 10.f, }, { 2116.212 / 10.f, -2263.317 / 10.f, }, 82.0 / 10.f}, + //{{ 2172.36, -2207.176, }, { 2116.212, 2263.317, }, 82.0}, + //{{ 2198.686, -2180.832, }, { 2116.212, 2263.317, }, 82.0}, + //{{ 2198.686, -2180.832, }, { 2116.212, 2263.317, }, 82.0}, + //{{ 2167.955, -2253.054, }, { 2175.023, 2245.327, }, -11.5}, + //{{ 2161.927, -2243.2519,}, { 2168.71, 2237.79, }, 10.0}, + //{{ 2160.6587, -2244.4089,}, { 2168.71, 2237.79, }, 12.0}, + //{{ 2157.47, -2246.72, }, { 2152.737, 2251.8567, }, -10.0}, + //{{ 2160.728, -2254.586, }, { 2152.4, 2263.3, }, 10.0}, + //{{ 1222.799, -735.563, }, { 1348.407, 759.322, }, -100.0}, + //{{-1559.2874, 1337.1991,}, { -1562.5834, 1327.2573, }, -62.9096}, + //{{-1590.3267, 1336.8811,}, { -1607.2059, 1307.5653, }, -37.0915}, + //{{-1616.5742, 1319.3361, }, { -1632.8600, 1303.4518, }, -23.9075}, + //{{-1595.1842, 1316.6805, }, { -1603.4827, 1308.8937, }, -8.3514}, + //{{ 1048.4285, 2093.5213, }, { 1048.8224, 2081.8444, }, 15.0}, + //{{ 1055.9634, 2093.8372, }, { 1055.8059, 2081.0337, }, 10.0}, + //{{ 1055.1740, 2064.7097, }, { 1054.8353, 21099.8091,}, 15.0}, + //{{ 1018.6445, 2100.5955, }, { 1020.0024, 2145.0366, }, -40.0}, + //{{ 1030.1047, 2095.7146, }, { 1054.8175, 2098.7776, }, 30.0}, + }; + + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, RWRSTATE(FALSE)); + RwRenderStateSet(rwRENDERSTATESRCBLEND, RWRSTATE(rwBLENDSRCALPHA)); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, RWRSTATE(rwBLENDINVSRCALPHA)); + RwRenderStateSet(rwRENDERSTATECULLMODE, RWRSTATE(rwCULLMODECULLNONE)); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, RWRSTATE(TRUE)); + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RWRSTATE(NULL)); + + const CRGBA color[4]{ // from top left, clockwise + {255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {255, 255, 0, 255} + }; + for (auto& area : areas) { + const auto angle = CGeneral::LimitRadianAngle(CGeneral::GetRadianAngleBetweenPoints( + area.a.x, area.a.y, + area.b.x, area.b.y + ) + HALF_PI); + const CVector2D offset{ + +std::sin(angle) * area.rot, + -std::cos(angle) * area.rot, + }; + const auto isInArea = IsPositionInAngledArea(FindPlayerCoors(), area.a, area.b, area.rot); + CVector2D pos[]{ + //area.a - CVector2D{std::sin(angle + PI), -std::cos(angle + PI)} * area.rot, + area.a, + area.b, + area.b + CVector2D{std::sin(angle), -std::cos(angle)} * area.rot, + }; + RwIm3DVertex vertices[std::size(pos)]; + for (auto i = 0u; i < std::size(pos); i++) { + RwIm3DVertexSetPos(&vertices[i], pos[i].x, pos[i].y, CWorld::FindGroundZForCoord(pos[i].x, pos[i].y) + 2.f); + RwIm3DVertexSetRGBA(&vertices[i], color[i + isInArea].r, color[i + isInArea].g, color[i + isInArea].b, color[i].a); + } + RwImVertexIndex idxs[]{ 0, 1, 2 }; // , 0, 1, 3 + if (RwIm3DTransform(vertices, std::size(vertices), nullptr, rwIM3D_VERTEXRGBA)) { + RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, idxs, std::size(idxs)); + RwIm3DEnd(); + } + } + */ +} +}; // namespace debugmodules +}; // namespace notsa diff --git a/source/toolsmenu/DebugModules/ScriptDebugModule.hpp b/source/toolsmenu/DebugModules/ScriptDebugModule.hpp new file mode 100644 index 0000000000..7d601cff84 --- /dev/null +++ b/source/toolsmenu/DebugModules/ScriptDebugModule.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "DebugModule.h" + +namespace notsa { +namespace debugmodules { +class ScriptDebugModule final : public DebugModule { +public: + void RenderWindow() override; + void RenderMenuEntry() override; + void Render3D() override; + +private: + bool m_IsOpen{}; +}; +}; // namespace debugmodules +}; // namespace notsa + diff --git a/source/toolsmenu/UIRenderer.h b/source/toolsmenu/UIRenderer.h index 50782e0dea..7e1d35cc4d 100644 --- a/source/toolsmenu/UIRenderer.h +++ b/source/toolsmenu/UIRenderer.h @@ -34,8 +34,9 @@ class UIRenderer : public notsa::Singleton { //! Random code you want to run (Called from `PreRenderUpdate`) void DebugCode(); private: - friend void ::RenderEffects(); // For `Render3D()` - friend void ::Render2dStuff(); // For `DrawLoop()` + friend void ::RenderEffects(); // For `Render3D()` + friend void ::FrontendIdle(); // For `DrawLoop()` VVV + friend void ::Idle(void*); // For `DrawLoop()` Yes, called in 2 places, but they are never called in the same frame (As `Idle` is called when not in the menu only) friend void ::CPad::UpdatePads(); private: From bdb98d33256bcc2442eb97d3d5f99da2315bcdaa Mon Sep 17 00:00:00 2001 From: Pirulax Date: Thu, 9 Mar 2023 15:15:39 +0100 Subject: [PATCH 05/19] Premake5 refactors + Enable AVX2 (#540) * vectorextensions "AVX2" * Define `NOTSA_DEBUG` in debug mode --- libs/premake5.lua | 124 ++++++++++++++++++++++++++++++ premake5.lua | 178 ++++++-------------------------------------- source/premake5.lua | 62 +++++++++++++++ 3 files changed, 210 insertions(+), 154 deletions(-) create mode 100644 libs/premake5.lua create mode 100644 source/premake5.lua diff --git a/libs/premake5.lua b/libs/premake5.lua new file mode 100644 index 0000000000..1a01b2f4b0 --- /dev/null +++ b/libs/premake5.lua @@ -0,0 +1,124 @@ +project "ogg" + language "C++" + kind "StaticLib" + targetname "ogg" + warnings "Off" + + vpaths { + ["Headers/*"] = {"ogg/**.h",}, + ["Sources/*"] = {"ogg/**.c",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "ogg/**.h", + "ogg/**.c" + } + + includedirs { + "vorbis/include", + "ogg/include", + "ogg/include" + } + +project "vorbis" + language "C++" + kind "StaticLib" + targetname "vorbis" + warnings "Off" + + vpaths { + ["Headers/*"] = {"vorbis/**.h",}, + ["Sources/*"] = {"vorbis/**.c",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "vorbis/win32/vorbis.def", + "vorbis/lib/**.*" + } + + removefiles { + "vorbis/lib/psytune.c", + "vorbis/lib/tone.c", + "vorbis/lib/misc.c", + "vorbis/lib/psy.h", + } + + includedirs { + "vorbis/include", + "ogg/include", + "%{cfg.targetdir}" + } + +project "vorbisenc" + language "C++" + kind "StaticLib" + targetname "vorbisenc" + warnings "Off" + + vpaths { + ["Sources/*"] = {"vorbis/**.c",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "vorbis/lib/vorbisenc.c", + "/vorbis/win32/vorbisenc.def" + } + + includedirs { + "vorbis/include", + "ogg/include", + "%{cfg.targetdir}" + } + + +project "vorbisfile" + language "C++" + kind "StaticLib" + targetname "vorbisfile" + warnings "Off" + + vpaths { + ["Sources/*"] = {"vorbis/**.c",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "vorbis/lib/vorbisfile.c", + "/vorbis/win32/vorbisfile.def" + } + + includedirs { + "vorbis/include", + "ogg/include", + "%{cfg.targetdir}" + } + +project "imgui" + language "C++" + kind "StaticLib" + targetname "imgui" + warnings "Off" + + vpaths { + ["Headers/*"] = {"imgui/**.h*",}, + ["Sources/*"] = {"imgui/**.c*",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "imgui/backends/imgui_impl_win32.h", + "imgui/backends/imgui_impl_win32.cpp", + "imgui/backends/imgui_impl_dx9.h", + "imgui/backends/imgui_impl_dx9.cpp", + "imgui/misc/cpp/*.*", + "imgui/*.*", + } + + includedirs { + "imgui", + "imgui/backends", + "imgui/misc/cpp" + } diff --git a/premake5.lua b/premake5.lua index a5d1258fd7..5501ede402 100644 --- a/premake5.lua +++ b/premake5.lua @@ -28,31 +28,31 @@ end --]] solution "gta_reversed" - configurations { "Release", "Debug" } - location( _OPTIONS["outdir"] ) + location(_OPTIONS["outdir"]) targetprefix "" -- no 'lib' prefix on gcc - targetdir "bin" - targetdir("bin/" .. "%{cfg.buildcfg}") - implibdir("bin/" .. "%{cfg.buildcfg}") + targetdir("bin/%{cfg.buildcfg}") filter "configurations:Debug*" - flags { symbols ("On") } - -- buildoptions {"/MDd"} staticruntime "off" + symbols "On" runtime "Debug" + vectorextensions "AVX2" + defines { "NOTSA_DEBUG" } + filter "configurations:Release*" - defines { "NDEBUG" } - flags { symbols ("On") } - -- buildoptions {"/MD"} staticruntime "off" + symbols "On" runtime "Release" + vectorextensions "AVX2" + defines { "NDEBUG" } optimize "Full" + filter "action:vs*" - flags {"MultiProcessorCompile"} + flags { "MultiProcessorCompile" } linkoptions { "/ignore:4099,4251,4275" } - buildoptions {"/EHsc", "/Zc:preprocessor", "/bigobj"} + buildoptions { "/EHsc", "/Zc:preprocessor", "/bigobj" } disablewarnings { 26812, 26495, 4099, 4251, 4275 } filter "files:libs/**" @@ -60,148 +60,18 @@ solution "gta_reversed" filter {} + characterset "MBCS" -- Fix strings + staticruntime "On" + rtti "Off" + flags { - characterset ("MBCS"), --fix strings - staticruntime("On"), "NoImportLib", - rtti ("Off"), - "NoBufferSecurityCheck", - "FatalWarnings" + --"NoBufferSecurityCheck" + "FatalWarnings", } - defines { "_SCL_SECURE_NO_WARNINGS" } - -group "Dependencies" - defines { "WIN32", "_WINDOWS" } - - project "ogg" - vpaths { - ["Headers/*"] = {"libs/ogg/**.h",}, - ["Sources/*"] = {"libs/ogg/**.c",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - includedirs { "libs/vorbis/include", "libs/ogg/include", "libs/ogg/include" } - language "C++" - kind "StaticLib" - targetname "ogg" - files { - "libs/ogg/**.h", - "libs/ogg/**.c" - } - - project "vorbis" - vpaths { - ["Headers/*"] = {"libs/vorbis/**.h",}, - ["Sources/*"] = {"libs/vorbis/**.c",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - includedirs { "libs/vorbis/include", "libs/ogg/include", "%{cfg.targetdir}" } - language "C++" - kind "StaticLib" - targetname "vorbis" - - local filePaths = { - "backends.h", "bitrate.h", "codebook.h", "codec_internal.h", "envelope.h", "highlevel.h", "lookup.h", "lookup_data.h", "lpc.h", "lsp.h", "masking.h", "mdct.h", "misc.h", "os.h", "psy.h", "registry.h", "scales.h", "smallft.h", "window.h", - "analysis.c", "bitrate.c", "block.c", "codebook.c", "envelope.c", "floor0.c", "floor1.c", "info.c", "lookup.c", "lpc.c", "lsp.c", "mapping0.c", "mdct.c", "psy.c", "registry.c", "res0.c", "sharedbook.c", "smallft.c", "synthesis.c", "vorbisenc.c", "window.c" - } - for i, fileName in pairs(filePaths) do - filePaths[i] = "libs/vorbis/lib/"..fileName - end - files { "libs/vorbis/win32/vorbis.def", table.unpack(filePaths) } - - project "vorbisenc" - vpaths { - ["Sources/*"] = {"libs/vorbis/**.c",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - includedirs { "libs/vorbis/include", "libs/ogg/include", "%{cfg.targetdir}" } - language "C++" - kind "StaticLib" - targetname "vorbisenc" - files { "libs/vorbis/lib/vorbisenc.c", "/libs/vorbis/win32/vorbisenc.def" } - - project "vorbisfile" - vpaths { - ["Sources/*"] = {"libs/vorbis/**.c",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - includedirs { "libs/vorbis/include", "libs/ogg/include", "%{cfg.targetdir}" } - language "C++" - kind "StaticLib" - targetname "vorbisfile" - files { "libs/vorbis/lib/vorbisfile.c", "/libs/vorbis/win32/vorbisfile.def" } - - project "imgui" - vpaths { - ["Headers/*"] = {"libs/imgui/**.h",}, - ["Sources/*"] = {"libs/imgui/**.c*",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - includedirs { "libs/imgui", "libs/imgui/backends", "libs/imgui/misc/cpp" } - language "C++" - kind "StaticLib" - targetname "imgui" - - local filePaths = { - "imconfig.h", "imgui.h", "imgui_internal.h", "imstb_rectpack.h", "imstb_textedit.h", "imstb_truetype.h", - "imgui.cpp", "imgui_draw.cpp", "imgui_widgets.cpp", "imgui_tables.cpp", "imgui_demo.cpp" - } - for i, fileName in pairs(filePaths) do - filePaths[i] = "libs/imgui/"..fileName - end - files { - "libs/imgui/backends/imgui_impl_win32.h", - "libs/imgui/backends/imgui_impl_win32.cpp", - "libs/imgui/backends/imgui_impl_dx9.h", - "libs/imgui/backends/imgui_impl_dx9.cpp", - "libs/imgui/misc/cpp/imgui_stdlib.h", - "libs/imgui/misc/cpp/imgui_stdlib.cpp", - table.unpack(filePaths), - } - -group "" - project "gta_reversed" - vpaths { - ["Headers/*"] = {"source/**.h*",}, - ["Sources/*"] = {"source/**.c*",}, - ["*"] = {"premake5.lua", "CMakeLists.txt"} - } - - defines { "NOMINMAX", "USE_GTASA_ALLOCATOR", "EXTRA_DEBUG_FEATURES", "FIX_BUGS" } - includedirs { - "source", "source/**", - "libs/vorbis/include", - "libs/ogg/include", - "libs/imgui", "libs/imgui/backends", "libs/imgui/misc/cpp", - "libs/dxsdk" - } - links { "ogg", "vorbis", "vorbisenc", "vorbisfile", "imgui" } - libdirs { - "%{cfg.targetdir}/ogg.lib", "%{cfg.targetdir}/vorbis.lib", "%{cfg.targetdir}/vorbisfile.lib", - "%{cfg.targetdir}/vorbisenc.lib", "%{cfg.targetdir}/imgui.lib", "libs/dxsdk/d3d9.lib", "libs/dxsdk/dinput.lib" - } - - cppdialect "C++20" - - kind "SharedLib" - targetname "gta_reversed" - targetextension ".asi" - pchheader "StdInc.h" - pchsource "source/StdInc.cpp" - files { - "source/StdInc.h", - "source/StdInc.cpp", - "source/**.h*", - "source/**.c*" - } - excludes{ - "source/**/errcom.def", --bugfix for premake5 - "source/**/errcore.def" - } - - filter {"vs*", "options:allow-script-cmd-hooks"} - buildoptions { "/bigobj"} - - filter {"options:allow-script-cmd-hooks"} - defines { "ENABLE_SCRIPT_COMMAND_HOOKS" } - - filter {} -- Clear filter \ No newline at end of file + + include "source/" + + group "Dependencies" + defines { "WIN32", "_WINDOWS" } + include "libs/" diff --git a/source/premake5.lua b/source/premake5.lua new file mode 100644 index 0000000000..6a593319c2 --- /dev/null +++ b/source/premake5.lua @@ -0,0 +1,62 @@ +project "gta_reversed" + cppdialect "C++20" + kind "SharedLib" + targetname "gta_reversed" + targetextension ".asi" + + pchheader "StdInc.h" + pchsource "StdInc.cpp" + + filter {"options:allow-script-cmd-hooks"} + defines { "ENABLE_SCRIPT_COMMAND_HOOKS" } + + filter {} -- Clear filter + + vpaths { + ["Headers/*"] = {"**.h*",}, + ["Sources/*"] = {"**.c*",}, + ["*"] = {"premake5.lua", "CMakeLists.txt"} + } + + files { + "StdInc.h", + "StdInc.cpp", + "**.h*", + "**.c*" + } + + includedirs { + ".", + "./**", + "../libs/vorbis/include", + "../libs/ogg/include", + "../libs/imgui", + "../libs/imgui/backends", + "../libs/imgui/misc/cpp", + "../libs/dxsdk" + } + + defines { + "NOMINMAX", + "USE_GTASA_ALLOCATOR", + "EXTRA_DEBUG_FEATURES", + "FIX_BUGS" + } + + links { + "ogg", + "vorbis", + "vorbisenc", + "vorbisfile", + "imgui" + } + + libdirs { + "../%{cfg.targetdir}/ogg.lib", + "../%{cfg.targetdir}/vorbis.lib", + "../%{cfg.targetdir}/vorbisfile.lib", + "../%{cfg.targetdir}/vorbisenc.lib", + "../%{cfg.targetdir}/imgui.lib", + "../libs/dxsdk/d3d9.lib", + "../libs/dxsdk/dinput.lib" + } From 09a3ec4912b9f7847fef0b16d51eabcfa4237d32 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Sat, 18 Mar 2023 10:22:56 +0100 Subject: [PATCH 06/19] fix crash caused by script command erroneously hooked as unimplemented (#555) --- .../game_sa/Scripts/CommandParser/Parser.hpp | 6 ++--- source/game_sa/Scripts/Commands/Basic.cpp | 1 + source/game_sa/Scripts/Commands/Player.cpp | 11 +++++--- source/game_sa/Scripts/RunningScript.cpp | 26 ++++++++++++++++++- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/source/game_sa/Scripts/CommandParser/Parser.hpp b/source/game_sa/Scripts/CommandParser/Parser.hpp index 3f2a6b597f..533ce24249 100644 --- a/source/game_sa/Scripts/CommandParser/Parser.hpp +++ b/source/game_sa/Scripts/CommandParser/Parser.hpp @@ -60,11 +60,11 @@ inline OpcodeResult CollectArgsAndCall(CRunningScript* S, eScriptCommands comman } //! Called for unimplemented commands -//! These are commands that have no (special) code associated with them +//! That is, ones that aren't used anywhere. +//! If this ever gets called, that means that the command is used after all, and shouldn't be hooked as unimplemented. inline auto NotImplemented(eScriptCommands cmd) { -#ifdef NOTSA_DEBUG DEV_LOG("Unimplemented command has been called! [ID: {:04X}; Name: {}]", (unsigned)(cmd), GetScriptCommandName(cmd)); -#endif + NOTSA_DEBUGBREAK(); // Something went horribly wrong here, and the game will crash after this, so better stop here. return OR_INTERRUPT; // Vanilla SA behavior } diff --git a/source/game_sa/Scripts/Commands/Basic.cpp b/source/game_sa/Scripts/Commands/Basic.cpp index 79d35f45bc..29928f3b9a 100644 --- a/source/game_sa/Scripts/Commands/Basic.cpp +++ b/source/game_sa/Scripts/Commands/Basic.cpp @@ -157,6 +157,7 @@ auto AndOr(CRunningScript& S, int32 logicalOp) { // 0x0D6 // Script loading, stopping // +// COMMAND_TERMINATE_THIS_SCRIPT auto TerminateThisScript(CRunningScript& S) { // 0x04E if (S.m_bIsMission) { CTheScripts::bAlreadyRunningAMissionScript = false; diff --git a/source/game_sa/Scripts/Commands/Player.cpp b/source/game_sa/Scripts/Commands/Player.cpp index c0d5f25330..3dcafe0a6b 100644 --- a/source/game_sa/Scripts/Commands/Player.cpp +++ b/source/game_sa/Scripts/Commands/Player.cpp @@ -109,6 +109,11 @@ bool IsPlayerTouchingObjectOnFoot(CPlayerPed& player, CObject& object) { return player.GetHasCollidedWith(&object); } +// COMMAND_IS_PLAYER_IN_POSITION_FOR_CONVERSATION - 0x474983 +//bool IsPlayerInPositionForConversation(CPed& ped) { // Yes, it's CPed +// +//} + void notsa::script::commands::player::RegisterHandlers() { REGISTER_COMMAND_HANDLER(COMMAND_CREATE_PLAYER, CreatePlayer); REGISTER_COMMAND_HANDLER(COMMAND_GET_PLAYER_COORDINATES, GetPlayerCoordinates); @@ -196,7 +201,7 @@ void notsa::script::commands::player::RegisterHandlers() { REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_NEVER_GETS_TIRED); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FAST_RELOAD); - REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_EVERYONE_IGNORE_PLAYER); + //REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_EVERYONE_IGNORE_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_UNSAFE); @@ -210,7 +215,7 @@ void notsa::script::commands::player::RegisterHandlers() { REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENABLE_PLAYER_CONTROL_CAMERA); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR); - REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_CHAR); + //REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_CHAR); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_TARGETTING_OBJECT); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_CURRENT_PLAYER_WEAPON); @@ -269,7 +274,7 @@ void notsa::script::commands::player::RegisterHandlers() { REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_ENABLE_ENTRY_EXIT_PLAYER_GROUP_WARPING); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_OBJECT_ONLY_DAMAGED_BY_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FIRE_BUTTON); - REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_POSITION_FOR_CONVERSATION); + //REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_PLAYER_IN_POSITION_FOR_CONVERSATION); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_PLANE_ATTACK_PLAYER_USING_DOG_FIGHT); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_GANG_DISAPPEAR); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_MAKE_PLAYER_GANG_REAPPEAR); diff --git a/source/game_sa/Scripts/RunningScript.cpp b/source/game_sa/Scripts/RunningScript.cpp index 2c04ce6f8f..92149971dd 100644 --- a/source/game_sa/Scripts/RunningScript.cpp +++ b/source/game_sa/Scripts/RunningScript.cpp @@ -206,7 +206,31 @@ void CRunningScript::RemoveScriptFromList(CRunningScript** queueList) { * @addr 0x465AA0 */ void CRunningScript::ShutdownThisScript() { - plugin::CallMethod<0x465AA0, CRunningScript*>(this); + return plugin::CallMethod<0x465AA0>(this); + /* + if (m_bIsExternal) { + const auto idx = CTheScripts::StreamedScripts.GetStreamedScriptWithThisStartAddress(m_pBaseIP); + CTheScripts::StreamedScripts.m_aScripts[idx].m_nStatus--; + } + + switch (m_nExternalType) { + case 0: + case 2: + case 3: + case 5: { + const auto pedRef = m_bIsMission + ? CTheScripts::LocalVariablesForCurrentMission.front().iParam + : m_aLocalVars[0].iParam; + if (const auto ped = GetPedPool()->GetAtRef(pedRef)) { + ped->bHasAScriptBrain = false; + if (m_nExternalType == 5) { + CScriptedBrainTaskStore::SetTask(ped, new CTaskSimpleFinishBrain{}); + } + } + break; + } + } + */ } // 0x465C20 From 22261929d55693ccacd4c1f01d2f751cd0da464d Mon Sep 17 00:00:00 2001 From: yukani Date: Fri, 21 Apr 2023 22:25:34 +0300 Subject: [PATCH 07/19] Fix some stuff --- README.md | 8 ++--- contrib/link_asi.bat | 34 +++++++++++++++---- source/game_sa/Collision/Collision.cpp | 8 +++-- source/game_sa/GangInfo.cpp | 2 +- source/game_sa/GenericGameStorage.h | 2 +- source/game_sa/LoadingScreen.h | 4 +-- .../game_sa/Scripts/CommandParser/ReadArg.hpp | 11 +++--- .../game_sa/Scripts/CommandParser/Utility.hpp | 3 ++ source/game_sa/Scripts/Commands/Basic.cpp | 2 +- source/game_sa/Scripts/Commands/Character.cpp | 8 ++--- 10 files changed, 54 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index d0aa44631e..0c1ccee68e 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,8 @@ git clone --recurse-submodules https://github.com/gta-reversed/gta-reversed-mode You can create symbolic links for artifacts to not copy them every time you compiled the project. -Open a console in the cloned git repo's directory (administrator privileges may be needed) and type in the following commands: -```shell -cd contrib -link_asi.bat "/scripts" -``` -Replace `` with the path to the game's root directory (i.e.: Where the `exe` is) +Open a console with administrator privileges in the git repo's directory and run `contrib\link_asi.bat` or right click `link_asi.bat` file and click `Run as administrator`, then +follow instructions at the command window. ### What to work on? Check [this](https://github.com/gta-reversed/gta-reversed-modern/discussions/402) out for some inspiration ;) diff --git a/contrib/link_asi.bat b/contrib/link_asi.bat index c3031b13ae..6c53e9f87b 100644 --- a/contrib/link_asi.bat +++ b/contrib/link_asi.bat @@ -1,12 +1,34 @@ @echo off +rem Check for admin rights +net session >nul 2>&1 +if not %errorlevel% == 0 ( + echo This script needs to be run as administrator! + pause + exit +) + set pwd=%~dp0 -set scriptdir=%1 -set scriptname=%2 -if "%scriptdir%"=="" goto :eof +:scriptdir_loop +set /p scriptdir="GTA SA 'scripts' directory path: " +if "%scriptdir%"=="" ( + echo Invalid path! + goto scriptdir_loop +) + +set /p scriptname="Name of the script file (default: gta_reversed): " if "%scriptname%"=="" set scriptname="gta_reversed" -mklink %scriptdir%\%scriptname%.asi "%pwd%\..\bin\debug\gta_reversed.asi" -mklink %scriptdir%\%scriptname%.pdb "%pwd%\..\bin\debug\gta_reversed.pdb" +:scriptconf_loop +set /p scriptconf="Choose configuration to link (debug/release, default: debug): " +if "%scriptconf%"=="" set scriptconf=debug + +if not "%scriptconf%"=="debug" if not "%scriptconf%"=="release" ( + echo Invalid configuration! Only 'debug' and 'release' is applicable!c + goto scriptconf_loop +) + +mklink "%scriptdir%\%scriptname%.asi" "%pwd%\..\bin\%scriptconf%\gta_reversed.asi" +mklink "%scriptdir%\%scriptname%.pdb" "%pwd%\..\bin\%scriptconf%\gta_reversed.pdb" -eof: \ No newline at end of file +pause \ No newline at end of file diff --git a/source/game_sa/Collision/Collision.cpp b/source/game_sa/Collision/Collision.cpp index 0c07c945d5..75207296db 100644 --- a/source/game_sa/Collision/Collision.cpp +++ b/source/game_sa/Collision/Collision.cpp @@ -19,7 +19,7 @@ #include "TaskComplexEnterCarAsDriver.h" #include "TaskComplexEnterCarAsPassenger.h" -#define NOTSA_VANILLA_COLLISIONS +#define NOTSA_VANILLA_COLLISIONS // TODO: move to config.h? using Shape = CCollision::DebugSettings::ShapeShapeCollision::Shape; @@ -3058,10 +3058,12 @@ bool CCollision::SphereVsEntity(CColSphere* sphere, CEntity* entity) { } void CCollision::InjectHooks() { - // Must be done be4 hooks are injected +#ifdef TEST_COLLISION_FUNCS + // Must be done before hooks are injected for (auto i = 0; i < 20; i++) { Tests(i); } +#endif RH_ScopedClass(CCollision); RH_ScopedCategoryGlobal(); @@ -3147,6 +3149,7 @@ void CCollision::InjectHooks() { } void CCollision::Tests(int32 i) { +#ifdef TEST_COLLISION_FUNCS const auto seed = (uint32)time(nullptr) + i; srand(seed); std::cout << "CCollision::Tests seed: " << seed << std::endl; @@ -3479,4 +3482,5 @@ void CCollision::Tests(int32 i) { Test("ProcessLineBox", Org, Rev, CmpEq, RandomLine(), RandomBox()); }*/ +#endif } diff --git a/source/game_sa/GangInfo.cpp b/source/game_sa/GangInfo.cpp index 9cd501d7c5..6ff07cdf79 100644 --- a/source/game_sa/GangInfo.cpp +++ b/source/game_sa/GangInfo.cpp @@ -7,7 +7,7 @@ CGangInfo::CGangInfo() { // NOTSA size_t CGangInfo::GetNumOfWeaponChoices() const { size_t n{}; - for (; m_nGangWeapons[n] != WEAPON_UNARMED && n++ < m_nGangWeapons.size();); + for (; m_nGangWeapons[n] != WEAPON_UNARMED && ++n < m_nGangWeapons.size();); return n; } diff --git a/source/game_sa/GenericGameStorage.h b/source/game_sa/GenericGameStorage.h index fef028dcce..ad5dc6c527 100644 --- a/source/game_sa/GenericGameStorage.h +++ b/source/game_sa/GenericGameStorage.h @@ -181,7 +181,7 @@ static void SaveMultipleDataToWorkBuffer(Ts&&... data) { template static void LoadMultipleDataFromWorkBuffer(Ts*... out) { if constexpr (HasSizeHeader) { // Verify size header - const auto size = LoadDataFromWorkBuffer(); + const auto size = LoadDataFromWorkBuffer(); assert(size == ExpectedSize); } (CGenericGameStorage::LoadDataFromWorkBuffer((void*)out, sizeof(Ts)), ...); // And now load all data diff --git a/source/game_sa/LoadingScreen.h b/source/game_sa/LoadingScreen.h index 7034c6123b..f3c0c02e0b 100644 --- a/source/game_sa/LoadingScreen.h +++ b/source/game_sa/LoadingScreen.h @@ -9,7 +9,7 @@ class CSprite2d; // TODO: Move this to an appropriate place -static inline struct FastLoadSettings { +inline struct FastLoadSettings { int32 SaveGameToLoad = -1; //< -2 - Don't load, -1 = Load first available, 0 <= - load from save slot bool TriedLoadingSaveGame = false; //< [Runtime Var] Whenever `SaveGameToLoad` was tried to be loaded uint32 SkipSaveGameLoadKey = VK_CONTROL; //< Skip auto-loading save game (If enabled) @@ -37,7 +37,7 @@ static inline struct FastLoadSettings { if (slot == -1) { // Find first valid slot and load that CFileMgr::SetDirMyDocuments(); for (auto i = 0u; i < MAX_SAVEGAME_SLOTS; i++) { - if (std::filesystem::exists(std::format("GTASAsf{}.b", i))) { + if (std::filesystem::exists(std::format("GTASAsf{}.b", i + 1))) { // Save file IDs start from 1, not 0 return StartGame(i); // Load this slot } } diff --git a/source/game_sa/Scripts/CommandParser/ReadArg.hpp b/source/game_sa/Scripts/CommandParser/ReadArg.hpp index 3b5f965b5e..f11807706f 100644 --- a/source/game_sa/Scripts/CommandParser/ReadArg.hpp +++ b/source/game_sa/Scripts/CommandParser/ReadArg.hpp @@ -79,15 +79,16 @@ concept PooledType = }; namespace detail { +//! Check if the type is an integer type excluding bool and character types. +template +inline constexpr bool is_standard_integer = std::is_integral_v && !is_any_of_type_v; + //! Safely cast one arithmetic type to another (Checks for under/overflow in debug mode only), then casts to `T` template inline T safe_arithmetic_cast(F value) { #ifdef NOTSA_DEBUG - if constexpr (std::numeric_limits::lowest() < std::numeric_limits::lowest()) { // Underflow - assert(value >= static_cast(std::numeric_limits::lowest())); - } - if constexpr (std::numeric_limits::max() >= std::numeric_limits::max()) { // Overflow - assert(value <= static_cast(std::numeric_limits::max())); + if constexpr (is_standard_integer && is_standard_integer) { + assert(std::in_range(value)); } #endif return static_cast(value); // In release mode just a simple, regular cast diff --git a/source/game_sa/Scripts/CommandParser/Utility.hpp b/source/game_sa/Scripts/CommandParser/Utility.hpp index bf1dddd81c..5a8b3badf6 100644 --- a/source/game_sa/Scripts/CommandParser/Utility.hpp +++ b/source/game_sa/Scripts/CommandParser/Utility.hpp @@ -125,4 +125,7 @@ inline auto GetPedOrItsVehicle(CPed& ped) -> CPhysical& { template using nth_element_t = typename std::tuple_element>::type; +template +concept is_any_of_type_v = (std::same_as || ...); + }; // notsa diff --git a/source/game_sa/Scripts/Commands/Basic.cpp b/source/game_sa/Scripts/Commands/Basic.cpp index 29928f3b9a..416480c5ec 100644 --- a/source/game_sa/Scripts/Commands/Basic.cpp +++ b/source/game_sa/Scripts/Commands/Basic.cpp @@ -353,7 +353,7 @@ void notsa::script::commands::basic::RegisterHandlers() { REGISTER_COMMAND_HANDLER(COMMAND_ABS_LVAR_FLOAT, AbsStore); REGISTER_COMMAND_HANDLER(COMMAND_GOTO, GoTo); - REGISTER_COMMAND_HANDLER(COMMAND_GOTO_IF_FALSE, GoToIfFalse); + //REGISTER_COMMAND_HANDLER(COMMAND_GOTO_IF_FALSE, GoToIfFalse); REGISTER_COMMAND_HANDLER(COMMAND_GOSUB, GoToSub); REGISTER_COMMAND_HANDLER(COMMAND_RETURN_TRUE, ReturnTrue); REGISTER_COMMAND_HANDLER(COMMAND_RETURN_FALSE, ReturnFalse); diff --git a/source/game_sa/Scripts/Commands/Character.cpp b/source/game_sa/Scripts/Commands/Character.cpp index 75ce2ac496..4d96ec60ad 100644 --- a/source/game_sa/Scripts/Commands/Character.cpp +++ b/source/game_sa/Scripts/Commands/Character.cpp @@ -1386,13 +1386,13 @@ void DisableCharSpeech(CPed& ped, bool stopCurrentSpeech) { } // SET_CHAR_WANTED_BY_POLICE -void SetCharWantedByPolice(CPed& ped) { - ped.bWantedByPolice = true; +void SetCharWantedByPolice(CPed& ped, bool state) { + ped.bWantedByPolice = state; } // SET_CHAR_SIGNAL_AFTER_KILL -void SetCharSignalAfterKill(CPed& ped) { - ped.bSignalAfterKill = true; +void SetCharSignalAfterKill(CPed& ped, bool state) { + ped.bSignalAfterKill = state; } // SET_CHAR_COORDINATES_DONT_WARP_GANG_NO_OFFSET From 9db2df9cc8c1dbeb8af2543528c6773402a3c502 Mon Sep 17 00:00:00 2001 From: plakapenka Date: Fri, 21 Apr 2023 22:36:59 +0300 Subject: [PATCH 08/19] Reverse LodAtomicModelInfo member vars --- source/game_sa/Models/LodAtomicModelInfo.cpp | 4 ++-- source/game_sa/Models/LodAtomicModelInfo.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/game_sa/Models/LodAtomicModelInfo.cpp b/source/game_sa/Models/LodAtomicModelInfo.cpp index 17fb57d94a..4dd88eb58c 100644 --- a/source/game_sa/Models/LodAtomicModelInfo.cpp +++ b/source/game_sa/Models/LodAtomicModelInfo.cpp @@ -14,8 +14,8 @@ void CLodAtomicModelInfo::InjectHooks() CLodAtomicModelInfo::CLodAtomicModelInfo() : CAtomicModelInfo() { - field_20 = 0; - field_22 = 0; + m_numChildren = 0; + m_numChildrenRendered = 0; } CLodAtomicModelInfo* CLodAtomicModelInfo::AsLodAtomicModelInfoPtr() diff --git a/source/game_sa/Models/LodAtomicModelInfo.h b/source/game_sa/Models/LodAtomicModelInfo.h index ec2ee19d79..98e8b0780b 100644 --- a/source/game_sa/Models/LodAtomicModelInfo.h +++ b/source/game_sa/Models/LodAtomicModelInfo.h @@ -4,8 +4,8 @@ class NOTSA_EXPORT_VTABLE CLodAtomicModelInfo : public CAtomicModelInfo { public: - int16 field_20; - int16 field_22; + int16 m_numChildren; + int16 m_numChildrenRendered; public: CLodAtomicModelInfo(); From 0ea6b4fdc9eea35419c3b3bb38ba728b7ce9dc69 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Tue, 2 May 2023 16:18:27 +0200 Subject: [PATCH 09/19] Fix crash caused by unimpl. script command (#561) Fix script command incorrectly marked as unimplemented causing a crash --- source/game_sa/Scripts/Commands/Game.cpp | 1 - source/game_sa/Scripts/Commands/Player.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/game_sa/Scripts/Commands/Game.cpp b/source/game_sa/Scripts/Commands/Game.cpp index 249eddb4a4..6a2c090c3f 100644 --- a/source/game_sa/Scripts/Commands/Game.cpp +++ b/source/game_sa/Scripts/Commands/Game.cpp @@ -372,5 +372,4 @@ void notsa::script::commands::game::RegisterHandlers() { REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_XBOX_PLAYER2_PRESSING_START); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_FINISHED_WITH_XBOX_PLAYER2); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RESTORE_PLAYER_AFTER_2P_GAME); - REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_IS_2PLAYER_GAME_GOING_ON); } diff --git a/source/game_sa/Scripts/Commands/Player.cpp b/source/game_sa/Scripts/Commands/Player.cpp index 3dcafe0a6b..54ab8d4fb2 100644 --- a/source/game_sa/Scripts/Commands/Player.cpp +++ b/source/game_sa/Scripts/Commands/Player.cpp @@ -199,7 +199,7 @@ void notsa::script::commands::player::RegisterHandlers() { REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER); - REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_NEVER_GETS_TIRED); + //REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_NEVER_GETS_TIRED); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_PLAYER_FAST_RELOAD); //REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_EVERYONE_IGNORE_PLAYER); REGISTER_COMMAND_UNIMPLEMENTED(COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER); From 61c279bfd0393a722b693a90a777b4af933834fe Mon Sep 17 00:00:00 2001 From: yukani Date: Sun, 7 May 2023 21:36:48 +0300 Subject: [PATCH 10/19] Update TaskComplexWander.cpp (#565) --- source/game_sa/Tasks/TaskTypes/TaskComplexWander.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexWander.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexWander.cpp index a5f50ad87a..1edfddf690 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexWander.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexWander.cpp @@ -176,7 +176,7 @@ CTask* CTaskComplexWander::ControlSubTask_Reversed(CPed* ped) { // 0x669DA0 void CTaskComplexWander::UpdateDir_Reversed(CPed* ped) { uint8 newDir = m_nDir; - if (m_NextNode.IsValid()) { + if (m_NextNode.IsAreaValid()) { const CPathNode* pathNodes = ThePaths.m_pPathNodes[m_NextNode.m_wAreaId]; if (pathNodes) { const CPathNode* pathNode = &pathNodes[m_NextNode.m_wNodeId]; From 913075c93a345a188f3c0fdaf0d77ca83e6326c2 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Fri, 26 May 2023 23:55:02 +0200 Subject: [PATCH 11/19] Fix `CEntryExitManager`/`CEntryExit` things (#562) * Refactor + fix `FindValidTeleportPoint` * Refactor `GetEntryExitToDisplayNameOf` --- source/game_sa/Core/Vector.cpp | 7 +- source/game_sa/EntryExit.cpp | 212 +++++++++++------- source/game_sa/EntryExit.h | 10 +- source/game_sa/EntryExitManager.cpp | 127 ++++++----- source/game_sa/PedGroup.cpp | 4 + source/game_sa/PedGroup.h | 1 + source/game_sa/Streaming.cpp | 2 +- source/game_sa/Streaming.h | 2 +- .../TaskComplexFollowLeaderInFormation.h | 6 +- 9 files changed, 218 insertions(+), 153 deletions(-) diff --git a/source/game_sa/Core/Vector.cpp b/source/game_sa/Core/Vector.cpp index 8a81683f7e..dc537e7223 100644 --- a/source/game_sa/Core/Vector.cpp +++ b/source/game_sa/Core/Vector.cpp @@ -181,14 +181,13 @@ CVector CVector::Average(const CVector* begin, const CVector* end) { } float CVector::Heading(bool limitAngle) const { - const auto heading = std::atan2(-x, y); + const auto radians = std::atan2(-x, y); if (limitAngle) { - return CGeneral::LimitRadianAngle(heading); + return CGeneral::LimitRadianAngle(radians); } - return heading; + return radians; } - CVector* CrossProduct(CVector* out, CVector* a, CVector* b) { *out = a->Cross(b); diff --git a/source/game_sa/EntryExit.cpp b/source/game_sa/EntryExit.cpp index a034628678..08e2716b03 100644 --- a/source/game_sa/EntryExit.cpp +++ b/source/game_sa/EntryExit.cpp @@ -4,8 +4,12 @@ #include "EntryExitManager.h" #include "TaskComplexGotoDoorAndOpen.h" #include "TaskSimpleUninterruptable.h" +#include "TaskComplexFollowLeaderInFormation.h" #include "TaskComplexFacial.h" #include "Garages.h" +#include "PlayerPed.h" +#include "Object.h" +#include "Interior/InteriorManager_c.h" bool& CEntryExit::ms_bWarping = *(bool*)0x96A7B8; CObject*& CEntryExit::ms_pDoor = *(CObject**)0x96A7BC; @@ -24,8 +28,8 @@ void CEntryExit::InjectHooks() { RH_ScopedInstall(TransitionFinished, 0x4404A0, { .reversed = false }); RH_ScopedInstall(RequestObjectsInFrustum, 0x43E690); RH_ScopedInstall(RequestAmbientPeds, 0x43E6D0); - RH_ScopedInstall(WarpGangWithPlayer, 0x43F1F0, { .reversed = false }); - RH_ScopedInstall(ProcessStealableObjects, 0x43E990, { .reversed = false }); + RH_ScopedInstall(WarpGangWithPlayer, 0x43F1F0); + RH_ScopedInstall(ProcessStealableObjects, 0x43E990); } // Code based on 0x43FA00 @@ -78,6 +82,12 @@ CEntryExit::CEntryExit( } } +// 0x43EA90 +bool IsTeleportPointValid(const CVector& origin, const CVector& target) { + return !CWorld::TestSphereAgainstWorld(target, 0.35f, nullptr, true, true, true, true, true, false) + && CWorld::GetIsLineOfSightClear(origin, target, true, true, false, true, true, false, false); +} + // 0x43E8B0 void CEntryExit::GenerateAmbientPeds(const CVector& posn) { CPopulation::bInPoliceStation = false; @@ -96,18 +106,19 @@ void CEntryExit::GenerateAmbientPeds(const CVector& posn) { // 0x43E650 CEntryExit* CEntryExit::GetEntryExitToDisplayNameOf() { - if (ms_spawnPoint->m_nArea != eAreaCodes::AREA_CODE_NORMAL_WORLD && HasNameSet()) { - if (m_nArea != eAreaCodes::AREA_CODE_NORMAL_WORLD) { - // TODO: Probably inlined from `CEntryExitManager` - if ( CEntryExitManager::ms_entryExitStackPosn > 1 - && CEntryExitManager::ms_entryExitStack[CEntryExitManager::ms_entryExitStackPosn - 1] == ms_spawnPoint - ) { - return CEntryExitManager::ms_entryExitStack[CEntryExitManager::ms_entryExitStackPosn]; - } - return this; - } + if (ms_spawnPoint->m_nArea == eAreaCodes::AREA_CODE_NORMAL_WORLD || !HasNameSet()) { + return nullptr; + } + if (m_nArea == eAreaCodes::AREA_CODE_NORMAL_WORLD) { + return nullptr; } - return nullptr; + // TODO: Probably inlined from `CEntryExitManager` + if ( CEntryExitManager::ms_entryExitStackPosn > 1 + && CEntryExitManager::ms_entryExitStack[CEntryExitManager::ms_entryExitStackPosn - 1] == ms_spawnPoint + ) { + return CEntryExitManager::ms_entryExitStack[CEntryExitManager::ms_entryExitStackPosn]; + } + return this; } // 0x43EA00 @@ -151,29 +162,27 @@ CVector CEntryExit::TransformEntrancePoint(const CVector& point) const { } // 0x43EAF0 -void CEntryExit::FindValidTeleportPoint(CVector* outTeleportPoint) { - const auto spawnPointExitPos = ms_spawnPoint->m_vecExitPos; +void CEntryExit::FindValidTeleportPoint(CVector& outTeleportPoint) { + outTeleportPoint = ms_spawnPoint->m_vecExitPos; - if (!CWorld::TestSphereAgainstWorld(spawnPointExitPos, 0.35f, nullptr, true, true, true, true, true, false)) { + if (!CWorld::TestSphereAgainstWorld(outTeleportPoint, 0.35f, nullptr, true, true, true, true, true, false)) { return; } - - // Test 8 spheres around the spawn point, and return whichever + + // Test 2 * 8 spheres around the spawn point, and return whichever // doesn't collide with the world and the line of sight between it and `outPoint` is clear for (auto r : { 1.25f, 2.f }) { // Test with 2 ranges constexpr auto NumTestPoints{ 8 }; for (auto i = 0; i < NumTestPoints; i++) { const auto rot{ (float)i * TWO_PI / (float)NumTestPoints }; - const auto point = *outTeleportPoint + CVector{ + const auto point = outTeleportPoint + CVector{ std::cos(rot) * r, std::sin(rot) * r, 0.f }; - if (!CWorld::TestSphereAgainstWorld(point, 0.35f, nullptr, true, true, true, true, true, false)) { - if (CWorld::GetIsLineOfSightClear(*outTeleportPoint, point, true, true, false, true, true, false, false)) { - *outTeleportPoint = point; - return; - } + if (IsTeleportPointValid(outTeleportPoint, point)) { + outTeleportPoint = point; + return; } } } @@ -235,10 +244,9 @@ bool CEntryExit::TransitionStarted(CPed* ped) { } break; } - default: { // Nothing else is disallowed - TODO: Allow uncomment this, I want my Rhino in CJ's house + default: // Nothing else is disallowed - TODO: Uncomment this, I want my Rhino in CJ's house return false; } - } } else if (bDisableExit) { return false; } @@ -257,21 +265,24 @@ bool CEntryExit::TransitionStarted(CPed* ped) { } bEnteredWithoutExit = true; - ms_pDoor = nullptr; + ms_pDoor = nullptr; - ped->bCanExitCar = false; + ped->bCanExitCar = false; const auto AddPedScriptCommand = [ped](auto* task) { - CEventScriptCommand scriptCmdEvent{ ePrimaryTasks::TASK_PRIMARY_PRIMARY, task, false }; - ped->GetEventGroup().Add(&scriptCmdEvent); + ped->GetEventGroup().Add(CEventScriptCommand{ + TASK_PRIMARY_PRIMARY, + task, + false + }); }; - if ((bUnknownPairing || bFoodDateFlag) || ped->bInVehicle) { - if (spawnPointExitToUs.Magnitude() > 10.f) { + if (bUnknownPairing || bFoodDateFlag || ped->bInVehicle) { + if (spawnPointExitToUs.SquaredMagnitude() > sq(10.f)) { ms_bWarping = true; } } else { - ms_bWarping = spawnPointExitToUs.Magnitude() > 10.f; + ms_bWarping = spawnPointExitToUs.SquaredMagnitude() > sq(10.f); const auto spawnPointExitToUsDir = Normalized(spawnPointExitToUs); @@ -312,7 +323,7 @@ bool CEntryExit::TransitionStarted(CPed* ped) { // 0x4404A0 bool CEntryExit::TransitionFinished(CPed* ped) { return plugin::CallMethodAndReturn(this, ped); - + /* const auto spawnPos = ms_spawnPoint->m_vecExitPos; if (const auto entity = ped->GetEntityThatThisPedIsHolding()) { @@ -509,6 +520,7 @@ bool CEntryExit::TransitionFinished(CPed* ped) { } return 0; // TODO + */ } // 0x43E6D0 @@ -518,44 +530,34 @@ void CEntryExit::RequestAmbientPeds() { return; } - if (_stricmp("bar1", m_szName) == 0) { - int32 peds[] = { - MODEL_BFYRI, MODEL_OFYST, MODEL_WFYST, MODEL_WMYST, - MODEL_BMYRI, MODEL_BMYST, MODEL_OMOST, MODEL_OMYST, - }; - CStreaming::StreamPedsIntoRandomSlots(peds); - } - - if (_stricmp("strip2", m_szName) == 0) { - int32 peds[] = { - MODEL_SBFYSTR, MODEL_SWFYSTR, MODEL_INVALID, MODEL_INVALID, - MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, - }; - CStreaming::StreamPedsIntoRandomSlots(peds); - } - - if (_stricmp("LAstrip", m_szName) == 0) { - int32 peds[] = { - MODEL_VWFYST1, MODEL_VBFYST2, MODEL_VHFYST3, MODEL_INVALID, - MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, - }; - CStreaming::StreamPedsIntoRandomSlots(peds); - } - - if (_stricmp("MAFCAS", m_szName) == 0) { - int32 peds[] = { - MODEL_BFORI, MODEL_HFYRI, MODEL_OMYRI, MODEL_WMYRI, - MODEL_OFYST, MODEL_VHMYELV, MODEL_WMOST, MODEL_BMORI, - }; - CStreaming::StreamPedsIntoRandomSlots(peds); - } + constexpr struct { const char* name; int32 models[TOTAL_LOADED_PEDS]; } mapping[]{ + { + "bar1", + {MODEL_BFYRI, MODEL_OFYST, MODEL_WFYST, MODEL_WMYST, MODEL_BMYRI, MODEL_BMYST, MODEL_OMOST, MODEL_OMYST} + }, + { + "strip2", + {MODEL_SBFYSTR, MODEL_SWFYSTR, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID} + }, + { + "LAstrip", + {MODEL_VWFYST1, MODEL_VBFYST2, MODEL_VHFYST3, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID, MODEL_INVALID} + }, + { + "MAFCAS", + {MODEL_BFORI, MODEL_HFYRI, MODEL_OMYRI, MODEL_WMYRI, MODEL_OFYST, MODEL_VHMYELV, MODEL_WMOST, MODEL_BMORI} + }, + { + "TRICAS", + {MODEL_BFYRI, MODEL_BMYRI, MODEL_HFORI, MODEL_HMORI, MODEL_WMORI, MODEL_WFYRI, MODEL_OMOST, MODEL_VBMYELV} + }, + }; - if (_stricmp("TRICAS", m_szName) == 0) { - int32 peds[] = { - MODEL_BFYRI, MODEL_BMYRI, MODEL_HFORI, MODEL_HMORI, - MODEL_WMORI, MODEL_WFYRI, MODEL_OMOST, MODEL_VBMYELV, - }; - CStreaming::StreamPedsIntoRandomSlots(peds); + for (auto&& [name, models] : mapping) { + if (_stricmp(name, m_szName)) { + CStreaming::StreamPedsIntoRandomSlots(models); + return; + } } } // 0x43E690 @@ -564,29 +566,75 @@ void CEntryExit::RequestObjectsInFrustum() const { } // 0x43F1F0 -void CEntryExit::WarpGangWithPlayer(CPed* ped) { - auto& grp = ped->AsPlayer()->GetPlayerGroup(); +void CEntryExit::WarpGangWithPlayer(CPlayerPed* player) { + auto& grp = player->GetPlayerGroup(); - if (!CPedGroups::ScriptReferenceIndex[grp.GetId()]) { + if (!grp.IsActive()) { return; } auto& ms = grp.GetMembership(); - if (!ms.IsLeader(ped)) { + if (!ms.IsLeader(player)) { return; } - /* - for (auto i = 0; i < 8 - 1; i++) { - const auto member = membership.GetMember(i); - if (!member || member == ped || ) + const auto& plyrPos = player->GetPosition(); + const auto& offsets = CTaskComplexFollowLeaderInFormation::ms_offsets.offsets; + + size_t offsetIdx = 0; + for (auto & mem : ms.GetMembers()) { + if (&mem == player) { + continue; + } + const auto& memPos = mem.GetPosition(); + + // Find position to teleport member to + // Original code tried only twice, but we'll try all offsets + // (Probably won't make a lot of difference) + CVector memTeleportTo; + do { + if (offsetIdx >= offsets.size()) { + return; // No more offsets + } + memTeleportTo = memPos + CVector{ offsets[offsetIdx++], 0.f }; + } while (!IsTeleportPointValid(plyrPos, memTeleportTo)); - // TODO: Missing CTaskComplexFollowLeaderInFormation::ms_offsets - }*/ + // Calculate member heading (So that they'll face the player) + const auto memHeading = (plyrPos - memPos).Heading(); + + // Teleport them + mem.Teleport(memTeleportTo, false); + + // Make the member be heading towards the player + mem.m_fCurrentRotation = mem.m_fAimingRotation = memHeading; + mem.SetHeading(memHeading); + mem.m_nAreaCode = player->m_nAreaCode; + mem.m_pEnex = player->m_pEnex; + } } // 0x43E990 void CEntryExit::ProcessStealableObjects(CPed* ped) { - plugin::CallMethod<0x43E990, CEntryExit*, CPed*>(this, ped); + const auto helde = ped->GetEntityThatThisPedIsHolding(); + if (!helde || !helde->IsObject() || !helde->AsObject()->objectFlags.bIsLiftable) { + return; + } + const auto heldobj = helde->AsObject(); + switch (heldobj->m_nObjectType) { + case OBJECT_MISSION: + case OBJECT_MISSION2: + break; + default: { + if (ms_spawnPoint->GetArea() != AREA_CODE_NORMAL_WORLD) { + if (g_interiorMan.FindStealableObjectId(heldobj) != -1) { + g_interiorMan.SetStealableObjectStolen(heldobj, false); + } + } else { + g_interiorMan.SetStealableObjectStolen(heldobj, true); + } + break; + } + } + heldobj->m_nAreaCode = ms_spawnPoint->GetArea(); } diff --git a/source/game_sa/EntryExit.h b/source/game_sa/EntryExit.h index 4813ca95e8..2c83ea3578 100644 --- a/source/game_sa/EntryExit.h +++ b/source/game_sa/EntryExit.h @@ -11,6 +11,7 @@ class CObject; class CPed; +class CPlayerPed; class CEntryExit; class CEntryExit { @@ -63,7 +64,7 @@ class CEntryExit { }; uint16 m_nFlags{}; }; - uint8 m_nArea{}; + eAreaCodes m_nArea{}; uint8 m_nSkyColor{}; uint8 m_nTimeOn{}; uint8 m_nTimeOff{}; @@ -76,6 +77,8 @@ class CEntryExit { public: static void InjectHooks(); + + static void WarpGangWithPlayer(CPlayerPed* plyr); CEntryExit( CVector center, @@ -99,9 +102,8 @@ class CEntryExit { void RequestObjectsInFrustum() const; bool TransitionFinished(CPed* ped); bool TransitionStarted(CPed* ped); - static void WarpGangWithPlayer(CPed* ped); void ProcessStealableObjects(CPed* ped); - void FindValidTeleportPoint(CVector* point); + void FindValidTeleportPoint(CVector& outTeleportPoint); bool HasNameSet() const; // NOTSA @@ -121,6 +123,6 @@ class CEntryExit { [[nodiscard]] CVector GetPosition() const; [[nodiscard]] CVector2D GetPosition2D() const; [[nodiscard]] uint8 GetMyOrLinkedArea() const; + [[nodiscard]] auto GetArea() const { return m_nArea; } }; - VALIDATE_SIZE(CEntryExit, 0x3C); diff --git a/source/game_sa/EntryExitManager.cpp b/source/game_sa/EntryExitManager.cpp index c336172590..3d38709e03 100644 --- a/source/game_sa/EntryExitManager.cpp +++ b/source/game_sa/EntryExitManager.cpp @@ -156,9 +156,8 @@ void CEntryExitManager::Update() { // 0x43E410 void CEntryExitManager::AddEntryExitToStack(CEntryExit* enex) { - if (enex->m_pLink) - enex = enex->m_pLink; - + enex = enex->GetLinkedOrThis(); + if (ms_entryExitStackPosn > 0 && ms_entryExitStack[ms_entryExitStackPosn - 1] == enex) { ms_entryExitStackPosn--; } else if (enex->m_nArea != AREA_CODE_NORMAL_WORLD) { @@ -224,6 +223,9 @@ int32 CEntryExitManager::AddOne( void CEntryExitManager::DeleteOne(int32 index) { if (const auto enex = mp_poolEntryExits->GetAt(index)) { mp_QuadTree->DeleteItem(enex); +#ifdef FIX_BUGS // Call destructor + std::destroy_at(enex); +#endif mp_poolEntryExits->Delete(enex); } } @@ -236,30 +238,31 @@ CObject* CEntryExitManager::FindNearestDoor(CEntryExit const& exit, float radius int16 numObjsInRange{}; CWorld::FindObjectsInRange(entranceCenter, radius, false, &numObjsInRange, (int16)std::size(objsInRange), objsInRange, false, false, false, true, false); - if (numObjsInRange) { - float closestDistSq{ FLT_MAX }; - CObject* closest{}; - - for (auto&& entity : std::span{ objsInRange, (size_t)numObjsInRange }) { - const auto object = entity->AsObject(); - if (object->physicalFlags.bDisableMoveForce) { - const auto distSq = (object->GetPosition() - entranceCenter).SquaredMagnitude(); // Using `SqMag` instead of `Mag` - if (distSq < closestDistSq) { - closest = object; - closestDistSq = distSq; - } - } + if (!numObjsInRange) { + return nullptr; + } + + float closestDistSq{ FLT_MAX }; + CObject* closest{}; + for (auto&& entity : std::span{ objsInRange, (size_t)numObjsInRange }) { + const auto obj = entity->AsObject(); + if (!obj->physicalFlags.bDisableMoveForce) { + continue; + } + + const auto distSq = (obj->GetPosition() - entranceCenter).SquaredMagnitude(); // Using `SqMag` instead of `Mag` + if (distSq < closestDistSq) { + closest = obj; + closestDistSq = distSq; } - return closest; } - return nullptr; + return closest; } // 0x43F4B0 int32 CEntryExitManager::FindNearestEntryExit(const CVector2D& position, float range, int32 ignoreArea) { - CRect rect(position.x - range, position.y - range, position.x + range, position.y + range); CPtrListSingleLink enexInRange{}; - mp_QuadTree->GetAllMatching(rect, enexInRange); + mp_QuadTree->GetAllMatching(CRect{ position, range }, enexInRange); float closestDist2D{ 2.f * range }; CEntryExit* closest{}; @@ -267,16 +270,20 @@ int32 CEntryExitManager::FindNearestEntryExit(const CVector2D& position, float r next = it->GetNext(); auto* enex = it->ItemAs(); - if (enex->GetMyOrLinkedArea() != ignoreArea) { - const auto dist = (enex->GetPosition2D() - position).Magnitude(); // TODO: Use SqMag - if (dist < closestDist2D) { - closest = enex; - closestDist2D = dist; - } + if (enex->GetLinkedOrThis()->GetArea() == ignoreArea) { + continue; + } + + const auto dist = (enex->GetPosition2D() - position).Magnitude(); // TODO: Use SqMag + if (dist < closestDist2D) { + closest = enex; + closestDist2D = dist; } } - return closest ? mp_poolEntryExits->GetIndex(closest) : -1; + return closest + ? mp_poolEntryExits->GetIndex(closest) + : -1; } // 0x43F180 @@ -323,6 +330,7 @@ void CEntryExitManager::LinkEntryExit(CEntryExit* enex) { if (linkedEnEx->m_pLink) { linkedEnEx->m_pLink = enex; } + // ????? linkedEnEx->m_nTimeOn = 0; linkedEnEx->m_nTimeOff = 24; } @@ -375,6 +383,39 @@ void CEntryExitManager::SetAreaCodeForVisibleObjects() { ms_oldAreaCode = CGame::currArea; } +// NOTSA (Code somewhat based on 0x43FC00) +void CEntryExitManager::AddEnExToWorld(CEntryExit* enex) { + // Calculate rotated corner positions (We ain't gonna use a matrix for this like they did, too complicated) + const auto& r = enex->m_recEntrance; + const auto rc = r.GetCenter(); + CVector2D corners[]{ + { r.right, r.top }, + { r.left, r.bottom } + }; + + // Rotate it around the center + for (auto& corner : corners) { + corner = rc + (corner - rc).RotatedBy(enex->m_fEntranceAngleRad); // NOTE: If doesn't work properly, negate (-angle) the angle + } + + const auto GetMinMaxAxis = [&](size_t axis) { + return rng::minmax( + corners | rng::views::transform([axis](auto&& c) { return c[axis]; }) + ); + }; + + // Calculate min-max coordinates + const auto [minX, maxX] = GetMinMaxAxis(0); + const auto [minY, maxY] = GetMinMaxAxis(1); + + // Add it to the QuadTree using the calculated bounding rect + mp_QuadTree->AddItem(enex, {minX, minY, maxX, maxY}); +} + +bool CEntryExitManager::WeAreInInteriorTransition() { + return ms_exitEnterState != 0; +} + // 0x5D55C0 bool CEntryExitManager::Load() { // Load entry exit stack @@ -404,7 +445,7 @@ bool CEntryExitManager::Load() { enex->m_pLink = nullptr; } } else { - assert(0); // NOTSA - Probably corrupted save file or something. + NOTSA_UNREACHABLE(); // NOTSA - Probably corrupted save file or something. } CGenericGameStorage::LoadDataFromWorkBuffer(&enexIdx, sizeof(enexIdx)); @@ -432,33 +473,3 @@ bool CEntryExitManager::Save() { return true; } - -// NOTSA (Code somewhat based on 0x43FC00) -void CEntryExitManager::AddEnExToWorld(CEntryExit* enex) { - // Calculate corner positions (We ain't gonna use a matrix for this like they did, too complicated) - const auto& r = enex->m_recEntrance; - CVector2D corners[]{ - { r.right, r.top }, - { r.left, r.bottom } - }; - for (auto& c : corners) { - c = c.RotatedBy(enex->m_fEntranceAngleRad); // NOTE: If doesn't work properly, negate (-angle) the angle - } - - const auto GetMinMax = [&](size_t axis) { - return rng::minmax( - corners | rng::views::transform([axis](auto&& c) { return c[axis]; }) - ); - }; - - // Calculate min-max coordinates - const auto [minX, maxX] = GetMinMax(0); - const auto [minY, maxY] = GetMinMax(1); - - // Add it to the QuadTree using the calculated bounding rect - mp_QuadTree->AddItem(enex, {minX, minY, maxX, maxY}); -} - -bool CEntryExitManager::WeAreInInteriorTransition() { - return plugin::CallAndReturn(); -} diff --git a/source/game_sa/PedGroup.cpp b/source/game_sa/PedGroup.cpp index 19824db33a..1f21ef9129 100644 --- a/source/game_sa/PedGroup.cpp +++ b/source/game_sa/PedGroup.cpp @@ -61,3 +61,7 @@ void CPedGroup::Teleport(const CVector* pos) { int32 CPedGroup::GetId() const { return CPedGroups::GetGroupId(this); } + +bool CPedGroup::IsActive() const { + return CPedGroups::ms_activeGroups[GetId()]; +} diff --git a/source/game_sa/PedGroup.h b/source/game_sa/PedGroup.h index bbe5f8db84..8a5b38a53e 100644 --- a/source/game_sa/PedGroup.h +++ b/source/game_sa/PedGroup.h @@ -41,6 +41,7 @@ class CPedGroup { inline CPedGroupIntelligence& GetIntelligence() { return m_groupIntelligence; } int32 GetId() const; + bool IsActive() const; inline auto& GetMembership() const { return m_groupMembership; } inline auto& GetMembership() { return m_groupMembership; } diff --git a/source/game_sa/Streaming.cpp b/source/game_sa/Streaming.cpp index ff14c54f64..7033ad1058 100644 --- a/source/game_sa/Streaming.cpp +++ b/source/game_sa/Streaming.cpp @@ -3374,7 +3374,7 @@ void CStreaming::StreamPedsForInterior(int32 interiorType) { // * -2 - Unload model from slot (If there's any) // * Positive values - Load given model into slot // 0x40BDA0 -void CStreaming::StreamPedsIntoRandomSlots(int32 modelArray[TOTAL_LOADED_PEDS]) { +void CStreaming::StreamPedsIntoRandomSlots(const int32(&modelArray)[TOTAL_LOADED_PEDS]) { for (int32 i = 0; i < TOTAL_LOADED_PEDS; i++) { if (modelArray[i] >= 0) { // Load model into slot diff --git a/source/game_sa/Streaming.h b/source/game_sa/Streaming.h index 56e0df7e97..bba025da1b 100644 --- a/source/game_sa/Streaming.h +++ b/source/game_sa/Streaming.h @@ -370,7 +370,7 @@ class CStreaming { static bool StreamFireEngineAndFireman(bool bStreamForFire); static void StreamOneNewCar(); static void StreamPedsForInterior(int32 interiorType); - static void StreamPedsIntoRandomSlots(int32 modelArray[TOTAL_LOADED_PEDS]); + static void StreamPedsIntoRandomSlots(const int32 (&modelArray)[TOTAL_LOADED_PEDS]); static void StreamVehiclesAndPeds(); static void StreamVehiclesAndPeds_Always(const CVector& unused); static void StreamZoneModels(const CVector& unused); diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexFollowLeaderInFormation.h b/source/game_sa/Tasks/TaskTypes/TaskComplexFollowLeaderInFormation.h index 340edb031f..8733de588b 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexFollowLeaderInFormation.h +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexFollowLeaderInFormation.h @@ -17,9 +17,9 @@ class CTaskComplexFollowLeaderInFormation : public CTaskComplex { static constexpr auto Type = TASK_COMPLEX_FOLLOW_LEADER_IN_FORMATION; struct Offsets { - CVector2D offsets[8]; - CVector2D movingOffsets[8]; - float scale; + std::array offsets; + std::array movingOffsets; + float scale; }; static inline auto& ms_offsets = StaticRef(); From 2946435ef9c61ee01d12a947d06b37a233ce6eaa Mon Sep 17 00:00:00 2001 From: yukani Date: Sat, 3 Jun 2023 15:25:28 +0300 Subject: [PATCH 12/19] CLI argument parser and INI config manager (#568) --- source/app/app_debug.h | 2 +- source/app/app_game.cpp | 4 +- source/app/platform/win/win.cpp | 30 +++--- source/config.h | 23 ++-- source/dllmain.cpp | 45 ++++---- source/extensions/CommandLine.cpp | 76 +++++++++++++ source/extensions/CommandLine.h | 13 +++ source/extensions/Configs/FastLoader.hpp | 101 ++++++++++++++++++ source/extensions/Configuration.hpp | 93 ++++++++++++++++ source/extensions/utility.hpp | 7 ++ source/game_sa/Frontend/MenuManager_Input.cpp | 7 +- source/game_sa/LoadingScreen.cpp | 16 +-- source/game_sa/LoadingScreen.h | 56 ---------- .../game_sa/Scripts/CommandParser/ReadArg.hpp | 3 - .../game_sa/Scripts/CommandParser/Utility.hpp | 3 - 15 files changed, 360 insertions(+), 119 deletions(-) create mode 100644 source/extensions/CommandLine.cpp create mode 100644 source/extensions/CommandLine.h create mode 100644 source/extensions/Configs/FastLoader.hpp create mode 100644 source/extensions/Configuration.hpp diff --git a/source/app/app_debug.h b/source/app/app_debug.h index 9784bc4c74..574c257ed2 100644 --- a/source/app/app_debug.h +++ b/source/app/app_debug.h @@ -1,5 +1,5 @@ #pragma once -//#include "common.h" +#include "common.h" namespace notsa { namespace detail { diff --git a/source/app/app_game.cpp b/source/app/app_game.cpp index c57f3c19cc..fcea300b58 100644 --- a/source/app/app_game.cpp +++ b/source/app/app_game.cpp @@ -22,6 +22,8 @@ #include #include +#include "extensions/Configs/FastLoader.hpp" + void AppGameInjectHooks() { RH_ScopedCategory("App"); RH_ScopedNamespaceName("Game"); @@ -384,7 +386,7 @@ void FrontendIdle() { CameraSize(Scene.m_pRwCamera, nullptr, SCREEN_VIEW_WINDOW, SCREEN_ASPECT_RATIO); CVisibilityPlugins::SetRenderWareCamera(Scene.m_pRwCamera); - if (FastLoadSettings.ShouldLoadSaveGame()) { + if (g_FastLoaderConfig.ShouldLoadSaveGame()) { return; // Don't render anything } diff --git a/source/app/platform/win/win.cpp b/source/app/platform/win/win.cpp index ae9972ba49..06028ed7ee 100644 --- a/source/app/platform/win/win.cpp +++ b/source/app/platform/win/win.cpp @@ -22,6 +22,8 @@ #include "MenuManager.h" #include +#include "extensions/Configs/FastLoader.hpp" + extern void WinPsInjectHooks(); static LPSTR AppClassName = LPSTR(APP_CLASS); @@ -524,22 +526,22 @@ bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { CLoadingScreen::DoPCTitleFadeIn(); CLoadingScreen::Shutdown(); }; - if (!FastLoadSettings.NoEAX) { + if (!g_FastLoaderConfig.NoEAX) { ProcessSplash(false); } - if (!FastLoadSettings.NoNVidia) { + if (!g_FastLoaderConfig.NoNVidia) { ProcessSplash(true); } ChangeGameStateTo(GAME_STATE_LOGO); break; } case GAME_STATE_LOGO: { - if (!FastLoadSettings.NoLogo) { + if (!g_FastLoaderConfig.NoLogo) { if (!Windowed) { VideoPlayer::Play(nCmdShow, "movies\\Logo.mpg"); } } - ChangeGameStateTo(FastLoadSettings.NoLogo ? GAME_STATE_TITLE : GAME_STATE_PLAYING_LOGO); + ChangeGameStateTo(g_FastLoaderConfig.NoLogo ? GAME_STATE_TITLE : GAME_STATE_PLAYING_LOGO); break; } case GAME_STATE_PLAYING_LOGO: @@ -566,17 +568,17 @@ bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { break; } case GAME_STATE_TITLE: { - if (!FastLoadSettings.NoTitleOrIntro) { + if (!g_FastLoaderConfig.NoTitleOrIntro) { VideoPlayer::Shutdown(); VideoPlayer::Play(nCmdShow, FrontEndMenuManager.GetMovieFileName()); } - ChangeGameStateTo(FastLoadSettings.NoTitleOrIntro ? GAME_STATE_FRONTEND_LOADING : GAME_STATE_PLAYING_INTRO); + ChangeGameStateTo(g_FastLoaderConfig.NoTitleOrIntro ? GAME_STATE_FRONTEND_LOADING : GAME_STATE_PLAYING_INTRO); break; } case GAME_STATE_FRONTEND_LOADING: { VideoPlayer::Shutdown(); CLoadingScreen::Init(true, false); - if (!FastLoadSettings.NoCopyright) { + if (!g_FastLoaderConfig.NoCopyright) { CLoadingScreen::DoPCTitleFadeOut(); } if (!CGame::InitialiseEssentialsAfterRW()) { @@ -595,7 +597,7 @@ bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { VideoModeNotSelected = false; } ChangeGameStateTo(GAME_STATE_FRONTEND_IDLE); - if (FastLoadSettings.NoCopyright) { + if (g_FastLoaderConfig.NoCopyright) { CLoadingScreen::SkipCopyrightSplash(); } else { CLoadingScreen::DoPCTitleFadeIn(); @@ -605,13 +607,13 @@ bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { case GAME_STATE_FRONTEND_IDLE: { // 0x748CB2 WINDOWPLACEMENT wndpl{ .length = sizeof(WINDOWPLACEMENT) }; VERIFY(GetWindowPlacement(PSGLOBAL(window), &wndpl)); - if (FastLoadSettings.ShouldLoadSaveGame()) { + if (g_FastLoaderConfig.ShouldLoadSaveGame()) { RsEventHandler(rsFRONTENDIDLE, nullptr); // We need to still run the frontend processing once because it has some important stuff - if ((GetAsyncKeyState(FastLoadSettings.SkipSaveGameLoadKey) & 0xF000) == 0) { - FastLoadSettings.StartGame(FastLoadSettings.SaveGameToLoad); // Load game + if ((GetAsyncKeyState(g_FastLoaderConfig.SkipSaveGameLoadKey) & 0xF000) == 0) { + g_FastLoaderConfig.StartGame(g_FastLoaderConfig.SaveGameToLoad); // Load game } - FastLoadSettings.TriedLoadingSaveGame = true; - } else if (FastLoadSettings.RenderAtAllTimes || wndpl.showCmd != SW_SHOWMINIMIZED) { + g_FastLoaderConfig.TriedLoadingSaveGame = true; + } else if (g_FastLoaderConfig.RenderAtAllTimes || wndpl.showCmd != SW_SHOWMINIMIZED) { RsEventHandler(rsFRONTENDIDLE, nullptr); } if (FrontEndMenuManager.m_bMenuActive && !FrontEndMenuManager.m_bLoadingData) { @@ -624,7 +626,7 @@ bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { NOTSA_SWCFALLTHRU; // Fall down and start loading } case GAME_STATE_LOADING_STARTED: { - if (!FastLoadSettings.NoLoadingTune) { + if (!g_FastLoaderConfig.NoLoadingTune) { AudioEngine.StartLoadingTune(); } diff --git a/source/config.h b/source/config.h index 386e2e7102..0b5f497a75 100644 --- a/source/config.h +++ b/source/config.h @@ -1,14 +1,25 @@ #pragma once -#define MORE_LANGUAGES -#define USE_ORIGINAL_CODE FALSE -#define USE_BUILD_INFORMATION +// Disables many extensions and non-vanilla features +//#define BAREBONES + #define BUILD_PC +#define USE_BUILD_INFORMATION + +#ifndef BAREBONES + +#define MORE_LANGUAGES +#define USE_ORIGINAL_CODE #define USE_ADDITIONAL_CHEATS +// Enables opcodes from III/VC that are not implemented in SA +#define IMPLEMENT_UNSUPPORTED_OPCODES + +// Extensions +#define EXT_FAST_LOADER + +#endif // !BAREBONES + #ifdef BUILD_PC //#define USE_EU_STUFF #endif - -// Enables opcodes from III/VC that are not implemented in SA -#define IMPLEMENT_UNSUPPORTED_OPCODES diff --git a/source/dllmain.cpp b/source/dllmain.cpp index 4fccc1e9d4..8169a63759 100644 --- a/source/dllmain.cpp +++ b/source/dllmain.cpp @@ -3,9 +3,11 @@ // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: https://pvs-studio.com #include "StdInc.h" - #include "config.h" +#include "extensions/CommandLine.h" +#include "extensions/Configuration.hpp" + void InjectHooksMain(HMODULE hThisDLL); void DisplayConsole() @@ -25,33 +27,18 @@ void WaitForDebugger() { } } -namespace CommandLineArguments { +static constexpr auto DEFAULT_INI_FILENAME = "gta-reversed.ini"; -std::vector Get() { - std::vector out; - int numArgs{0}; - LPWSTR* szArgs = CommandLineToArgvW(GetCommandLineW(), &numArgs); - out.reserve(numArgs); - if (szArgs) { - for (int i = 0; i < numArgs; i++) { - out.emplace_back(szArgs[i]); - } - } - LocalFree(szArgs); - return out; -} +#include "extensions/Configs/FastLoader.hpp" -void Process() { - using namespace std::literals; - const auto args = Get(); - for (const auto& arg : args) { - if (arg == L"--debug") { - WaitForDebugger(); - } - } -} +void LoadConfigurations() { + // Firstly load the INI into the memory. + g_ConfigurationMgr.Load(DEFAULT_INI_FILENAME); -} // namespace CommandLineArguments + // Then load all specific configurations. + g_FastLoaderConfig.Load(); + // ... +} BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -67,7 +54,13 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser } DisplayConsole(); - CommandLineArguments::Process(); + CommandLine::Load(__argc, __argv); + + if (CommandLine::waitForDebugger) + WaitForDebugger(); + + LoadConfigurations(); + InjectHooksMain(hModule); break; } diff --git a/source/extensions/CommandLine.cpp b/source/extensions/CommandLine.cpp new file mode 100644 index 0000000000..d06fe92478 --- /dev/null +++ b/source/extensions/CommandLine.cpp @@ -0,0 +1,76 @@ +#include "StdInc.h" +#include "CommandLine.h" + +#include +#include +#include + +namespace CommandLine { + bool unhookAll{false}; + std::vector unhookSome{}; + std::vector unhookExcept{}; + + bool waitForDebugger{false}; + + void ProcessArgument(const char* arg) { + const auto str = std::string_view{arg, std::strlen(arg)}; + + if (str == "--debug") { + waitForDebugger = true; + return; + } + + if (str == "--unhook-all") { + if (!unhookExcept.empty()) { + DEV_LOG("warn: --unhook-except has been called previously, unhook-all will be effective."); + unhookExcept.clear(); // we aren't gonna use it. + } + if (!unhookSome.empty()) { + DEV_LOG("warn: --unhook-some has been called previously, unhook-all will be effective."); + unhookSome.clear(); // we aren't gonna use it. + } + unhookAll = true; + return; + } + + if (str.starts_with("--unhook-except=")) { + if (unhookAll) { + DEV_LOG("warn: --unhook-all has been called previously, unhook-all will be effective."); + } else { + if (!unhookSome.empty()) { + DEV_LOG("warn: --unhook-some has been called previously, unhook-except will be effective."); + unhookSome.clear(); // we aren't gonna use it. + } + + for (auto hook : SplitStringView(str.substr(str.find('=') + 1), ",")) { + unhookExcept.emplace_back(std::move(hook)); + } + } + return; + } + + if (str.starts_with("--unhook=")) { + if (unhookAll) { + DEV_LOG("warn: --unhook-all has been called previously, unhook-all will be effective."); + } else if (!unhookExcept.empty()) { + DEV_LOG("warn: --unhook-except has been called previously, unhook-except will be effective."); + } else { + for (auto hook : SplitStringView(str.substr(str.find('=') + 1), ",")) { + unhookSome.emplace_back(std::move(hook)); + } + } + return; + } + + DEV_LOG("warn: unknown argument '{}'", str); + } + + void CommandLine::Load(int argc, char** argv) { + if (--argc <= 0) + return; // No need to process, there is no argument supplied. + ++argv; // Skip first 'argument' which is the exec path. + + for (auto& arg : std::span{argv, (size_t)argc}) + ProcessArgument(arg); + } +} // namespace CommandLine diff --git a/source/extensions/CommandLine.h b/source/extensions/CommandLine.h new file mode 100644 index 0000000000..dd1d7019b9 --- /dev/null +++ b/source/extensions/CommandLine.h @@ -0,0 +1,13 @@ +#pragma once + +namespace CommandLine { + // Hook features + extern bool unhookAll; + extern std::vector unhookSome; + extern std::vector unhookExcept; + + // Debug features + extern bool waitForDebugger; + + void Load(int argc, char** argv); +} // namespace CommandLine; diff --git a/source/extensions/Configs/FastLoader.hpp b/source/extensions/Configs/FastLoader.hpp new file mode 100644 index 0000000000..fb83663aaf --- /dev/null +++ b/source/extensions/Configs/FastLoader.hpp @@ -0,0 +1,101 @@ +#pragma once +#include +#include "app_debug.h" +#include "AudioEngine.h" +#include "Common.h" +#include "FileMgr.h" +#include "GenericGameStorage.h" +#include "MenuManager.h" + +#include "extensions/Configuration.hpp" + +inline struct FastLoaderConfig { + bool TriedLoadingSaveGame = false; //< [Runtime Var] Whenever `SaveGameToLoad` was tried to be loaded + + INI_CONFIG_SECTION("FastLoader"); + + int32 SaveGameToLoad = -2; //< -2 - Don't load, -1 = Load first available, 0 <= - load from save slot + uint32 SkipSaveGameLoadKey = VK_CONTROL; //< Skip auto-loading save game (If enabled) + uint32 SoundDelay = 50; + + bool NoEAX = false; //< Skip EAX splash + bool NoNVidia = false; //< Skip nVidia splash + bool NoLogo = false; //< Skip Logo.mpg + bool NoTitleOrIntro = false; //< Skip GTAtitles.mpg + bool NoCopyright = false; //< Skip Copyright screen + bool NoFading = false; //< Skip fading (takes quite a bit of time) + bool NoLoadScreen = false; //< Skip load pre-game screen + bool NoLoadBar = false; //< Skip load bar in pre-game load screen + bool NoLoadingTune = false; //< Skip GTA theme music + bool NoDbgLogScreens = true; //< Don't display [notsa] loading screen debug messages to console + bool RenderAtAllTimes = true; //< Render even when minimized + float ScreenChangeTime = 5.f; //< Time to change splash screens in the loading screen (seconds?) + + //! If there's a game to load, but not loaded yet... + bool ShouldLoadSaveGame() const { return SaveGameToLoad >= -1 && !TriedLoadingSaveGame; } + + //! Start the game from a slot + //! `slot` can have the same values as `SaveGameToLoad` + bool StartGame(int32 slot) { + const auto CheckIfSaveFileExists = [](int32 slot) { + CFileMgr::SetDirMyDocuments(); + const bool exists = std::filesystem::exists(std::format("GTASAsf{}.b", slot + 1)); + CFileMgr::ChangeDir(""); + return exists; + }; + + if (slot == -1) { // Find first valid slot and load that + for (auto i = 0u; i < MAX_SAVEGAME_SLOTS; i++) { + if (CheckIfSaveFileExists(i)) { // Save file IDs start from 1, not 0 + return StartGame(i); // Load this slot + } + } + DEV_LOG("Couldn't find any savegame to load!"); + return false; + } + + // Load game from slot + assert(slot >= 0); + if (!CheckIfSaveFileExists(slot)) { + DEV_LOG("Save slot {} is empty!", slot + 1); + return false; + } + DEV_LOG("Loading game from slot ({})", slot + 1); + + if (!NoLoadingTune) { + for (size_t i = 0; i < SoundDelay; i++) { + AudioEngine.ServiceLoadingTune(1.f); + } + } + + // Actually load the game (By simulating the user going in the menu and doing it) + FrontEndMenuManager.SimulateGameLoad(false, slot); + + return true; + } + + void Load() { +#ifdef EXT_FAST_LOADER + if (!GET_INI_CONFIG_VALUE("Enable", true)) { + return; + } + + STORE_INI_CONFIG_VALUE(SaveGameToLoad, -1); + STORE_INI_CONFIG_VALUE(SkipSaveGameLoadKey, VK_CONTROL); + STORE_INI_CONFIG_VALUE(SoundDelay, 50); + + STORE_INI_CONFIG_VALUE(NoEAX, true); + STORE_INI_CONFIG_VALUE(NoNVidia, true); + STORE_INI_CONFIG_VALUE(NoLogo, true); + STORE_INI_CONFIG_VALUE(NoTitleOrIntro, true); + STORE_INI_CONFIG_VALUE(NoCopyright, true); + STORE_INI_CONFIG_VALUE(NoFading, true); + STORE_INI_CONFIG_VALUE(NoLoadScreen, true); + STORE_INI_CONFIG_VALUE(NoLoadBar, false); + STORE_INI_CONFIG_VALUE(NoLoadingTune, true); + STORE_INI_CONFIG_VALUE(NoDbgLogScreens, true); + STORE_INI_CONFIG_VALUE(RenderAtAllTimes, true); + STORE_INI_CONFIG_VALUE(ScreenChangeTime, 5.f); +#endif + } +} g_FastLoaderConfig{}; diff --git a/source/extensions/Configuration.hpp b/source/extensions/Configuration.hpp new file mode 100644 index 0000000000..31990d8c54 --- /dev/null +++ b/source/extensions/Configuration.hpp @@ -0,0 +1,93 @@ +#pragma once +#include +#include +#include +#include + +#include "app_debug.h" + +static inline class { + using IniContent = std::unordered_map>; + IniContent m_content{}; + +public: + template + std::optional GetIniValue(const std::string& section, const std::string& key) { + if (m_content.empty() || !m_content.contains(section) || !m_content[section].contains(key)) + return {}; + + const auto& str = m_content[section][key]; + if constexpr (std::is_same_v) { + return str; + } else if constexpr (std::is_same_v) { + return str == "true" || str == "TRUE" || str == "1"; + } else if constexpr (notsa::is_standard_integer || std::is_floating_point_v) { + Ret rt{}; + + auto [_, ec] = std::from_chars(str.data(), str.data() + str.size(), rt); + if (ec != std::errc{}) + return {}; + + return rt; + } else { + return {}; + } + } + + void Load(const std::string& fileName) { + std::ifstream in(fileName); + assert(!in.bad()); + + ParseIniFile(in, m_content); + } + +private: + static void ParseIniFile(std::ifstream& file, IniContent& ini) { + std::string sectionName{""}; + std::string line; + while (std::getline(file, line)) { + const auto StripText = [](std::string& s) { + s.erase(0, s.find_first_not_of(" \t\n\r\f\v")); + s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1); + }; + + if (const auto cf = line.find(';'); cf != std::string::npos) { + line.erase(cf); + } + + StripText(line); + + if (line.empty()) + continue; + + if (line.front() == '[' && line.back() == ']') { + sectionName = line.substr(1, line.size() - 2); + continue; + } + + if (const auto ef = line.find('='); ef != std::string::npos) { + auto key = line.substr(0, ef); + auto value = line.substr(ef + 1); + + StripText(key); + StripText(value); + + DEV_LOG("key: {} value: {}", key, value); + ini[sectionName][std::move(key)] = std::move(value); + continue; + } + + DEV_LOG("err: something wrong!"); + break; + } + } +} g_ConfigurationMgr; + +#define INI_CONFIG_SECTION(name) \ + static constexpr auto IniSectionName = name + +#define GET_INI_CONFIG_VALUE(key, _default) \ + g_ConfigurationMgr.GetIniValue(IniSectionName, key).value_or(_default) + +#define STORE_INI_CONFIG_VALUE(key_var, _default) \ + key_var = g_ConfigurationMgr.GetIniValue(IniSectionName, #key_var).value_or(_default) diff --git a/source/extensions/utility.hpp b/source/extensions/utility.hpp index 947aeacf99..3351c4fec7 100644 --- a/source/extensions/utility.hpp +++ b/source/extensions/utility.hpp @@ -304,4 +304,11 @@ class Singleton { } }; +template +concept is_any_of_type_v = (std::same_as || ...); + +//! Check if the type is an integer type excluding bool and character types. +template +inline constexpr bool is_standard_integer = std::is_integral_v && !is_any_of_type_v; + }; diff --git a/source/game_sa/Frontend/MenuManager_Input.cpp b/source/game_sa/Frontend/MenuManager_Input.cpp index 9130a1e8a8..fbe22d143b 100644 --- a/source/game_sa/Frontend/MenuManager_Input.cpp +++ b/source/game_sa/Frontend/MenuManager_Input.cpp @@ -6,7 +6,8 @@ #include "app/app.h" #include "VideoMode.h" // todo #include "ControllerConfigManager.h" -#include "LoadingScreen.h" + +#include "extensions/Configs/FastLoader.hpp" /*! * @addr 0x57FD70 @@ -230,7 +231,7 @@ void CMenuManager::CheckForMenuClosing() { if ((!field_35 || !m_bActivateMenuNextFrame) && !m_bLoadingData) { AudioEngine.ReportFrontendAudioEvent(AE_FRONTEND_START); - if (!FastLoadSettings.ShouldLoadSaveGame()) { // If loading, skip menu audio + if (!g_FastLoaderConfig.ShouldLoadSaveGame()) { // If loading, skip menu audio AudioEngine.Service(); } } @@ -333,7 +334,7 @@ void CMenuManager::CheckForMenuClosing() { AudioEngine.ReportFrontendAudioEvent(AE_FRONTEND_START); - if (!FastLoadSettings.ShouldLoadSaveGame()) { // If loading, skip menu audio + if (!g_FastLoaderConfig.ShouldLoadSaveGame()) { // If loading, skip menu audio AudioEngine.Service(); } diff --git a/source/game_sa/LoadingScreen.cpp b/source/game_sa/LoadingScreen.cpp index 0e9d857d44..bf2be98d18 100644 --- a/source/game_sa/LoadingScreen.cpp +++ b/source/game_sa/LoadingScreen.cpp @@ -9,6 +9,8 @@ #include "platform.h" #include "LoadingScreen.h" +#include "extensions/Configs/FastLoader.hpp" + void CLoadingScreen::InjectHooks() { RH_ScopedClass(CLoadingScreen); RH_ScopedCategoryGlobal(); @@ -175,9 +177,10 @@ void CLoadingScreen::Continue() { // 0x590370 void CLoadingScreen::RenderLoadingBar() { +#ifdef PRINT_LOADMSG // NOTSA - // TODO: Add some kind of ifdef for dev stuff // TODO: Fix new-line not rendered when using fastload into a savegame + char loadingMsg[1024]; *std::format_to(loadingMsg, "{}\n{}", m_LoadingGxtMsg1, m_LoadingGxtMsg2) = 0; CFont::SetOrientation(eFontAlignment::ALIGN_LEFT); @@ -189,6 +192,7 @@ void CLoadingScreen::RenderLoadingBar() { loadingMsg ); CFont::RenderFontBuffer(); +#endif if (m_TimeBarAppeared == 0.0f) { m_TimeBarAppeared = GetClockTime(); @@ -240,7 +244,7 @@ void CLoadingScreen::DisplayPCScreen() { DefinedState2d(); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, RWRSTATE(TRUE)); RenderSplash(); - if (!FastLoadSettings.NoLoadBar) { + if (!g_FastLoaderConfig.NoLoadBar) { if (m_currDisplayedSplash > 0 && (!m_bFading || m_currDisplayedSplash != 1)) { RenderLoadingBar(); } @@ -299,7 +303,7 @@ void CLoadingScreen::DoPCScreenChange(uint32 finish) { #endif } - if (!FastLoadSettings.NoFading) { + if (!g_FastLoaderConfig.NoFading) { for (auto i = 20; i > 0; i--) { m_FadeAlpha = 0; DisplayPCScreen(); @@ -344,11 +348,11 @@ void CLoadingScreen::NewChunkLoaded() { } #ifdef FIX_BUGS // Fix copyright screen appearing instead of an actual loading screen splash - if (m_currDisplayedSplash && delta < FastLoadSettings.ScreenChangeTime) { + if (m_currDisplayedSplash && delta < g_FastLoaderConfig.ScreenChangeTime) { #else if ((m_currDisplayedSplash && delta < 5.0f) || (!m_currDisplayedSplash && delta < 5.5f)) { #endif - if (!FastLoadSettings.NoLoadScreen || !FastLoadSettings.NoLoadBar) { + if (!g_FastLoaderConfig.NoLoadScreen || !g_FastLoaderConfig.NoLoadBar) { DisplayPCScreen(); } } else { // New splash screen @@ -377,7 +381,7 @@ void CLoadingScreen::SkipCopyrightSplash() { // 0x53DED0 void LoadingScreen(const char* msg1, const char* msg2, const char* msg3) { if (msg1) { - if (!FastLoadSettings.NoDbgLogScreens) { // Very slow, so skip it + if (!g_FastLoaderConfig.NoDbgLogScreens) { // Very slow, so skip it DEV_LOG("Loadingscreen: {} [{}][{}]", msg1, msg2 ? msg2 : "NULL", msg3 ? msg3 : "NULL"); } CLoadingScreen::SetLoadingBarMsg(msg1, msg2); diff --git a/source/game_sa/LoadingScreen.h b/source/game_sa/LoadingScreen.h index f3c0c02e0b..9737b6e264 100644 --- a/source/game_sa/LoadingScreen.h +++ b/source/game_sa/LoadingScreen.h @@ -8,62 +8,6 @@ class CSprite2d; -// TODO: Move this to an appropriate place -inline struct FastLoadSettings { - int32 SaveGameToLoad = -1; //< -2 - Don't load, -1 = Load first available, 0 <= - load from save slot - bool TriedLoadingSaveGame = false; //< [Runtime Var] Whenever `SaveGameToLoad` was tried to be loaded - uint32 SkipSaveGameLoadKey = VK_CONTROL; //< Skip auto-loading save game (If enabled) - uint32 SoundDelay = 50; - - bool NoEAX = true; //< Skip EAX splash - bool NoNVidia = true; //< Skip nVidia splash - bool NoLogo = true; //< Skip Logo.mpg - bool NoTitleOrIntro = true; //< Skip GTAtitles.mpg - bool NoCopyright = true; //< Skip Copyright screen - bool NoFading = true; //< Skip fading (takes quite a bit of time) - bool NoLoadScreen = true; //< Skip load pre-game screen - bool NoLoadBar = false; //< Skip load bar in pre-game load screen - bool NoLoadingTune = true; //< Skip GTA theme music - bool NoDbgLogScreens = true; //< Don't display [notsa] loading screen debug messages to console - bool RenderAtAllTimes = true; //< Render even when minimized - float ScreenChangeTime = 5.f; //< Time to change splash screens in the loading screen (seconds?) - - //! If there's a game to load, but not loaded yet... - bool ShouldLoadSaveGame() const { return SaveGameToLoad >= -1 && !TriedLoadingSaveGame; } - - //! Start the game from a slot - //! `slot` can have the same values as `SaveGameToLoad` - bool StartGame(int32 slot) { - if (slot == -1) { // Find first valid slot and load that - CFileMgr::SetDirMyDocuments(); - for (auto i = 0u; i < MAX_SAVEGAME_SLOTS; i++) { - if (std::filesystem::exists(std::format("GTASAsf{}.b", i + 1))) { // Save file IDs start from 1, not 0 - return StartGame(i); // Load this slot - } - } - CFileMgr::ChangeDir(""); - DEV_LOG("Didn't find any savegame to load!"); - return false; - } - - // Load game from slot - assert(slot >= 0); - - DEV_LOG("Loading game from slot ({})", slot); - - if (!NoLoadingTune) { - for (size_t i = 0; i < SoundDelay; i++) { - AudioEngine.ServiceLoadingTune(1.f); - } - } - - // Actually load the game (By simulating the user going in the menu and doing it) - FrontEndMenuManager.SimulateGameLoad(false, slot); - - return true; - } -} FastLoadSettings{}; - class CLoadingScreen { public: static constexpr size_t MAX_SPLASHES = 7u; diff --git a/source/game_sa/Scripts/CommandParser/ReadArg.hpp b/source/game_sa/Scripts/CommandParser/ReadArg.hpp index f11807706f..1be4080f07 100644 --- a/source/game_sa/Scripts/CommandParser/ReadArg.hpp +++ b/source/game_sa/Scripts/CommandParser/ReadArg.hpp @@ -79,9 +79,6 @@ concept PooledType = }; namespace detail { -//! Check if the type is an integer type excluding bool and character types. -template -inline constexpr bool is_standard_integer = std::is_integral_v && !is_any_of_type_v; //! Safely cast one arithmetic type to another (Checks for under/overflow in debug mode only), then casts to `T` template diff --git a/source/game_sa/Scripts/CommandParser/Utility.hpp b/source/game_sa/Scripts/CommandParser/Utility.hpp index 5a8b3badf6..bf1dddd81c 100644 --- a/source/game_sa/Scripts/CommandParser/Utility.hpp +++ b/source/game_sa/Scripts/CommandParser/Utility.hpp @@ -125,7 +125,4 @@ inline auto GetPedOrItsVehicle(CPed& ped) -> CPhysical& { template using nth_element_t = typename std::tuple_element>::type; -template -concept is_any_of_type_v = (std::same_as || ...); - }; // notsa From a3dfb52ff2bf69e8d3504759dfa719d8a30e4176 Mon Sep 17 00:00:00 2001 From: plakapenka Date: Sat, 8 Jul 2023 14:19:40 +0300 Subject: [PATCH 13/19] reverse CSimpleTransform (#567) --- source/game_sa/SimpleTransform.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/source/game_sa/SimpleTransform.cpp b/source/game_sa/SimpleTransform.cpp index e406a99989..dddb64f719 100644 --- a/source/game_sa/SimpleTransform.cpp +++ b/source/game_sa/SimpleTransform.cpp @@ -9,17 +9,36 @@ #include "SimpleTransform.h" +// 0x54EF40 void CSimpleTransform::UpdateRwMatrix(RwMatrix* out) { - ((void(__thiscall*)(CSimpleTransform*, RwMatrix*))0x54EF40)(this, out); + const float sinHeading = std::sin(m_fHeading); + const float cosHeading = std::cos(m_fHeading); + + out->right = { cosHeading, sinHeading, 0.0f }; + out->up = { -sinHeading, cosHeading, 0.0f }; + out->at = { 0.0f, 0.0f, 1.0f }; + out->pos = m_vPosn; + + RwMatrixUpdate(out); + } +// 0x54EF90 void CSimpleTransform::Invert(const CSimpleTransform& base) { - ((void(__thiscall*)(CSimpleTransform*, const CSimpleTransform&))0x54EF90)(this, base); + const float cosHeading = cosf(base.m_fHeading); + const float sinHeading = sinf(base.m_fHeading); + + m_vPosn.x = -(cosHeading * base.m_vPosn.x) - (sinHeading * base.m_vPosn.y); + m_vPosn.y = (sinHeading * base.m_vPosn.x) - (cosHeading * base.m_vPosn.y); + m_vPosn.z = -base.m_vPosn.z; + m_fHeading = -base.m_fHeading; } +// 0x54F1B0 void CSimpleTransform::UpdateMatrix(CMatrix* out) { - ((void(__thiscall*)(CSimpleTransform*, class CMatrix*))0x54F1B0)(this, out); + out->SetTranslate(m_vPosn); + out->SetRotateZOnly(m_fHeading); } From 9a083c0bae161080e429a9837b3316130de546ad Mon Sep 17 00:00:00 2001 From: Sebastian Jura Date: Sun, 9 Jul 2023 11:46:39 +0200 Subject: [PATCH 14/19] Reverse 0x504e490 Camera::Init (#571) --- source/game_sa/Cam.cpp | 69 +++++++++++++++++++++++++++++++++++++-- source/game_sa/Cam.h | 2 +- source/game_sa/Camera.cpp | 4 +++ source/game_sa/Camera.h | 4 +++ 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/source/game_sa/Cam.cpp b/source/game_sa/Cam.cpp index 34b2733d25..fff43bd7f5 100644 --- a/source/game_sa/Cam.cpp +++ b/source/game_sa/Cam.cpp @@ -8,8 +8,8 @@ void CCam::InjectHooks() { RH_ScopedClass(CCam); RH_ScopedCategory("Camera"); - RH_ScopedInstall(Constructor, 0x517730, { .reversed = false }); - RH_ScopedInstall(Init, 0x50E490, { .reversed = false }); + RH_ScopedInstall(Constructor, 0x517730); + RH_ScopedInstall(Init, 0x50E490); RH_ScopedInstall(CacheLastSettingsDWCineyCam, 0x50D7A0, { .reversed = false }); RH_ScopedInstall(DoCamBump, 0x50CB30); RH_ScopedInstall(Finalise_DW_CineyCams, 0x50DD70, { .reversed = false }); @@ -65,7 +65,70 @@ CCam* CCam::Constructor() { // 0x50E490 void CCam::Init() { - plugin::CallMethod<0x50E490, CCam*>(this); + m_vecFront = CVector(0, 0, -1); + m_vecUp = CVector(0, 0, 1); + m_nMode = eCamMode::MODE_FOLLOWPED; + m_bRotating = false; + m_nDoCollisionChecksOnFrameNum = 1; + m_nDoCollisionCheckEveryNumOfFrames = 9; + m_nFrameNumWereAt = 0; + m_bCollisionChecksOn = true; + m_fRealGroundDist = 0.0f; + m_fBetaSpeed = 0.0f; + m_fAlphaSpeed = 0.0f; + m_fCameraHeightMultiplier = 0.75; + m_fMaxRoleAngle = DegreesToRadians(20.0f); + m_fDistance = 30.0f; + m_fDistanceSpeed = 0.0f; + m_pLastCarEntered = nullptr; + m_pLastPedLookedAt = nullptr; + m_bResetStatics = true; + m_fHorizontalAngle = 0.0f; + m_fTilt = 0.0f; + m_fTiltSpeed = 0.0f; + m_bFixingBeta = false; + m_fCaMinDistance = 0.0f; + m_fCaMaxDistance = 0.0f; + m_bLookingBehind = false; + m_bLookingLeft = false; + m_bLookingRight = false; + m_fPlayerInFrontSyphonAngleOffSet = DegreesToRadians(20.0f); + m_fSyphonModeTargetZOffSet = 0.5f; + m_fRadiusForDead = 1.5f; + m_nDirectionWasLooking = 3; // TODO: enum + m_bLookBehindCamWasInFront = 0; + m_fRoll = 0.0f; + m_fRollSpeed = 0.0f; + m_fCloseInPedHeightOffset = 0.0f; + m_fCloseInPedHeightOffsetSpeed = 0.0f; + m_fCloseInCarHeightOffset = 0.0f; + m_fCloseInCarHeightOffsetSpeed = 0.0f; + m_fPedBetweenCameraHeightOffset = 0.0f; + m_fTargetBeta = 0.0f; + m_fBufferedTargetBeta = 0.0f; + m_fBufferedTargetOrientation = 0.0f; + m_fBufferedTargetOrientationSpeed = 0.0f; + m_fDimensionOfHighestNearCar = 0.0; + m_fBeta_Targeting = 0.0f; + m_fX_Targetting = 0.0f; + m_fY_Targetting = 0.0f; + m_pCarWeAreFocussingOn = nullptr; + m_pCarWeAreFocussingOnI = nullptr; + m_fCamBumpedHorz = 1.0f; + m_fCamBumpedVert = 0.0f; + m_nCamBumpedTime = 0; + for (int i = 0; i < 4; ++i) { + m_anTargetHistoryTime[i] = 0; + m_avecTargetHistoryPos[i] = CVector{}; + } + m_nCurrentHistoryPoints = 0; + gPlayerPedVisible = true; + gbCineyCamMessageDisplayed = 2; // TODO: enum + gCameraDirection = 3; // TODO: enum + gCameraMode = (eCamMode)-1; + gLastTime2PlayerCameraWasOK = 0; + gLastTime2PlayerCameraCollided = 0; + TheCamera.m_bCinemaCamera = false; } // 0x50D7A0 diff --git a/source/game_sa/Cam.h b/source/game_sa/Cam.h index 8b4578574b..8adac50837 100644 --- a/source/game_sa/Cam.h +++ b/source/game_sa/Cam.h @@ -102,7 +102,7 @@ class CCam { CVehicle* m_pCarWeAreFocussingOnI; float m_fCamBumpedHorz; float m_fCamBumpedVert; - uint32 m_nCamBumpedTime; + uint32 m_nCamBumpedTime; // TODO: Probably float CVector m_vecSourceSpeedOverOneFrame; CVector m_vecTargetSpeedOverOneFrame; CVector m_vecUpOverOneFrame; diff --git a/source/game_sa/Camera.cpp b/source/game_sa/Camera.cpp index 214e9d5f5b..cb1277aa0b 100644 --- a/source/game_sa/Camera.cpp +++ b/source/game_sa/Camera.cpp @@ -17,6 +17,10 @@ bool& CCamera::bDidWeProcessAnyCinemaCam = *reinterpret_cast(0xB6EC2D); CCamera& TheCamera = *reinterpret_cast(0xB6F028); bool& gbModelViewer = *reinterpret_cast(0xBA6728); int8& gbCineyCamMessageDisplayed = *(int8*)0x8CC381; // 2 +int32& gCameraDirection = *(int32*)0x8CC384; // 3 +eCamMode& gCameraMode = *(eCamMode*)0x8CC388; // -1 +uint32& gLastTime2PlayerCameraWasOK = *(uint32*)0xB6EC24; // 0 +uint32& gLastTime2PlayerCameraCollided = *(uint32*)0xB6EC28; // 0 bool& gPlayerPedVisible = *(bool*)0x8CC380; // true uint8& gCurCamColVars = *(uint8*)0x8CCB80; float& gCurDistForCam = *(float*)0x8CCB84; diff --git a/source/game_sa/Camera.h b/source/game_sa/Camera.h index 93591f14d8..2b157c6d01 100644 --- a/source/game_sa/Camera.h +++ b/source/game_sa/Camera.h @@ -516,6 +516,10 @@ extern bool& gbModelViewer; extern int8& gbCineyCamMessageDisplayed; extern bool& gPlayerPedVisible; extern uint8& gCurCamColVars; +extern int32& gCameraDirection; +extern eCamMode& gCameraMode; +extern uint32& gLastTime2PlayerCameraWasOK; +extern uint32& gLastTime2PlayerCameraCollided; extern float*& gpCamColVars; extern float (&gCamColVars)[28][6]; static inline auto& gpMadeInvisibleEntities = StaticRef, 0x9655A0>(); From aa48149dc004dd4ef80290d60cb8db9799fa8e7e Mon Sep 17 00:00:00 2001 From: Sebastian Jura Date: Sun, 9 Jul 2023 12:18:43 +0200 Subject: [PATCH 15/19] Audio zones (#570) --- source/game_sa/Audio/AudioZones.cpp | 28 +++++++++++++++---- source/game_sa/Audio/AudioZones.h | 28 +++++++++++++++---- source/game_sa/CompressedBox.cpp | 8 ++++++ source/game_sa/CompressedBox.h | 2 ++ source/game_sa/FileLoader.cpp | 12 ++++---- .../DebugModules/AudioZonesDebugModule.cpp | 18 ++++++++++++ .../DebugModules/AudioZonesDebugModule.h | 15 ++++++++++ .../toolsmenu/DebugModules/DebugModules.cpp | 2 ++ 8 files changed, 96 insertions(+), 17 deletions(-) create mode 100644 source/game_sa/CompressedBox.cpp create mode 100644 source/toolsmenu/DebugModules/AudioZonesDebugModule.cpp create mode 100644 source/toolsmenu/DebugModules/AudioZonesDebugModule.h diff --git a/source/game_sa/Audio/AudioZones.cpp b/source/game_sa/Audio/AudioZones.cpp index 95a6ede034..5ab5e5c70c 100644 --- a/source/game_sa/Audio/AudioZones.cpp +++ b/source/game_sa/Audio/AudioZones.cpp @@ -18,8 +18,8 @@ void CAudioZones::InjectHooks() { RH_ScopedCategory("Audio"); RH_ScopedInstall(Init, 0x5081A0, { .reversed = false }); - RH_ScopedInstall(RegisterAudioSphere, 0x5081C0, { .reversed = false }); - RH_ScopedInstall(RegisterAudioBox, 0x508240, { .reversed = false }); + RH_ScopedInstall(RegisterAudioSphere, 0x5081C0); + RH_ScopedInstall(RegisterAudioBox, 0x508240); RH_ScopedInstall(SwitchAudioZone, 0x508320, { .reversed = false }); RH_ScopedInstall(Update, 0x5083C0, { .reversed = false }); } @@ -33,13 +33,29 @@ void CAudioZones::Init() { } // 0x508240 -int32 CAudioZones::RegisterAudioBox(char* name, int32 id, bool b, float x1, float y1, float z1, float x2, float y2, float z2) { - return plugin::CallAndReturn(name, id, b, x1, y1, z1, x2, y2, z2); +void CAudioZones::RegisterAudioBox(char name[8], int32 id, bool isActive, CVector min, CVector max) { + + tAudioZoneBox audioZoneBox; + strcpy_s(audioZoneBox.m_szName, name); + audioZoneBox.m_bIsActive = isActive; // TODO: m_nFlags field has only 1 flag - Active or inactive and takes only 1 bit. Although gta uses 2 bytes for this, but how is the idea to define this single flag so as not to be confused in the future + audioZoneBox.m_nAudioZone = id; + audioZoneBox.m_Box = CompressedBox{ + .m_vecMin = CompressLargeVector(min), + .m_vecMax = CompressLargeVector(max), + }; + CAudioZones::m_aBoxes[m_NumBoxes++] = audioZoneBox; } // 0x5081C0 -int32 CAudioZones::RegisterAudioSphere(char* name, int32 id, bool b, float x1, float y1, float z1, float radius) { - return plugin::CallAndReturn(name, id, b, x1, y1, z1, radius); +void CAudioZones::RegisterAudioSphere(char name[8], int32 id, bool isActive, CVector position, float radius) { + tAudioZoneSphere audioZoneSphere; + strcpy_s(audioZoneSphere.m_szName, name); + audioZoneSphere.m_nAudioZone = id; + audioZoneSphere.m_bIsActive = isActive; // TODO: m_nFlags field has only 1 flag - Active or inactive and takes only 1 bit. Although gta uses 2 bytes for this, but how is the idea to define this single flag so as not to be confused in the future + audioZoneSphere.m_vPosn = position; + audioZoneSphere.m_fRadius = radius; + + CAudioZones::m_aSpheres[m_NumSpheres++] = audioZoneSphere; } // 0x508320 diff --git a/source/game_sa/Audio/AudioZones.h b/source/game_sa/Audio/AudioZones.h index 72a420ef99..a6840cc129 100644 --- a/source/game_sa/Audio/AudioZones.h +++ b/source/game_sa/Audio/AudioZones.h @@ -1,21 +1,35 @@ #pragma once #include "CompressedBox.h" +#include struct tAudioZoneSphere { char m_szName[8]; int16 m_nAudioZone; - uint16 m_nFlags; + union { + struct { + uint16 m_bIsActive : 1; + }; + uint16 m_nFlags; + }; CVector m_vPosn; float m_fRadius; }; VALIDATE_SIZE(tAudioZoneSphere, 0x1C); struct tAudioZoneBox { - char m_Name[8]; + char m_szName[8]; int16 m_nAudioZone; - uint16 m_Flags; + union { + struct { + uint16 m_bIsActive : 1; + }; + uint16 m_nFlags; + }; CompressedBox m_Box; + void DrawWireFrame(CRGBA color, const CMatrix& transform) const { + m_Box.DrawWireFrame(color, transform); + } }; VALIDATE_SIZE(tAudioZoneBox, 0x18); @@ -40,9 +54,13 @@ class CAudioZones { static void Init(); - static int32 RegisterAudioBox(char name[16], int32 id, bool b, float x1, float y1, float z1, float x2, float y2, float z2); - static int32 RegisterAudioSphere(char name[16], int32 id, bool b, float x1, float y1, float z1, float radius); + static void RegisterAudioBox(char name[8], int32 id, bool isActive, CVector min, CVector max); + static void RegisterAudioSphere(char name[8], int32 id, bool isActive, CVector position, float radius); static void SwitchAudioZone(char* zoneName, bool enable); static void Update(bool a1, CVector posn); + + static auto GetActiveAudioBoxes() { + return m_aBoxes | rng::views::take((size_t)m_NumBoxes); + } }; diff --git a/source/game_sa/CompressedBox.cpp b/source/game_sa/CompressedBox.cpp new file mode 100644 index 0000000000..9eaf4fdcde --- /dev/null +++ b/source/game_sa/CompressedBox.cpp @@ -0,0 +1,8 @@ +#include "StdInc.h" +#include "CompressedBox.h" +#include "Lines.h" + +void CompressedBox::DrawWireFrame(CRGBA color, const CMatrix& transform) const +{ + CBox{UncompressLargeVector(m_vecMin), UncompressLargeVector(m_vecMax)}.DrawWireFrame(color, transform); +} diff --git a/source/game_sa/CompressedBox.h b/source/game_sa/CompressedBox.h index f61c1d8c02..d7ee0b56a9 100644 --- a/source/game_sa/CompressedBox.h +++ b/source/game_sa/CompressedBox.h @@ -6,5 +6,7 @@ class CompressedBox { public: CompressedVector m_vecMin; CompressedVector m_vecMax; + + void DrawWireFrame(CRGBA color, const CMatrix& transform) const; }; VALIDATE_SIZE(CompressedBox, 0xC); diff --git a/source/game_sa/FileLoader.cpp b/source/game_sa/FileLoader.cpp index e8197c7c80..f2e4f551e9 100644 --- a/source/game_sa/FileLoader.cpp +++ b/source/game_sa/FileLoader.cpp @@ -257,18 +257,18 @@ char* CFileLoader::LoadLine(char*& bufferIt, int32& buffSize) { // IPL -> AUZO // 0x5B4D70 void CFileLoader::LoadAudioZone(const char* line) { - char name[16]; + char name[8]; int32 id; - int32 enabled; + int32 flags; float x1, y1, z1; float x2, y2, z2; float radius; - if (sscanf_s(line, "%s %d %d %f %f %f %f %f %f", SCANF_S_STR(name), &id, &enabled, &x1, &y1, &z1, &x2, &y2, &z2) == 9) { - CAudioZones::RegisterAudioBox(name, id, enabled != 0, x1, y1, z1, x2, y2, z2); + if (sscanf_s(line, "%s %d %d %f %f %f %f %f %f", SCANF_S_STR(name), &id, &flags, &x1, &y1, &z1, &x2, &y2, &z2) == 9) { + CAudioZones::RegisterAudioBox(name, id, flags == 1, {x1, y1, z1}, {x2, y2, z2}); } else { - VERIFY(sscanf_s(line, "%s %d %d %f %f %f %f", SCANF_S_STR(name), &id, &enabled, &x1, &y1, &z1, &radius) == 7); - CAudioZones::RegisterAudioSphere(name, id, enabled != 0, x1, y1, z1, radius); + VERIFY(sscanf_s(line, "%s %d %d %f %f %f %f", SCANF_S_STR(name), &id, &flags, &x1, &y1, &z1, &radius) == 7); + CAudioZones::RegisterAudioSphere(name, id, flags == 1, {x1, y1, z1}, radius); } } diff --git a/source/toolsmenu/DebugModules/AudioZonesDebugModule.cpp b/source/toolsmenu/DebugModules/AudioZonesDebugModule.cpp new file mode 100644 index 0000000000..baf351f53d --- /dev/null +++ b/source/toolsmenu/DebugModules/AudioZonesDebugModule.cpp @@ -0,0 +1,18 @@ +#include "StdInc.h" + +#include "AudioZonesDebugModule.h" +#include "Lines.h" + +void AudioZonesDebugModule::RenderMenuEntry() { + notsa::ui::DoNestedMenuIL({"Visualization", "Audio zones"}, [&] { ImGui::Checkbox("Show all", &m_ShowAudioZones); }); +} + +void AudioZonesDebugModule::Render3D() { + if (!m_ShowAudioZones) { + return; + } + + for (auto& box : CAudioZones::GetActiveAudioBoxes()) { + box.DrawWireFrame(CRGBA(255, 0, 0, 255), m_transform); + } +} diff --git a/source/toolsmenu/DebugModules/AudioZonesDebugModule.h b/source/toolsmenu/DebugModules/AudioZonesDebugModule.h new file mode 100644 index 0000000000..131e115381 --- /dev/null +++ b/source/toolsmenu/DebugModules/AudioZonesDebugModule.h @@ -0,0 +1,15 @@ +#pragma once + +#include "DebugModule.h" + +class AudioZonesDebugModule : public DebugModule { +public: + void Render3D() override final; + void RenderMenuEntry() override final; + +private: + bool m_ShowAudioZones{}; + bool m_IsOpen{}; + + CMatrix m_transform = CMatrix::GetIdentity(); +}; diff --git a/source/toolsmenu/DebugModules/DebugModules.cpp b/source/toolsmenu/DebugModules/DebugModules.cpp index 6229ba6907..f77d74f7de 100644 --- a/source/toolsmenu/DebugModules/DebugModules.cpp +++ b/source/toolsmenu/DebugModules/DebugModules.cpp @@ -23,6 +23,7 @@ #include "./Spawner/SpawnerDebugModule.hpp" #include "./ImGuiDebugModule.hpp" #include "./ScriptDebugModule.hpp" +#include "./AudioZonesDebugModule.h" DebugModules::DebugModules(ImGuiContext* ctx) : m_ImCtx(ctx) @@ -86,6 +87,7 @@ void DebugModules::CreateModules() { Add(); // Visualization + Extra Add(); // Visualization + Extra Add(); // Visualization + Extra + Add(); // Visualization + Extra Add(); // Stats + Extra } From 8691a46739fd935784a9b8bb9f52c8c509d64b0a Mon Sep 17 00:00:00 2001 From: Pirulax Date: Sun, 9 Jul 2023 21:45:32 +0200 Subject: [PATCH 16/19] Update the coding guidelines (#573) --- docs/CodingGuidelines.MD | 61 +++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/docs/CodingGuidelines.MD b/docs/CodingGuidelines.MD index 0612e6182a..a401c24f2f 100644 --- a/docs/CodingGuidelines.MD +++ b/docs/CodingGuidelines.MD @@ -4,10 +4,13 @@ * Please check and try to eliminate warnings from your code. ### Code style -* 4 space indentation, LF line endings. -* If some rule about something is not specified here, refer to how it's done in the code. -* Some classes may have *helper* functions to make code more readable. (usually denoted by *NOTSA* or *Helpers*) Try looking for and use them. +* 4 space indentation, LF line endings +* No hungarian notation [It's useless] +* If some rule about something is not specified here, refer to how it's done in the code +* Some classes may have *helper* functions to make code more readable. (Denoted by *NOTSA*) - Try adding new ones, or looking for and using them. +* Prefer `get`-ters/`set`-ters over raw member access * Use range-based for loops as much as possible. +* We encourage you to write modern C++, but if that's not your style, please keep the following in mind: ```cpp for (auto& element : array); // <-- GOOD @@ -17,23 +20,55 @@ for (int i = 0; i < std::size(array); i++); // <-- BAD ```cpp for (auto&& [i, e] : notsa::enumerate(array)); ``` +* If there's a dynamic `count` variable associated with a fixed size array, use `std::span` or `rng::views::take`. E.g.: +```cpp +// Bad +for (auto i = 0u; i < m_numThings; i++); + +// Good +for (auto& thing : std::span{ m_things, m_numThings }); +// Also good +for (auto& thing : m_things | rng::views::take(m_numThings)); -* Use `f` in float literals, omitting it makes them double. (e.g. `1.0f`) -* Use `std` library for generic functions like `min`, `max`, `lerp` etc. -* Function prototypes must look like it's from Android symbols. Except for output parameters like `T*` can be changed to `T&` to make the code prettier. -* Use lambdas for repetitive procedures in functions. -* Use `constexpr` variables instead of macros. +// ^ If these funcs are called more than once, make a helper function in the header. Like below: +auto GetActiveThings() { + return std::span{ m_things, m_numThings } +} +* Use `f` in float literals [As omitting it would make them a `double`] (e.g. `1.0f`) +* Use `std` library for generic functions like `min`, `max`, `lerp`, etc... +* `CVector` is interchangible with 3 floats [As is `CVector2D` with 2 floats] for function args +* Use lambdas for repetitive procedures in functions +* Use `constexpr` variables instead of macros +* Use `static inline` instead of `extern` and `static` in headers: +```cpp +class Foo { + static uint32& m_FooCount; // Bad + + static inline auto& m_FooCount = StaticRef(); // Good +} +``` #### Types * Use `auto` in function bodies if the variables' type is guessable. -* Guess for enum values, at least leave a TODO comment for it. -* Take care of const correctness. (e.g. `const char*` over `char*`) +* Guess for enum values [Or at least leave a `TODO` comment] +* Take care of const correctness [Especially of class methods] (e.g. `const char*` over `char*`) * Try to use SA types over RW as much as possible, **except** `RwMatrix`. (e.g. `CVector` for `RwV3d`, `CRGBA` for `RwRGBA`) * Use fixed width integer types (e.g. `uint8`, `int32`). -* Use `std::array` for arrays most of the time. Do not use it if the variable is just a pair of values or something basic like that. -* Do not use Win32 types. (e.g. `DWORD` -> `uint32`) +* Do not use Win32 integer types. [Except for Win32 exclusive code] (e.g. `DWORD` -> `uint32`) +* For array sizes, etc.. prefer using `unsigned` types over `signed` ones +* Whenever possible use `std::array` over `C-Style` array [as the former has bounds checking in debug mode, and can help us discover many bugs] + +#### Fixing bugs +Whenever you find a bug, we encourage you to fix it [and/or at least] leave a comment explaining what the bug is. +Bug fixes should only be active if `notsa::IsFixBugs()` returns `true`. +If that's not possible [due to code complexity], then wrap into an `#ifdef`: +```c +#ifdef FIX_BUGS +// Bug fixing code here +#endif +``` ### Contributing -Please make sure to test your contribution before opening a PR. Guess what places/missions are affected by your code and test them. Use debug menu (F7) for quick access to stuff. +Please make sure to test your code before opening a PR. Guess what places/missions are affected by your code and test them. Use debug menu (F7) for quick access to stuff. If you don't know how to test the code or think you have not tested enough specify it in the PR message. From 8b09631bbe40c74b17e6e378c6286ea8644b4da8 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Mon, 10 Jul 2023 22:34:33 +0200 Subject: [PATCH 17/19] Update ImGUI and use docking branch (#543) --- .gitmodules | 1 + libs/imgui | 2 +- source/app/app_game.cpp | 12 +- source/app/platform/win/win.cpp | 107 +++++++++++------- source/game_sa/Frontend/MenuManager_Input.cpp | 4 + source/game_sa/MenuManager.cpp | 2 +- source/game_sa/Pad.cpp | 19 ++-- source/toolsmenu/UIRenderer.cpp | 99 ++++------------ source/toolsmenu/UIRenderer.h | 11 +- 9 files changed, 119 insertions(+), 138 deletions(-) diff --git a/.gitmodules b/.gitmodules index 20ef394e63..fa589ef933 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,4 @@ [submodule "libs/imgui"] path = libs/imgui url = ../../ocornut/imgui.git + branch = docking diff --git a/libs/imgui b/libs/imgui index 17a7084b57..e25e4526cd 160000 --- a/libs/imgui +++ b/libs/imgui @@ -1 +1 @@ -Subproject commit 17a7084b57f9713ec6538881079d6c5a1b6b3598 +Subproject commit e25e4526cd41cd6536194de098540d54244f54e9 diff --git a/source/app/app_game.cpp b/source/app/app_game.cpp index fcea300b58..52978b8962 100644 --- a/source/app/app_game.cpp +++ b/source/app/app_game.cpp @@ -38,11 +38,11 @@ void AppGameInjectHooks() { RH_ScopedGlobalInstall(RenderEffects, 0x53E170); RH_ScopedGlobalInstall(RenderScene, 0x53DF40); RH_ScopedGlobalInstall(RenderMenus, 0x53E530); - RH_ScopedGlobalInstall(Render2dStuff, 0x53E230, {.locked = true}); + RH_ScopedGlobalInstall(Render2dStuff, 0x53E230); RH_ScopedGlobalInstall(RenderDebugShit, 0x53E160); RH_ScopedGlobalInstall(Idle, 0x53E920); - RH_ScopedGlobalInstall(FrontendIdle, 0x53E770); + RH_ScopedGlobalInstall(FrontendIdle, 0x53E770, { .locked = true }); // Must be hooked at all times otherwise imgui stops working! } // 0x5BF3B0 @@ -310,8 +310,11 @@ void Idle(void* param) { } if (!FrontEndMenuManager.m_bMenuActive && TheCamera.GetScreenFadeStatus() != eNameState::NAME_FADE_IN) { - CVector2D mousePos{SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f}; - RsMouseSetPos(&mousePos); + if (!notsa::ui::UIRenderer::GetSingleton().GetImIO()->NavActive) { // If imgui nav is active don't center the cursor + CVector2D mousePos{SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f}; + RsMouseSetPos(&mousePos); + } + CRenderer::ConstructRenderList(); CRenderer::PreRender(); CWorld::ProcessPedsAfterPreRender(); @@ -348,6 +351,7 @@ void Idle(void* param) { } RenderMenus(); + notsa::ui::UIRenderer::GetSingleton().DrawLoop(); // NOTSA: ImGui menu draw loop RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RWRSTATE(NULL)); DoFade(); CHud::DrawAfterFade(); diff --git a/source/app/platform/win/win.cpp b/source/app/platform/win/win.cpp index 06028ed7ee..bbed5c5804 100644 --- a/source/app/platform/win/win.cpp +++ b/source/app/platform/win/win.cpp @@ -4,6 +4,7 @@ #include #include "win.h" +#include "imgui_impl_win32.h" #include "dshow.h" #include "VideoPlayer.h" @@ -30,6 +31,9 @@ static LPSTR AppClassName = LPSTR(APP_CLASS); constexpr auto NO_FOREGROUND_PAUSE = true; +// Dear ImGui said I have to copy this here +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + // Custom enum, generated by ChatGPT, thank you enum class WinVer { WIN_95, //< Windows 95 @@ -244,6 +248,11 @@ BOOL GTATranslateShiftKey(RsKeyCodes*) { // The in keycode is ignored, so we won // 0x747EB0 LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + const auto imio = ImGui::GetCurrentContext() ? &ImGui::GetIO() : nullptr; + if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam)) { + return true; + } + switch (uMsg) { case WM_SETCURSOR: { ShowCursor(false); @@ -253,7 +262,11 @@ LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: - case WM_SYSKEYUP: { //< 0x74823B - wParam is a `VK_` (virtual key), lParam are the flags (See https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keyup) + case WM_SYSKEYUP: { //< 0x74823B - wParam is a `VK_` (virtual key), lParam are the flags (See https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keyup)] + if (imio && imio->WantCaptureKeyboard) { + return 0; + } + if (RsKeyCodes ck; GTATranslateKey(&ck, lParam, wParam)) { RsKeyboardEventHandler( (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) @@ -268,6 +281,54 @@ LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara } return 0; } + case WM_MOUSEMOVE: { //< 0x748323 + if (imio && imio->WantCaptureMouse) { + return 0; + } + FrontEndMenuManager.m_nMousePosWinX = GET_X_LPARAM(lParam); + FrontEndMenuManager.m_nMousePosWinY = GET_Y_LPARAM(lParam); + break; + } + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: { + if (imio && imio->WantCaptureMouse) { + return 0; + } + SetCapture(hWnd); + return 0; + } + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: { + if (imio && imio->WantCaptureMouse) { + return 0; + } + ReleaseCapture(); + return 0; + } + case WM_MOUSEWHEEL: + return 0; + case WM_SIZING: { // 0x74829E + if (RwInitialized) { + if (gGameState == GAME_STATE_IDLE) { + RsEventHandler(rsIDLE, (void*)TRUE); + } + } + + const auto wndrect = reinterpret_cast(lParam); + VERIFY(SetWindowPos( + hWnd, + HWND_TOP, + 0, 0, + wndrect->right - wndrect->left, wndrect->bottom - wndrect->top, + SWP_NOSIZE | SWP_NOMOVE + )); + + CPostEffects::SetupBackBufferVertex(); + + return 0; + } case WM_ACTIVATEAPP: { // 0x748087 const auto wndBeingActivated = !!wParam; @@ -317,7 +378,7 @@ LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara return 0; } - case WA_CLICKACTIVE: + case WM_DESTROY: case WM_CLOSE: { // 0x747EF3 VERIFY(ClipCursor(nullptr)); VERIFY(SUCCEEDED(WinInput::Shutdown())); @@ -447,46 +508,6 @@ LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara CTimer::SetCodePause(false); break; } - case WM_MOUSEFIRST: { //< 0x748323 - FrontEndMenuManager.m_nMousePosWinX = GET_X_LPARAM(lParam); - FrontEndMenuManager.m_nMousePosWinY = GET_Y_LPARAM(lParam); - return 0; - } - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_MBUTTONDOWN: { - SetCapture(hWnd); - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: { - ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: { - return 0; - } - case WM_SIZING: { // 0x74829E - if (RwInitialized) { - if (gGameState == GAME_STATE_IDLE) { - RsEventHandler(rsIDLE, (void*)TRUE); - } - } - - const auto wndrect = reinterpret_cast(lParam); - VERIFY(SetWindowPos( - hWnd, - HWND_TOP, - 0, 0, - wndrect->right - wndrect->left, wndrect->bottom - wndrect->top, - SWP_NOSIZE | SWP_NOMOVE - )); - - CPostEffects::SetupBackBufferVertex(); - - return 0; - } } return DefWindowProc(hWnd, uMsg, wParam, lParam); } @@ -822,7 +843,7 @@ void Win32InjectHooks() { RH_ScopedGlobalInstall(GTATranslateShiftKey, 0x747CD0); RH_ScopedGlobalInstall(GTATranslateKey, 0x747820); - RH_ScopedGlobalInstall(__MainWndProc, 0x747EB0); + RH_ScopedGlobalInstall(__MainWndProc, 0x747EB0, { .locked = true }); // Unhooking these 2 after the game has started will do nothing RH_ScopedGlobalInstall(__WinMain, 0x748710); diff --git a/source/game_sa/Frontend/MenuManager_Input.cpp b/source/game_sa/Frontend/MenuManager_Input.cpp index fbe22d143b..92237273da 100644 --- a/source/game_sa/Frontend/MenuManager_Input.cpp +++ b/source/game_sa/Frontend/MenuManager_Input.cpp @@ -285,7 +285,11 @@ void CMenuManager::CheckForMenuClosing() { if (IsVideoModeExclusive()) { DIReleaseMouse(); +#ifdef FIX_BUGS // Causes the retarded fucktard code to not dispatch mouse input to WndProc => ImGUI mouse not working. Amazing piece of technology. + InitialiseMouse(false); +#else InitialiseMouse(true); +#endif // !FIX_BUGS } m_fStatsScrollSpeed = 150.0f; diff --git a/source/game_sa/MenuManager.cpp b/source/game_sa/MenuManager.cpp index 0b8ecca832..f8567da840 100644 --- a/source/game_sa/MenuManager.cpp +++ b/source/game_sa/MenuManager.cpp @@ -67,7 +67,7 @@ void CMenuManager::InjectHooks() { RH_ScopedInstall(CheckFrontEndDownInput, 0x5738B0); RH_ScopedInstall(CheckFrontEndLeftInput, 0x573920); RH_ScopedInstall(CheckFrontEndRightInput, 0x573990); - RH_ScopedInstall(CheckForMenuClosing, 0x576B70); + RH_ScopedInstall(CheckForMenuClosing, 0x576B70, { .locked = true }); // Must be hooked at all times otherwise imgui stops working! [The input at least does] RH_ScopedInstall(CheckHover, 0x57C4F0); RH_ScopedInstall(CheckMissionPackValidMenu, 0x57D720); RH_ScopedInstall(CheckCodesForControls, 0x57DB20, { .reversed = false }); diff --git a/source/game_sa/Pad.cpp b/source/game_sa/Pad.cpp index 16de590488..bbba7105b6 100644 --- a/source/game_sa/Pad.cpp +++ b/source/game_sa/Pad.cpp @@ -7,7 +7,7 @@ #include "StdInc.h" #include "Pad.h" - +#include "platform/win/Input.h" #include "UIRenderer.h" #include "ControllerConfigManager.h" #include "app.h" @@ -212,11 +212,16 @@ void CPad::Update(int32 pad) { // 0x541DD0 void CPad::UpdatePads() { - GetPad(0)->UpdateMouse(); - ProcessPad(false); + const auto& ImIONavActive = notsa::ui::UIRenderer::GetSingleton().GetImIO()->NavActive; + + if (!ImIONavActive) { + GetPad(0)->UpdateMouse(); + } + ProcessPad(false); ControlsManager.ClearSimButtonPressCheckers(); - if (!notsa::ui::UIRenderer::GetSingleton().Visible()) { // NOTSA: Don't handle updates if the menu is open, so we don't affect gameplay inputting text + + if (!ImIONavActive) { ControlsManager.AffectPadFromKeyBoard(); ControlsManager.AffectPadFromMouse(); GetPad(0)->Update(0); @@ -225,8 +230,6 @@ void CPad::UpdatePads() { OldKeyState = NewKeyState; NewKeyState = TempKeyState; - - notsa::ui::UIRenderer::GetSingleton().UpdateInput(); } // 0x53F3C0 @@ -1190,10 +1193,10 @@ int GetCurrentKeyPressed(RsKeyCodes& keys) { return plugin::CallAndReturn(keys); } -IDirectInputDevice8* DIReleaseMouse() { +IDirectInputDevice8* DIReleaseMouse() { // todo: wininput return plugin::CallAndReturn(); } void InitialiseMouse(bool exclusive) { - plugin::Call<0x7469A0, bool>(exclusive); + WinInput::InitialiseMouse(exclusive); } diff --git a/source/toolsmenu/UIRenderer.cpp b/source/toolsmenu/UIRenderer.cpp index da9ed372ab..bce0999ae2 100644 --- a/source/toolsmenu/UIRenderer.cpp +++ b/source/toolsmenu/UIRenderer.cpp @@ -25,12 +25,9 @@ UIRenderer::UIRenderer() : { IMGUI_CHECKVERSION(); - m_ImIO->WantCaptureMouse = true; - m_ImIO->WantCaptureKeyboard = true; - m_ImIO->WantSetMousePos = true; - m_ImIO->MouseDrawCursor = false; - m_ImIO->ConfigFlags = ImGuiConfigFlags_NavEnableSetMousePos | ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad; - m_ImIO->DisplaySize = ImVec2(SCREEN_WIDTH, SCREEN_HEIGHT); + m_ImIO->ConfigFlags = ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable; + m_ImIO->DisplaySize = ImVec2(SCREEN_WIDTH, SCREEN_HEIGHT); + m_ImIO->NavActive = false; ImGui_ImplWin32_Init(PSGLOBAL(window)); ImGui_ImplDX9_Init(GetD3DDevice()); @@ -46,73 +43,6 @@ UIRenderer::~UIRenderer() { DEV_LOG("Good bye!"); } -void UIRenderer::UpdateInput() { - if (!Visible()) { - return; - } - - // Update mouse - { - const auto WHEEL_SPEED = 20.0f; - - CPad::GetPad()->DisablePlayerControls = true; - - // Update position - auto& MousePos = m_ImIO->MousePos; - MousePos.x += CPad::NewMouseControllerState.X; - MousePos.y -= CPad::NewMouseControllerState.Y; - - MousePos.x = std::clamp(MousePos.x, 0.0f, SCREEN_WIDTH); - MousePos.y = std::clamp(MousePos.y, 0.0f, SCREEN_HEIGHT); - - if (CPad::NewMouseControllerState.wheelDown) - m_ImIO->MouseWheel -= (WHEEL_SPEED * m_ImIO->DeltaTime); - - if (CPad::NewMouseControllerState.wheelUp) - m_ImIO->MouseWheel += (WHEEL_SPEED * m_ImIO->DeltaTime); - - m_ImIO->MouseDown[ImGuiMouseButton_Left] = CPad::NewMouseControllerState.lmb; - m_ImIO->MouseDown[ImGuiMouseButton_Right] = CPad::NewMouseControllerState.rmb; - m_ImIO->MouseDown[ImGuiMouseButton_Middle] = CPad::NewMouseControllerState.mmb; - - CPad::NewMouseControllerState.X = 0.0f; - CPad::NewMouseControllerState.Y = 0.0f; - } - - // Update keyboard - { - BYTE KeyStates[256]; - - VERIFY(GetKeyboardState(KeyStates)); - - const auto IsKeyDown = [&](auto key) { return (KeyStates[key] & 0x80) != 0; }; - - for (auto key = 0; key < 256; key++) { - // Check if there was a state change - if (IsKeyDown(key) == m_ImIO->KeysDown[key]) { - continue; - } - - // There was! - if (IsKeyDown(key)) { // Key is now down - m_ImIO->KeysDown[key] = true; - - char ResultUTF8[16] = {0}; - if (ToAscii(key, MapVirtualKey(key, 0), KeyStates, (LPWORD)ResultUTF8, 0)) { - m_ImIO->AddInputCharactersUTF8(ResultUTF8); - } - } else { // Key is now released - m_ImIO->KeysDown[key] = false; - } - } - - m_ImIO->KeyCtrl = IsKeyDown(VK_CONTROL); - m_ImIO->KeyShift = IsKeyDown(VK_SHIFT); - m_ImIO->KeyAlt = IsKeyDown(VK_MENU); - m_ImIO->KeySuper = false; - } -} - void UIRenderer::PreRenderUpdate() { m_ImIO->DeltaTime = CTimer::GetTimeStepInSeconds(); m_ImIO->DisplaySize = ImVec2(SCREEN_WIDTH, SCREEN_HEIGHT); // Update display size, in case of window resize after imgui was already initialized @@ -122,12 +52,17 @@ void UIRenderer::PreRenderUpdate() { ReversibleHooks::CheckAll(); if (const auto pad = CPad::GetPad(); pad->DebugMenuJustPressed()) { - m_ShowMenu = !m_ShowMenu; - m_ImIO->MouseDrawCursor = m_ShowMenu; - pad->bPlayerSafe = m_ShowMenu; + m_ShowMenu = !m_ShowMenu; + m_ImIO->MouseDrawCursor = m_ShowMenu; + m_ImIO->NavActive = m_ShowMenu; + pad->DisablePlayerControls = m_ShowMenu; } } +void UIRenderer::PostRenderUpdate() { + m_ImIO->NavActive = m_ShowMenu; // ImGUI clears `NavActive` every frame, so have to set it here. +} + void UIRenderer::DrawLoop() { if (m_ReInitRequested) { ResetSingleton(); // This will destruct the current object so we gotta stop here. @@ -135,7 +70,7 @@ void UIRenderer::DrawLoop() { } PreRenderUpdate(); - + ImGui_ImplWin32_NewFrame(); ImGui_ImplDX9_NewFrame(); ImGui::NewFrame(); @@ -144,7 +79,15 @@ void UIRenderer::DrawLoop() { ImGui::EndFrame(); ImGui::Render(); ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); - ImGui_ImplDX9_InvalidateDeviceObjects(); + //ImGui_ImplDX9_InvalidateDeviceObjects(); + + PostRenderUpdate(); + + // Update and Render additional Platform Windows + if (m_ImIO->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } } void UIRenderer::Render2D() { diff --git a/source/toolsmenu/UIRenderer.h b/source/toolsmenu/UIRenderer.h index 7e1d35cc4d..c28797ebd7 100644 --- a/source/toolsmenu/UIRenderer.h +++ b/source/toolsmenu/UIRenderer.h @@ -15,6 +15,9 @@ class UIRenderer : public notsa::Singleton { //! Request restart of render (done on before frame) void RequestReInit() { m_ReInitRequested = true; } + //! Same as ImGui::GetIO(), but won't crash the code if called before ctx is created + auto GetImIO() const { return m_ImIO; } + private: //! Render 3D stuff in the world (If rendered elsewhere it won't be visible) void Render3D(); @@ -22,22 +25,24 @@ class UIRenderer : public notsa::Singleton { //! Render 2D stuff (Called after a new (ImGui) frame has been began) void Render2D(); - //! Update input (mouse and keyboard) if necessary - void UpdateInput(); - //! Called before a new (ImGui) frame is started (and after the previous one has ended) void PreRenderUpdate(); + //! Called after the frame has ended + void PostRenderUpdate(); + //! The actual draw loop void DrawLoop(); //! Random code you want to run (Called from `PreRenderUpdate`) void DebugCode(); + private: friend void ::RenderEffects(); // For `Render3D()` friend void ::FrontendIdle(); // For `DrawLoop()` VVV friend void ::Idle(void*); // For `DrawLoop()` Yes, called in 2 places, but they are never called in the same frame (As `Idle` is called when not in the menu only) friend void ::CPad::UpdatePads(); + friend void ::Idle(void*); private: bool m_Initialised{}; From 490f772ac4cd28e8c050b8e2ce0bc88e7b95ff52 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Tue, 11 Jul 2023 13:02:58 +0200 Subject: [PATCH 18/19] Fix ImGui (#578) --- source/game_sa/ShadowCamera.cpp | 16 ++++------------ source/toolsmenu/DebugModules/DebugModule.h | 1 + source/toolsmenu/UIRenderer.cpp | 17 +++++++++++------ source/toolsmenu/UIRenderer.h | 4 ++-- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/source/game_sa/ShadowCamera.cpp b/source/game_sa/ShadowCamera.cpp index 9046a4a381..332003bc82 100644 --- a/source/game_sa/ShadowCamera.cpp +++ b/source/game_sa/ShadowCamera.cpp @@ -211,19 +211,11 @@ RwCamera* CShadowCamera::Create(int32 rasterSizePower) { RpAtomic* atomicQuickRender(RpAtomic* atomic, void* data) { UNUSED(data); - // Save original callback - const auto origcb = RpAtomicGetRenderCallBack(atomic); + const auto original = RpAtomicGetRenderCallBack(atomic); - // Set default callback - RpAtomicSetRenderCallBack(atomic, &AtomicDefaultRenderCallBack); - - // NOTSA: Omitting useless `if` - It's condition always evals to `false` - Perhaps BUG? - - // Render using default - AtomicDefaultRenderCallBack(atomic); - - // Set original (if not null) or use default - RpAtomicSetRenderCallBack(atomic, origcb ? origcb : &AtomicDefaultRenderCallBack); + RpAtomicSetRenderCallBack(atomic, NULL); + RpAtomicRender(atomic); + RpAtomicSetRenderCallBack(atomic, original); return atomic; } diff --git a/source/toolsmenu/DebugModules/DebugModule.h b/source/toolsmenu/DebugModules/DebugModule.h index 485b960688..3357e67e1f 100644 --- a/source/toolsmenu/DebugModules/DebugModule.h +++ b/source/toolsmenu/DebugModules/DebugModule.h @@ -1,5 +1,6 @@ #pragma once +// NOTE: Ideally we'd use imconfig.h, but it's too finnicky #define IM_VEC2_CLASS_EXTRA \ operator CVector2D() const { return {x, y}; } \ ImVec2(const CVector2D& v) : x{v.x}, y{v.y} {} \ diff --git a/source/toolsmenu/UIRenderer.cpp b/source/toolsmenu/UIRenderer.cpp index bce0999ae2..e4bbfea39a 100644 --- a/source/toolsmenu/UIRenderer.cpp +++ b/source/toolsmenu/UIRenderer.cpp @@ -51,16 +51,21 @@ void UIRenderer::PreRenderUpdate() { DebugCode(); ReversibleHooks::CheckAll(); - if (const auto pad = CPad::GetPad(); pad->DebugMenuJustPressed()) { - m_ShowMenu = !m_ShowMenu; - m_ImIO->MouseDrawCursor = m_ShowMenu; - m_ImIO->NavActive = m_ShowMenu; - pad->DisablePlayerControls = m_ShowMenu; + // A delay of a frame has to be added, otherwise the release of F7 wont be processed + // and the menu will close + const auto Shortcut = [](ImGuiKeyChord chord) { + return ImGui::Shortcut(chord, ImGuiKeyOwner_Any, ImGuiInputFlags_RouteAlways); + }; + if (Shortcut(ImGuiKey_F7) || Shortcut(ImGuiKey_M | ImGuiMod_Ctrl)) { + m_InputActive = !m_InputActive; + m_ImIO->MouseDrawCursor = m_InputActive; + m_ImIO->NavActive = m_InputActive; + CPad::GetPad()->DisablePlayerControls = m_InputActive; } } void UIRenderer::PostRenderUpdate() { - m_ImIO->NavActive = m_ShowMenu; // ImGUI clears `NavActive` every frame, so have to set it here. + m_ImIO->NavActive = m_InputActive; // ImGUI clears `NavActive` every frame, so have to set it here. } void UIRenderer::DrawLoop() { diff --git a/source/toolsmenu/UIRenderer.h b/source/toolsmenu/UIRenderer.h index c28797ebd7..358b257f23 100644 --- a/source/toolsmenu/UIRenderer.h +++ b/source/toolsmenu/UIRenderer.h @@ -10,7 +10,7 @@ class UIRenderer : public notsa::Singleton { UIRenderer(); ~UIRenderer(); - bool Visible() { return m_ShowMenu; } + bool Visible() { return m_InputActive; } //! Request restart of render (done on before frame) void RequestReInit() { m_ReInitRequested = true; } @@ -46,7 +46,7 @@ class UIRenderer : public notsa::Singleton { private: bool m_Initialised{}; - bool m_ShowMenu{}; + bool m_InputActive{}; bool m_ReInitRequested{}; ImGuiContext* m_ImCtx{}; ImGuiIO* m_ImIO{}; From 163ddc6ab22181004afd57e017618d9e3953a734 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Tue, 11 Jul 2023 16:18:38 +0200 Subject: [PATCH 19/19] More Windows Platform Specific stuff (#544) --- libs/imgui | 2 +- source/CMakeLists.txt | 6 + source/InjectHooksMain.cpp | 3 +- source/app/app.cpp | 2 +- source/app/app_camera.cpp | 3 +- source/app/app_input.cpp | 153 +++- source/app/platform/platform.cpp | 14 +- source/app/platform/platform.h | 6 +- source/app/platform/win/Input.cpp | 124 ++- source/app/platform/win/Input.h | 2 +- source/app/platform/win/Platform.cpp | 33 + source/app/platform/win/Platform.h | 48 + source/app/platform/win/VideoMode.cpp | 9 +- source/app/platform/win/VideoMode.h | 43 +- .../platform/win/VideoModeSelectDialog.cpp | 178 ++++ .../app/platform/win/VideoModeSelectDialog.h | 5 + source/app/platform/win/WinMain.cpp | 422 +++++++++ source/app/platform/win/WinMain.h | 3 + source/app/platform/win/WinPs.cpp | 587 ++++++++++++ source/app/platform/win/WndProc.cpp | 408 +++++++++ source/app/platform/win/WndProc.h | 6 + source/app/platform/win/win.cpp | 854 ------------------ source/app/platform/win/win.h | 25 - source/app/platform/win/win_impl.cpp | 233 ----- source/game_sa/ControllerConfigManager.cpp | 44 +- source/game_sa/ControllerConfigManager.h | 22 +- source/game_sa/CutsceneMgr.cpp | 4 +- source/game_sa/LoadingScreen.h | 1 - source/game_sa/MenuManager.h | 4 +- source/game_sa/RenderWare/rw/rwplcore.cpp | 4 +- source/game_sa/RenderWare/rw/rwplcore.h | 2 +- source/premake5.lua | 8 +- 32 files changed, 2079 insertions(+), 1179 deletions(-) create mode 100644 source/app/platform/win/Platform.cpp create mode 100644 source/app/platform/win/Platform.h create mode 100644 source/app/platform/win/VideoModeSelectDialog.cpp create mode 100644 source/app/platform/win/VideoModeSelectDialog.h create mode 100644 source/app/platform/win/WinMain.cpp create mode 100644 source/app/platform/win/WinMain.h create mode 100644 source/app/platform/win/WinPs.cpp create mode 100644 source/app/platform/win/WndProc.cpp create mode 100644 source/app/platform/win/WndProc.h delete mode 100644 source/app/platform/win/win.cpp delete mode 100644 source/app/platform/win/win.h delete mode 100644 source/app/platform/win/win_impl.cpp diff --git a/libs/imgui b/libs/imgui index e25e4526cd..8566fec661 160000 --- a/libs/imgui +++ b/libs/imgui @@ -1 +1 @@ -Subproject commit e25e4526cd41cd6536194de098540d54244f54e9 +Subproject commit 8566fec661801a026e56f06cd53f5dac25c2595b diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 805fa910b0..4b3e16d182 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -63,6 +63,12 @@ target_link_libraries(${RE_PROJECT_LIB_NAME} PRIVATE ogg vorbisfile imgui + ddraw + Winmm + dxguid + strmiids + dsound + d3d9 ) target_include_directories(${RE_PROJECT_LIB_NAME} PRIVATE / diff --git a/source/InjectHooksMain.cpp b/source/InjectHooksMain.cpp index ceaaf310fa..156716b4e9 100644 --- a/source/InjectHooksMain.cpp +++ b/source/InjectHooksMain.cpp @@ -398,8 +398,7 @@ #include "platform/win/VideoPlayer/VideoPlayer.h" #include "platform/win/VideoMode.h" -#include "platform/win/win.h" -#include "platform/platform.h" +#include "platform/win/Platform.h" #include "app/app.h" #include diff --git a/source/app/app.cpp b/source/app/app.cpp index 4fa6c8f797..f6e51724b1 100644 --- a/source/app/app.cpp +++ b/source/app/app.cpp @@ -10,7 +10,7 @@ #include "Plugins/BreakablePlugin/BreakablePlugin.h" #include "Pipelines/CustomBuilding/CustomBuildingRenderer.h" -#include "win/win.h" +#include "platform/win/Platform.h" void AppInjectHooks() { RH_ScopedCategory("App"); diff --git a/source/app/app_camera.cpp b/source/app/app_camera.cpp index 192d083de5..f0aed69a30 100644 --- a/source/app/app_camera.cpp +++ b/source/app/app_camera.cpp @@ -74,8 +74,7 @@ void CameraSize(RwCamera* camera, RwRect* rect, RwReal viewWindow, RwReal aspect rect->x = rect->y = 0; } - RwVideoMode videoMode; - RwEngineGetVideoModeInfo(&videoMode, RwEngineGetCurrentVideoMode()); + const auto videoMode = RwEngineGetVideoModeInfo(RwEngineGetCurrentVideoMode()); if (videoMode.flags & rwVIDEOMODEEXCLUSIVE) { rect->x = rect->y = 0; diff --git a/source/app/app_input.cpp b/source/app/app_input.cpp index f97bba891a..4d1c45ca1b 100644 --- a/source/app/app_input.cpp +++ b/source/app/app_input.cpp @@ -2,6 +2,7 @@ #include "app_input.h" #include "platform.h" +#include "ControllerConfigManager.h" void AppInputInjectHooks() { RH_ScopedCategory("App"); @@ -9,12 +10,15 @@ void AppInputInjectHooks() { RH_ScopedGlobalInstall(AttachInputDevices, 0x744A20); - RH_ScopedGlobalInstall(HandleKeyDown, 0x743DF0, {.reversed = false}); - RH_ScopedGlobalInstall(HandleKeyUp, 0x7443C0, {.reversed = false}); - RH_ScopedGlobalInstall(KeyboardHandler, 0x744880, {.reversed = false}); - RH_ScopedGlobalInstall(HandlePadButtonDown, 0x7448B0, {.reversed = false}); - RH_ScopedGlobalInstall(HandlePadButtonUp, 0x744930, {.reversed = false}); - RH_ScopedGlobalInstall(PadHandler, 0x7449F0, {.reversed = false}); + RH_ScopedGlobalInstall(HandleKeyDown, 0x743DF0); + RH_ScopedGlobalInstall(HandleKeyUp, 0x7443C0); + RH_ScopedGlobalInstall(KeyboardHandler, 0x744880); + + // Can't hook these, because argument is passed in eax (And I don't feel like dealing with that) + //RH_ScopedGlobalInstall(HandlePadButtonDown, 0x7448B0); + //RH_ScopedGlobalInstall(HandlePadButtonUp, 0x744930); + + RH_ScopedGlobalInstall(PadHandler, 0x7449F0); } // 0x744A20 @@ -24,22 +28,141 @@ bool AttachInputDevices() { return true; } +// Combined code from `0x743DF0` and `0x7443C0` +RsEventStatus HandleKeyEvent(bool isDown, RsKeyStatus* ks) { + // First handle keyboard keys + const auto pKBKeyState = [ks]() -> int16* { + auto& tks = CPad::TempKeyState; + switch (ks->keyScanCode) { + case rsNULL: return nullptr; + case rsF1: + case rsF2: + case rsF3: + case rsF4: + case rsF5: + case rsF6: + case rsF7: + case rsF8: + case rsF9: + case rsF10: + case rsF11: + case rsF12: return &tks.FKeys[ks->keyScanCode - rsF1]; + case rsESC: return &tks.esc; + case rsINS: return &tks.insert; + case rsDEL: return &tks.del; + case rsHOME: return &tks.home; + case rsEND: return &tks.end; + case rsPGUP: return &tks.pgup; + case rsPGDN: return &tks.pgdn; + case rsUP: return &tks.up; + case rsDOWN: return &tks.down; + case rsLEFT: return &tks.left; + case rsRIGHT: return &tks.right; + case rsDIVIDE: return &tks.div; + case rsTIMES: return &tks.mul; + case rsPLUS: return &tks.add; + case rsMINUS: return &tks.sub; + case rsPADDEL: return &tks.decimal; + case rsPADEND: return &tks.num1; + case rsPADDOWN: return &tks.num2; + case rsPADPGDN: return &tks.num3; + case rsPADLEFT: return &tks.num4; + case rsPAD5: return &tks.num5; + case rsNUMLOCK: return &tks.numlock; + case rsPADRIGHT: return &tks.num6; + case rsPADHOME: return &tks.num7; + case rsPADUP: return &tks.num8; + case rsPADPGUP: return &tks.num9; + case rsPADINS: return &tks.num0; + case rsPADENTER: return &tks.enter; + case rsSCROLL: return &tks.scroll; + case rsPAUSE: return &tks.pause; + case rsBACKSP: return &tks.back; + case rsTAB: return &tks.tab; + case rsCAPSLK: return &tks.capslock; + case rsENTER: return &tks.extenter; + case rsLSHIFT: return &tks.lshift; + case rsRSHIFT: return &tks.rshift; + case rsSHIFT: return &tks.shift; + case rsLCTRL: return &tks.lctrl; + case rsRCTRL: return &tks.rctrl; + case rsLALT: return &tks.lmenu; + case rsRALT: return &tks.rmenu; + case rsLWIN: return &tks.lwin; + case rsRWIN: return &tks.rwin; + case rsAPPS: return &tks.apps; + default: return (ks->keyScanCode < 255) ? &tks.standardKeys[ks->keyScanCode] : nullptr; + } + }(); + if (pKBKeyState) { + *pKBKeyState = isDown ? 255 : 0; + } + + // Then if a pad is plugged in, possibly handle that as well + if (CPad::padNumber) { + const auto [pPadBtnState, downValue] = [&]() -> std::pair { + auto& pctks = CPad::GetPad(1)->PCTempKeyState; // Why pad 1? + switch (ks->keyScanCode) { // TODO: Enums + case 68: return { &pctks.LeftStickX, 128 }; + case 65: return { &pctks.LeftStickX, -128 }; + + case 87: return { &pctks.LeftStickY, 128 }; + case 83: return { &pctks.LeftStickY, -128 }; + + case 74: return { &pctks.RightStickX, 128 }; + case 71: return { &pctks.RightStickX, -128 }; + + case 89: return { &pctks.RightStickY, 128 }; + case 72: return { &pctks.RightStickY, -128 }; + + case 90: return { &pctks.LeftShoulder1, 255 }; + case 88: return { &pctks.LeftShoulder2, 255 }; + + case 67: return { &pctks.RightShoulder1, 255 }; + case 86: return { &pctks.RightShoulder2, 255 }; + + case 79: return { &pctks.DPadUp, 255 }; + case 76: return { &pctks.DPadDown, 255 }; + case 75: return { &pctks.DPadLeft, 255 }; + case 59: return { &pctks.DPadRight, 255 }; + + case 66: return { &pctks.Start, 255 }; + case 78: return { &pctks.Select, 255 }; + + case 77: return { &pctks.ButtonSquare, 255 }; + case 44: return { &pctks.ButtonTriangle, 255 }; + case 46: return { &pctks.ButtonCross, 255 }; + case 47: return { &pctks.ButtonCircle, 255 }; + + case 1047: return { &pctks.ShockButtonL, 255 }; + case 1050: return { &pctks.ShockButtonR, 255 }; + } + return { NULL, NULL }; + }(); + if (pPadBtnState) { + *pPadBtnState = isDown ? downValue : 0; + } + } + + return rsEVENTPROCESSED; +} + // 0x743DF0 -RsEventStatus HandleKeyDown(RsKeyStatus* param) { - return plugin::CallAndReturn(param); +RsEventStatus HandleKeyDown(RsKeyStatus* ks) { + return HandleKeyEvent(true, ks); } // 0x7443C0 -RsEventStatus HandleKeyUp(RsKeyStatus* param) { - return plugin::CallAndReturn(param); +RsEventStatus HandleKeyUp(RsKeyStatus* ks) { + return HandleKeyEvent(false, ks); } // 0x744880 RsEventStatus KeyboardHandler(RsEvent event, void* param) { switch (event) { - case rsPADBUTTONDOWN: + case rsKEYDOWN: return HandleKeyDown((RsKeyStatus*)param); - case rsPADBUTTONUP: + case rsKEYUP: return HandleKeyUp((RsKeyStatus*)param); default: return rsEVENTNOTPROCESSED; @@ -48,12 +171,14 @@ RsEventStatus KeyboardHandler(RsEvent event, void* param) { // 0x7448B0 RsEventStatus HandlePadButtonDown(RsKeyStatus* param) { - return plugin::CallAndReturn(param); + ControlsManager.HandleJoyButtonUpDown(CPad::padNumber ? 1 : 0, true); + return rsEVENTPROCESSED; } // 0x744930 RsEventStatus HandlePadButtonUp(RsKeyStatus* param) { - return plugin::CallAndReturn(param); + ControlsManager.HandleJoyButtonUpDown(CPad::padNumber ? 1 : 0, false); + return rsEVENTPROCESSED; } // 0x7449F0 diff --git a/source/app/platform/platform.cpp b/source/app/platform/platform.cpp index 65c7e0ff2c..1fbd94fdc9 100644 --- a/source/app/platform/platform.cpp +++ b/source/app/platform/platform.cpp @@ -131,7 +131,7 @@ bool rsCommandLine(void* param) { // 0x619530 bool rsPreInitCommandLine(RwChar* arg) { if (strcmp(arg, RWSTRING("-vms")) == 0) { - DefaultVideoMode = FALSE; + DefaultVM = FALSE; return true; } return false; @@ -245,7 +245,7 @@ RwMemoryFunctions* psGetMemoryFunctions() { } // 0x619C90 -bool RsRwInitialize(void* param) { +bool RsRwInitialize(void* param) { // Win32: Param is HWND if (!RwEngineInit(psGetMemoryFunctions(), 0, rsRESOURCESDEFAULTARENASIZE)) return false; @@ -304,7 +304,7 @@ RsEventStatus RsEventHandler(RsEvent event, void* param) { case rsREGISTERIMAGELOADER: return rsEVENTPROCESSED; - case rsRWINITIALIZE: + case rsRWINITIALIZE: // Win32: Param is HWND return RSEVENT_SUCCEED(RsRwInitialize(param)); case rsRWTERMINATE: @@ -331,10 +331,12 @@ RsEventStatus RsEventHandler(RsEvent event, void* param) { return rsEVENTNOTPROCESSED; } -float IsWideScreenRatio(float ratio) { +// Returns true if ratio is 5:3, 16:9 or 16:10. +bool IsWideScreenRatio(float ratio) { return ratio == 0.6f || ratio == 10.0f / 16.0f || ratio == 9.0f / 16.0f; } -float IsFullScreenRatio(float ratio) { - return ratio == 3.0f / 4.0f || ratio == 0.8f; +// Returns true if ratio is 4:3 or 5:4. +bool IsFullScreenRatio(float ratio) { + return ratio == 3.0f / 4.0f || ratio == 4.0f / 5.0f; } diff --git a/source/app/platform/platform.h b/source/app/platform/platform.h index 61606cb4b7..7465bbdca3 100644 --- a/source/app/platform/platform.h +++ b/source/app/platform/platform.h @@ -1,7 +1,7 @@ #pragma once -float IsWideScreenRatio(float ratio); -float IsFullScreenRatio(float ratio); +bool IsWideScreenRatio(float ratio); +bool IsFullScreenRatio(float ratio); #define IS_WIDESCREEN_RATIO(ratio) IsWideScreenRatio(ratio) #define IS_FULLSCREEN_RATIO(ratio) IsFullScreenRatio(ratio) @@ -56,7 +56,7 @@ RwCamera* RsCameraShowRaster(RwCamera* camera); /** * Platform Specific */ -bool psInitialize(); +RwBool psInitialize(); void psTerminate(); void psWindowSetText(const char* str); diff --git a/source/app/platform/win/Input.cpp b/source/app/platform/win/Input.cpp index 3b221a8814..b2ce2c8c51 100644 --- a/source/app/platform/win/Input.cpp +++ b/source/app/platform/win/Input.cpp @@ -2,22 +2,12 @@ #include "Input.h" #include "ControllerConfigManager.h" -#include "win.h" +#include "Platform.h" #include "VideoMode.h" #pragma comment(lib, "dinput8.lib") #pragma comment(lib, "dxguid.lib") namespace WinInput { -void InjectHooks() { - RH_ScopedCategory("Win"); - RH_ScopedNamespaceName("Input"); - - //RH_ScopedGlobalInstall(Initialise, 0x7487CF, { .reversed = false }); - //RH_ScopedGlobalInstall(InitialiseMouse, 0x7469A0, { .reversed = false }); // Can't be hooked because it fails with ACCESS DENIED and crashes - //RH_ScopedGlobalInstall(InitialiseJoys, 0x7485C0, {.reversed = false}); - RH_ScopedGlobalInstall(EnumDevicesCallback, 0x747020); -} - // 0x746990 HRESULT CreateInput() { if (PSGLOBAL(diInterface)) { @@ -41,7 +31,7 @@ bool Initialise() { } InitialiseMouse(false); - InitialiseJoys(); + diPadInit(); return true; } @@ -65,9 +55,104 @@ void InitialiseMouse(bool exclusive) { WIN_FCHECK(PSGLOBAL(diMouse)->Acquire()); } +// 0x746D80 +HRESULT diPadSetRanges(LPDIRECTINPUTDEVICE8 dev, DWORD padNum) { + if (dev == NULL) { + return S_OK; // Weird but okay + } + + enum { + NO_PROPERTY, // Device doesn't have this property + PROP_READ_ONLY, // Deivce does have this property, but it's read-only + SUCCESS // Device has property and we've set it successfully + }; + + const auto SetPropery = [&](DWORD prop) { + // Set ranges + DIDEVICEOBJECTINSTANCEA objinfo{ + sizeof(DIDEVICEOBJECTINSTANCEA) + }; + DIPROPRANGE range{ + .diph = DIPROPHEADER{ + .dwSize = sizeof(DIPROPRANGE), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwObj = prop, + .dwHow = DIPH_BYOFFSET, + }, + .lMin = -2000, + .lMax = 2000, + }; + if (FAILED(dev->GetObjectInfo(&objinfo, prop, DIPH_BYOFFSET))) { + return NO_PROPERTY; + } + if (FAILED(dev->SetProperty(DIPROP_RANGE, &range.diph))) { + return PROP_READ_ONLY; + } + return SUCCESS; + }; + + if (SetPropery(DIJOFS_X) == PROP_READ_ONLY) { + return S_FALSE; + } + + if (SetPropery(DIJOFS_Y) == PROP_READ_ONLY) { + return S_FALSE; + } + + const auto SetProperyAndSetFlag = [&](DWORD prop, bool& flag) { + const auto res = SetPropery(DIJOFS_Z); + if (res != NO_PROPERTY) { + flag = true; + } + return res; + }; + + if (SetProperyAndSetFlag(DIJOFS_Z, PadConfigs[padNum].zAxisPresent) == PROP_READ_ONLY) { + return S_FALSE; + } + + if (SetProperyAndSetFlag(DIJOFS_RZ, PadConfigs[padNum].rzAxisPresent) == PROP_READ_ONLY) { + return S_FALSE; + } + + return S_OK; +} + +//! [NOTSA - From 0x7485C0] - Set device config product/vendor id +void diPadSetPIDVID(LPDIRECTINPUTDEVICE8 dev, DWORD padNum) { + DIPROPDWORD vidpid{ + .diph = DIPROPHEADER{ + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwObj = NULL, + .dwHow = DIPH_DEVICE, + } + }; + WIN_FCHECK(dev->GetProperty(DIPROP_VIDPID, &vidpid.diph)); + auto& cfg = PadConfigs[padNum]; + cfg.vendorId = LOWORD(vidpid.dwData); + cfg.vendorId = HIWORD(vidpid.dwData); +} + // 0x7485C0 -void InitialiseJoys() { - plugin::Call<0x7485C0>(); +void diPadInit() { + rng::fill(PadConfigs, CPadConfig{}); + + // Initialize devices (+ Set PSGLOBAL(diDeviceX) vars) + WIN_FCHECK(PSGLOBAL(diInterface)->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumDevicesCallback, NULL, DIEDFL_ALLDEVICES)); + + // Pirulax: Original code queried the capabilities [for pad 0] too, but did nothing with it, so I'll skip that. + + const auto InitializePad = [](LPDIRECTINPUTDEVICE8 dev, DWORD padNum) { + if (dev == NULL) { + return; + } + WIN_FCHECK(diPadSetRanges(dev, padNum)); + diPadSetPIDVID(dev, padNum); + PadConfigs[padNum].present = true; + }; + InitializePad(PSGLOBAL(diDevice1), 0); + InitializePad(PSGLOBAL(diDevice2), 1); } // 0x747020 @@ -140,4 +225,15 @@ CMouseControllerState GetMouseState() { return state; } +void InjectHooks() { + RH_ScopedCategory("Win"); + RH_ScopedNamespaceName("Input"); + + //RH_ScopedGlobalInstall(Initialise, 0x7487CF, { .reversed = false }); + //RH_ScopedGlobalInstall(InitialiseMouse, 0x7469A0, { .reversed = false }); // Can't be hooked because it fails with ACCESS DENIED and crashes + //RH_ScopedGlobalInstall(InitialiseJoys, 0x7485C0, {.reversed = false}); + RH_ScopedGlobalInstall(EnumDevicesCallback, 0x747020); + RH_ScopedGlobalInstall(diPadInit, 0x7485C0); + RH_ScopedGlobalInstall(diPadSetRanges, 0x746D80); +} } // namespace WinInput diff --git a/source/app/platform/win/Input.h b/source/app/platform/win/Input.h index 594441e223..0847202ce6 100644 --- a/source/app/platform/win/Input.h +++ b/source/app/platform/win/Input.h @@ -9,7 +9,7 @@ void InjectHooks(); bool Initialise(); HRESULT Shutdown(); void InitialiseMouse(bool exclusive); -void InitialiseJoys(); +void diPadInit(); BOOL CALLBACK EnumDevicesCallback(LPCDIDEVICEINSTANCEA pInst, LPVOID); CMouseControllerState GetMouseState(); diff --git a/source/app/platform/win/Platform.cpp b/source/app/platform/win/Platform.cpp new file mode 100644 index 0000000000..6615e6f8a9 --- /dev/null +++ b/source/app/platform/win/Platform.cpp @@ -0,0 +1,33 @@ +/* +* This file contains ps* function implementations for Windows +*/ + +#include "StdInc.h" + +#include "WndProc.h" +#include "WinMain.h" +#include "Input.h" + +#include "Gamma.h" + +// @notsa +// @brief Resets the screen gamma if ever changed. +void ResetGammaWhenExiting() { + if (gbGammaChanged) { + if (auto d3dDevice = (IDirect3DDevice9*)RwD3D9GetCurrentD3DDevice()) { + d3dDevice->SetGammaRamp(0u, D3DSGR_CALIBRATE, &savedGamma); + } + gbGammaChanged = false; + } +} + +void WinPsInjectHooks(); +void Win32InjectHooks() { + RH_ScopedCategory("Win"); + RH_ScopedNamespaceName("Win"); + + InjectHooksWndProcStuff(); + InjectWinMainStuff(); + WinPsInjectHooks(); + WinInput::InjectHooks(); +} diff --git a/source/app/platform/win/Platform.h b/source/app/platform/win/Platform.h new file mode 100644 index 0000000000..96ac8ca357 --- /dev/null +++ b/source/app/platform/win/Platform.h @@ -0,0 +1,48 @@ +#pragma once + +#define APP_CLASS "Grand theft auto San Andreas" + +//! Win32 fail check (returns from function automagically) +#define WIN_FCHECK(x) do { \ + if (HRESULT hr = (x); FAILED(hr)) { \ + DEV_LOG(TEXT("FAILED(hr=0x{:x}) in ") TEXT(#x) TEXT("\n"), (size_t)(hr)); \ + return; \ + } \ + } while (0) \ + +// Custom enum, generated by ChatGPT ~~thank you~~ - ChatGPT was wrong. +// +enum class WinVer { + WIN_95, //< Windows 95 + WIN_98, //< Windows 98 + WIN_NT, //< Windows NT + WIN_2000_XP_2003, //< Windows 2000/XP/Server 2003 + WIN_VISTA_OR_LATER, //< Windows Vista/7/8/10 + + UNKNOWN +}; +static auto& s_WinVer = StaticRef(); + +struct OSStatus { + WinVer OSVer; + DWORD DxVer; // Always 0x900 + struct { // TODO: Just use MEMORYSTATUS stuct here + SIZE_T TotalPhys; + SIZE_T AvailPhys; + SIZE_T TotalVirtual; + SIZE_T AvailVirtual; + } RAM; + struct { + SIZE_T Total; + SIZE_T Avail; + } VRAM; +}; +inline auto& s_OSStatus = StaticRef(); + +inline bool& anisotropySupportedByGFX = *(bool*)0xC87FFC; +inline bool& isForeground = *(bool*)0xC920EC; +inline bool& Windowed = *(bool*)0xC920CC; + +void Win32InjectHooks(); + +#define IDI_MAIN_ICON 1042 diff --git a/source/app/platform/win/VideoMode.cpp b/source/app/platform/win/VideoMode.cpp index 3acd3472f5..b925505b1e 100644 --- a/source/app/platform/win/VideoMode.cpp +++ b/source/app/platform/win/VideoMode.cpp @@ -32,9 +32,8 @@ char** GetVideoModeList() { gVideoModes = (char**)CMemoryMgr::Calloc(numVidModes, sizeof(char*)); - RwVideoMode videoMode{}; for (auto modeId = 0u; modeId < numVidModes; modeId++) { - VERIFY(RwEngineGetVideoModeInfo(&videoMode, modeId)); + const auto videoMode = RwEngineGetVideoModeInfo(modeId); gVideoModes[modeId] = nullptr; if ((videoMode.flags & rwVIDEOMODEEXCLUSIVE) == 0) { @@ -60,7 +59,7 @@ char** GetVideoModeList() { } if (videoMode.width != APP_MINIMAL_WIDTH || videoMode.height != APP_MINIMAL_HEIGHT) { - if (gnMemTotalVideo - videoMode.height * videoMode.width * videoMode.depth / 8 <= GAME_FREE_VIDEO_MEM_REQUIRED) { + if (s_OSStatus.VRAM.Avail - videoMode.height * videoMode.width * videoMode.depth / 8 <= GAME_FREE_VIDEO_MEM_REQUIRED) { continue; } } @@ -105,7 +104,5 @@ void SetVideoMode(int32 mode) { // 0x745CA0 bool IsVideoModeExclusive() { // AKA isCurrentModeFullscreen - RwVideoMode videoMode{}; - VERIFY(RwEngineGetVideoModeInfo(&videoMode, gCurrentVideoMode)); - return videoMode.flags & rwVIDEOMODEEXCLUSIVE; + return RwEngineGetVideoModeInfo(gCurrentVideoMode).flags & rwVIDEOMODEEXCLUSIVE; } diff --git a/source/app/platform/win/VideoMode.h b/source/app/platform/win/VideoMode.h index ecf437decb..745fc986fa 100644 --- a/source/app/platform/win/VideoMode.h +++ b/source/app/platform/win/VideoMode.h @@ -1,15 +1,50 @@ #pragma once -static inline bool DefaultVideoMode = *(bool*)0x8D2E34; -static inline bool MultipleVideoModes = *(bool*)0xC92118; -static inline bool VideoModeNotSelected = *(bool*)0x8D6218; +#include "Platform.h" +#include +#include + +// +// Sub Systems +// + +//! Maximum number of subsystems we can deal with +//! 1 SubSystem / Display (And maybe / GPU ?) +constexpr auto MAX_SUBSYSTEMS = 16; + +//! Subsystem infos (Populated using `RwEngineGetSubSystemInfo`) +static inline auto& GsubSysInfo = StaticRef, 0xC8CFC0>(); + +//! Number of subsystems +static inline auto& GnumSubSystems = StaticRef(); + +//! Currently selected subsystem +static inline auto& GcurSelSS = StaticRef(); + +//! Whenever there are multiple subsystems available +static inline auto& MultipleSubSystems = StaticRef(); + +// +// Video Mode +// + +//! Currently selected videomode +static inline auto& GcurSelVM = StaticRef(); // VM = Video Mode + +//! Whenever to use the default videomode (Instead of the user selecting it) +static inline auto& UseDefaultVM = StaticRef(); + +//! Unused shit +static inline auto& DefaultVM = StaticRef(); + +//! Whenever FrontEndMemnuManager videomode stuff was **NOT** yet set (See WinMain) +static inline auto& IsVMNotSelected = StaticRef(); /* * Dynamic array of video modes with format "width x height x depth" */ static inline char**& gVideoModes = *(char***)0xC920D0; static inline uint32& gCurrentGpu = *(uint32*)0x8D6248; -static inline uint32 gnMemTotalVideo = *(uint32*)(0xC8CF68 + 0x18); // todo: fix static inline int32& gCurrentVideoMode = *(int32*)0x8D6220; void VideoModeInjectHooks(); diff --git a/source/app/platform/win/VideoModeSelectDialog.cpp b/source/app/platform/win/VideoModeSelectDialog.cpp new file mode 100644 index 0000000000..0516570ba5 --- /dev/null +++ b/source/app/platform/win/VideoModeSelectDialog.cpp @@ -0,0 +1,178 @@ +#include "StdInc.h" + +#include "VideoModeSelectDialog.h" +#include "VideoMode.h" +#include "platform/platform.h" + +// +// TODO: +// For the final exe we have to add win.rc (A resource file that contains the definition of this dialogbox) +// It will also generate a resource.h that will have these defines in it +// + +//! Resource ID of the videomode select dialog +#define IDD_DIALOG1 104 + +//! Device select combobox item id +#define IDC_DEVICESEL 1000 + +//! Video mode select combobox item id +#define IDC_VIDMODE 1001 + +//! Exit button item id +#define IDEXIT 1002 + +// +// Helper functions +// + +RwInt32 GetCBCurSel(HWND hComboBox) { + return SendMessage(hComboBox, CB_GETCURSEL, NULL, NULL); +} + +void SetCBCurSel(HWND hComboBox, RwInt32 entryIdx) { + SendMessage(hComboBox, CB_SETCURSEL, entryIdx, NULL); +} + +RwInt32 GetCBItemData(HWND hComboBox, RwInt32 entryIdx) { + return SendMessage(hComboBox, CB_GETITEMDATA, entryIdx, NULL); +} + +RwInt32 GetCBCurSelData(HWND hComboBox) { + return GetCBItemData(hComboBox, GetCBCurSel(hComboBox)); +} + +void SetSelectedVM(HWND hDlg, RwInt32 vm) { + GcurSelVM = vm; + SetCBCurSel(GetDlgItem(hDlg, IDC_VIDMODE), vm); +} + +// 0x745920 - Fill in the available VMs to the dialog box vm select item +void FillAvailableVMs(HWND hVMSel) { + const auto numVM = RwEngineGetNumVideoModes(); + for (auto i = 0; i < numVM; i++) { + const auto vmi = RwEngineGetVideoModeInfo(i); + + if ((vmi.flags & rwVIDEOMODEEXCLUSIVE) == 0 || vmi.width < APP_MINIMAL_WIDTH || vmi.height < APP_MINIMAL_HEIGHT) { + continue; + } + + const auto aspectr = (float)vmi.height / (float)vmi.width; + // printf("%f\n", aspectr) // annoying printf that spams the console was here + + const auto gcd = std::gcd(vmi.width, vmi.height); + const auto ratioW = vmi.width / gcd, ratioH = vmi.height / gcd; + + // NOTSA: Provide aspect ratio info (W:H) instead of 'FULLSCREEN' and 'WIDESCREEN'. + if (IsFullScreenRatio(aspectr) || IsWideScreenRatio(aspectr)) { + static char vmName[1024]; + *std::format_to(vmName, "{} x {} x {} ({}:{})", vmi.width, vmi.height, vmi.depth, ratioW, ratioH) = 0; + const auto idx = SendMessage(hVMSel, CB_ADDSTRING, NULL, (LPARAM)vmName); // Add entry, and get it's index + SendMessage(hVMSel, CB_SETITEMDATA, idx, i); // Set index of that entry to correspond to `i` + } else { + DEV_LOG("Not listing video mode ({}) to device select! [{} x {} ({}:{})]", i, vmi.width, vmi.height, ratioW, ratioH); + } + } +} + +// Fill in the available devices (subystems) +void FillAvailableDevices(HWND hDevSel) { + // Fill in the available subystems + for (auto ss = GnumSubSystems; ss-- > 0;) { + SendMessage(hDevSel, CB_ADDSTRING, NULL, (LPARAM)(&GsubSysInfo[ss])); + SendMessage(hDevSel, CB_SETITEMDATA, (WPARAM)ss, (LPARAM)ss); + } + + // Set current SS as the selected one + SendMessage(hDevSel, CB_SETCURSEL, GcurSelSS, NULL); +} + +void ComboBoxClear(HWND hComboBox) { + SendMessage(hComboBox, CB_RESETCONTENT, NULL, NULL); +} + +// 0x745CC0 - Initialize the dialogbox +INT_PTR InitDialog(HWND hDlg) { + const auto hDevSel = GetDlgItem(hDlg, IDC_DEVICESEL), + hVMSel = GetDlgItem(hDlg, IDC_VIDMODE); + + FillAvailableDevices(hDevSel); + FillAvailableVMs(hVMSel); + + // Figure out currently selected videomode + const auto numVM = RwEngineGetNumVideoModes(); + const auto lastUsedVM = FrontEndMenuManager.m_nDisplayVideoMode; + if (GcurSelVM == -1 && lastUsedVM < numVM && GetVideoModeList()[lastUsedVM]) { + for (auto i = 0; i < numVM; i++) { + if (GetCBItemData(hVMSel, i) == FrontEndMenuManager.m_nDisplayVideoMode) { + SetSelectedVM(hDlg, i); + return NULL; + } + } + + // Failed + SetSelectedVM(hDlg, -1); + } else { + SetSelectedVM(hDlg, RwEngineGetCurrentVideoMode()); + } + + return NULL; +} + +// 0x745E50 +INT_PTR CALLBACK DialogFunc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch (Msg) { + case WM_INITDIALOG: + return InitDialog(hDlg); + + case WM_COMMAND: { + const auto hDevSel = GetDlgItem(hDlg, IDC_DEVICESEL), + hVMSel = GetDlgItem(hDlg, IDC_VIDMODE); + + switch (LOWORD(wParam)) { + case IDEXIT: { + if (HIWORD(wParam) == 0) { + EndDialog(hDlg, NULL); + } + return TRUE; + } + case IDC_VIDMODE: { + if (HIWORD(wParam) == 1) { + GcurSelVM = GetCBCurSelData(hVMSel); + } + return TRUE; + } + case IDOK: { + if (HIWORD(wParam) == 0) { + GcurSelVM = GetCBCurSelData(hVMSel); + EndDialog(hDlg, TRUE); + } + return TRUE; + } + case IDC_DEVICESEL: { // Function from 0x745DC0 + const auto hDevSel = GetDlgItem(hDlg, IDC_DEVICESEL), + hVMSel = GetDlgItem(hDlg, IDC_VIDMODE); + + const auto currEntry = GetCBCurSel(hDevSel); + if (currEntry != GcurSelSS) { + GcurSelSS = GetCBItemData(hDevSel, currEntry); + + VERIFY(RwEngineSetSubSystem(GcurSelSS)); + + // Deal with VM select ComboBox + ComboBoxClear(hVMSel); + FillAvailableVMs(hVMSel); + SetSelectedVM(hDlg, RwEngineGetCurrentVideoMode()); + } + return TRUE; + } + break; + } + } + } + return FALSE; +} + +LRESULT CreateVidModeSelectDialog(HINSTANCE hInst, HWND hWnd) { + return DialogBoxParam(hInst, MAKEINTRESOURCEA(IDD_DIALOG1), hWnd, DialogFunc, 0); +} diff --git a/source/app/platform/win/VideoModeSelectDialog.h b/source/app/platform/win/VideoModeSelectDialog.h new file mode 100644 index 0000000000..13fd57af57 --- /dev/null +++ b/source/app/platform/win/VideoModeSelectDialog.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +LRESULT CreateVidModeSelectDialog(HINSTANCE hInst, HWND hWnd); diff --git a/source/app/platform/win/WinMain.cpp b/source/app/platform/win/WinMain.cpp new file mode 100644 index 0000000000..68d45fb524 --- /dev/null +++ b/source/app/platform/win/WinMain.cpp @@ -0,0 +1,422 @@ +/* +* This file contains WinMain and related functions +*/ + +#include "StdInc.h" + +#include "LoadingScreen.h" +#include "ControllerConfigManager.h" +#include "Gamma.h" + +#include "VideoPlayer.h" +#include "VideoMode.h" +#include "Input.h" +#include "Platform.h" +#include "WndProc.h" + +#include "extensions/Configs/FastLoader.hpp" + +constexpr auto NO_FOREGROUND_PAUSE = true; + +// 0x746870 +void MessageLoop() { + MSG msg; + while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE | PM_NOYIELD)) { + if (msg.message == WM_QUIT) { + RsGlobal.quit = true; + } else { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + } +} + +// 0x7486A0 +bool InitApplication(HINSTANCE hInstance) { + WNDCLASS windowClass = { 0 }; + windowClass.style = CS_BYTEALIGNWINDOW; + windowClass.lpfnWndProc = __MainWndProc; + windowClass.hInstance = hInstance; + windowClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON)); + windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); + windowClass.lpszClassName = APP_CLASS; + return RegisterClass(&windowClass); +} + +// 0x745560 +HWND InitInstance(HINSTANCE hInstance) { + RECT rect = { 0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight }; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false); + + return CreateWindowEx( + 0, + APP_CLASS, + RsGlobal.appName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + nullptr, + nullptr, + hInstance, + nullptr + ); +} + +// 0x7468E0 +bool IsAlreadyRunning() { + CreateEvent(nullptr, false, true, APP_CLASS); + if (GetLastError() != ERROR_ALREADY_EXISTS) { + return false; + } + + const auto fgwnd = FindWindow(APP_CLASS, RsGlobal.appName); + SetForegroundWindow(fgwnd != INVALID_HANDLE_VALUE ? fgwnd : PSGLOBAL(window)); + + return true; +}; + +// 0x746060 +bool IsForegroundApp() { + return ForegroundApp; +} + +// 0x746480 +char** CommandLineToArgv(char* cmdLine, int* argCount) { + return plugin::CallAndReturn(cmdLine, argCount); +} + +// Code from winmain, 0x748DCF +bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { + if (RsGlobal.quit || FrontEndMenuManager.m_bStartGameLoading) { + return false; + } + + // Process Window messages + if (PeekMessage(&Msg, NULL, NULL, NULL, PM_REMOVE | PM_NOYIELD)) { + if (Msg.message == WM_QUIT) { + return false; + } + TranslateMessage(&Msg); + DispatchMessage(&Msg); + return true; + } + + // Game is in background + if (!NO_FOREGROUND_PAUSE && !ForegroundApp) { + if (isForeground) { + isForeground = false; + } + Sleep(100); + return true; + } + + // TODO: Move this out from here (It's not platform specific at all) + switch (gGameState) { + case GAME_STATE_INITIAL: { + const auto ProcessSplash = [](bool isNVidia) { + CLoadingScreen::LoadSplashes(true, isNVidia); + CLoadingScreen::Init(true, true); + CLoadingScreen::DoPCTitleFadeOut(); + CLoadingScreen::DoPCTitleFadeIn(); + CLoadingScreen::Shutdown(); + }; + if (!g_FastLoaderConfig.NoEAX) { + ProcessSplash(false); + } + if (!g_FastLoaderConfig.NoNVidia) { + ProcessSplash(true); + } + ChangeGameStateTo(GAME_STATE_LOGO); + break; + } + case GAME_STATE_LOGO: { + if (!g_FastLoaderConfig.NoLogo) { + if (!Windowed) { + VideoPlayer::Play(nCmdShow, "movies\\Logo.mpg"); + } + } + ChangeGameStateTo(g_FastLoaderConfig.NoLogo ? GAME_STATE_TITLE : GAME_STATE_PLAYING_LOGO); + break; + } + case GAME_STATE_PLAYING_LOGO: + case GAME_STATE_PLAYING_INTRO: { // 0x748B17 + CPad::UpdatePads(); + auto* pad = CPad::GetPad(); + if ( Windowed + || ControlsManager.GetJoyButtonJustDown() + || pad->NewState.CheckForInput() + || CPad::IsMouseLButtonPressed() + || CPad::IsEnterJustPressed() + || pad->IsStandardKeyJustPressed(VK_SPACE) + || CPad::IsMenuKeyJustPressed() + || CPad::IsTabJustPressed() + ) { + ChangeGameStateTo([] { + switch (gGameState) { + case GAME_STATE_PLAYING_LOGO: return GAME_STATE_TITLE; + case GAME_STATE_PLAYING_INTRO: return GAME_STATE_FRONTEND_LOADING; + default: NOTSA_UNREACHABLE(); + } + }()); + } + break; + } + case GAME_STATE_TITLE: { + if (!g_FastLoaderConfig.NoTitleOrIntro) { + VideoPlayer::Shutdown(); + VideoPlayer::Play(nCmdShow, FrontEndMenuManager.GetMovieFileName()); + } + ChangeGameStateTo(g_FastLoaderConfig.NoTitleOrIntro ? GAME_STATE_FRONTEND_LOADING : GAME_STATE_PLAYING_INTRO); + break; + } + case GAME_STATE_FRONTEND_LOADING: { + VideoPlayer::Shutdown(); + CLoadingScreen::Init(true, false); + if (!g_FastLoaderConfig.NoCopyright) { + CLoadingScreen::DoPCTitleFadeOut(); + } + if (!CGame::InitialiseEssentialsAfterRW()) { + RsGlobal.quit = true; + } + CGame::InitialiseCoreDataAfterRW(); + ChangeGameStateTo(GAME_STATE_FRONTEND_LOADED); + anisotropySupportedByGFX = (RwD3D9GetCaps()->RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0; // todo: func + break; + } + case GAME_STATE_FRONTEND_LOADED: { + FrontEndMenuManager.m_bActivateMenuNextFrame = true; + FrontEndMenuManager.m_bMainMenuSwitch = true; + if (IsVMNotSelected) { + FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode = gCurrentVideoMode; + IsVMNotSelected = false; + } + ChangeGameStateTo(GAME_STATE_FRONTEND_IDLE); + if (g_FastLoaderConfig.NoCopyright) { + CLoadingScreen::SkipCopyrightSplash(); + } else { + CLoadingScreen::DoPCTitleFadeIn(); + } + break; + } + case GAME_STATE_FRONTEND_IDLE: { // 0x748CB2 + WINDOWPLACEMENT wndpl{ .length = sizeof(WINDOWPLACEMENT) }; + VERIFY(GetWindowPlacement(PSGLOBAL(window), &wndpl)); + if (g_FastLoaderConfig.ShouldLoadSaveGame()) { + RsEventHandler(rsFRONTENDIDLE, nullptr); // We need to still run the frontend processing once because it has some important stuff + if ((GetAsyncKeyState(g_FastLoaderConfig.SkipSaveGameLoadKey) & 0xF000) == 0) { + g_FastLoaderConfig.StartGame(g_FastLoaderConfig.SaveGameToLoad); // Load game + } + g_FastLoaderConfig.TriedLoadingSaveGame = true; + } else if (g_FastLoaderConfig.RenderAtAllTimes || wndpl.showCmd != SW_SHOWMINIMIZED) { + RsEventHandler(rsFRONTENDIDLE, nullptr); + } + if (FrontEndMenuManager.m_bMenuActive && !FrontEndMenuManager.m_bLoadingData) { + break; + } + ChangeGameStateTo(GAME_STATE_LOADING_STARTED); + if (!FrontEndMenuManager.m_bLoadingData) { + break; + } + NOTSA_SWCFALLTHRU; // Fall down and start loading + } + case GAME_STATE_LOADING_STARTED: { + if (!g_FastLoaderConfig.NoLoadingTune) { + AudioEngine.StartLoadingTune(); + } + + InitialiseGame(); + ChangeGameStateTo(GAME_STATE_IDLE); + FrontEndMenuManager.m_bMainMenuSwitch = false; + + AudioEngine.InitialisePostLoading(); + break; + } + case GAME_STATE_IDLE: { + if (!RwInitialized) + break; + + auto v9_1 = 1000.0f / (float)RsGlobal.frameLimit; + auto v9_2 = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); + if (!FrontEndMenuManager.m_bPrefsFrameLimiter && CReplay::Mode != eReplayMode::MODE_PLAYBACK && !AudioEngine.IsBeatInfoPresent() || v9_1 < v9_2) { + RsEventHandler(rsIDLE, (void*)true); + } + break; + } + } + + if (!isForeground) { + isForeground = true; + } + + return true; +} + +// Code from winmain, 0x7489FB +void MainLoop(INT nCmdShow, MSG& Msg) { + bool bNewGameFirstTime = false; + while (true) { + RwInitialized = true; + + RwV2d pos{ SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f }; + RsMouseSetPos(&pos); + + gamma.Init(); + + // Game logic main loop + while (ProcessGameLogic(nCmdShow, Msg)); + + // 0x748DDA + RwInitialized = false; + FrontEndMenuManager.UnloadTextures(); + if (!FrontEndMenuManager.m_bStartGameLoading) { + break; + } + + // load game + CCheat::ResetCheats(); + CTimer::Stop(); + + if (FrontEndMenuManager.m_bLoadingData) { + CGame::ShutDownForRestart(); + CGame::InitialiseWhenRestarting(); + FrontEndMenuManager.m_bLoadingData = false; + } else if (bNewGameFirstTime) { + CTimer::Stop(); + ChangeGameStateTo( + FrontEndMenuManager.m_nGameState != 1 + ? GAME_STATE_LOADING_STARTED + : GAME_STATE_FRONTEND_LOADED + ); + } else { + CCheat::ResetCheats(); + CGame::ShutDownForRestart(); + CTimer::Stop(); + CGame::InitialiseWhenRestarting(); + } + + bNewGameFirstTime = false; + FrontEndMenuManager.m_nGameState = 0; + FrontEndMenuManager.m_bStartGameLoading = false; + } + +} + +// 0x748710 +INT WINAPI NOTSA_WinMain(HINSTANCE instance, HINSTANCE hPrevInstance, LPSTR cmdLine, INT nCmdShow) { + SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0u, nullptr, 2); + if (IsAlreadyRunning()) { + return false; + } + + auto initializeEvent = RsEventHandler(rsINITIALIZE, nullptr); + if (rsEVENTERROR == initializeEvent) { + return false; + } + + if (!InitApplication(instance)) { + return false; + } + + cmdLine = GetCommandLine(); + int argc; + char** argv = CommandLineToArgv(cmdLine, &argc); + for (int i = 0; i < argc; i++) { + RsEventHandler(rsPREINITCOMMANDLINE, argv[i]); + } + + PSGLOBAL(window) = InitInstance(instance); + if (!PSGLOBAL(window)) { + return false; + } + PSGLOBAL(instance) = instance; + + // 0x7487CF + VERIFY(WinInput::Initialise()); + + ControlsManager.InitDefaultControlConfigMouse(WinInput::GetMouseState(), !FrontEndMenuManager.m_nController); + + // 0x748847 + if (RsEventHandler(rsRWINITIALIZE, PSGLOBAL(window)) == rsEVENTERROR) { + DestroyWindow(PSGLOBAL(window)); + RsEventHandler(rsTERMINATE, nullptr); + return false; + } + + // 0x7488EE + for (auto i = 0; i < argc; i++) { + RsEventHandler(rsCOMMANDLINE, argv[i]); + } + + if (MultipleSubSystems || PSGLOBAL(fullScreen)) { + SetWindowLongPtr(PSGLOBAL(window), GWL_STYLE, (LONG_PTR)WS_POPUP); + SetWindowPos(PSGLOBAL(window), nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + RwRect rect{ 0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight }; + RsEventHandler(rsCAMERASIZE, &rect); + + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0u, nullptr, 2u); + SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0u, nullptr, 2u); + SystemParametersInfo(SPI_SETLOWPOWERACTIVE, 0u, nullptr, 2u); + STICKYKEYS pvParam { .cbSize = sizeof(STICKYKEYS) }; + SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam, 2u); + STICKYKEYS pvParam1 = { .cbSize = sizeof(STICKYKEYS), .dwFlags = SKF_TWOKEYSOFF }; + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam1, 2u); + + ShowWindow(PSGLOBAL(window), nCmdShow); + UpdateWindow(PSGLOBAL(window)); + + // 0x748995 + CFileMgr::SetDirMyDocuments(); + if (auto* file = CFileMgr::OpenFile("gta_sa.set", "rb")) { + if (!ControlsManager.LoadSettings(file)) { + ControlsManager.ReinitControls(); + } + CFileMgr::CloseFile(file); + } + CFileMgr::SetDir(""); + + SetErrorMode(SEM_FAILCRITICALERRORS); + + // 0x7489FB + MSG Msg; + MainLoop(nCmdShow, Msg); + + // if game is loaded, shut it down + if (gGameState == GAME_STATE_IDLE) { + CGame::Shutdown(); + } + + // now quit 0x748E75 + AudioEngine.Shutdown(); + FreeVideoModeList(); + RsEventHandler(rsRWTERMINATE, nullptr); + DestroyWindow(PSGLOBAL(window)); + RsEventHandler(rsTERMINATE, nullptr); + free(argv); + ShowCursor(true); + + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam, 2u); + SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1u, nullptr, 2u); // TODO: GUID_VIDEO_POWERDOWN_TIMEOUT + SystemParametersInfo(SPI_SETLOWPOWERACTIVE, 1u, nullptr, 2u); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1u, nullptr, 2u); + // nullsub_0x72F3C0() + SetErrorMode(0); + + return Msg.wParam; +} + +void InjectWinMainStuff() { + RH_ScopedCategory("Win"); + RH_ScopedNamespaceName("Win"); + + RH_ScopedGlobalInstall(IsForegroundApp, 0x746060); + RH_ScopedGlobalInstall(IsAlreadyRunning, 0x7468E0); + RH_ScopedGlobalInstall(CommandLineToArgv, 0x746480, { .reversed = false }); + + // Unhooking these 2 after the game has started will do nothing + RH_ScopedGlobalInstall(NOTSA_WinMain, 0x748710); + RH_ScopedGlobalInstall(InitInstance, 0x745560); +} diff --git a/source/app/platform/win/WinMain.h b/source/app/platform/win/WinMain.h new file mode 100644 index 0000000000..99cc414e24 --- /dev/null +++ b/source/app/platform/win/WinMain.h @@ -0,0 +1,3 @@ +#pragma once + +void InjectWinMainStuff(); diff --git a/source/app/platform/win/WinPs.cpp b/source/app/platform/win/WinPs.cpp new file mode 100644 index 0000000000..b3497ea3cb --- /dev/null +++ b/source/app/platform/win/WinPs.cpp @@ -0,0 +1,587 @@ +#include "StdInc.h" + +#include +#include +#include +#include +#include "VideoMode.h" +#include "VideoModeSelectDialog.h" +#include "LoadingScreen.h" +#include "C_PcSave.h" + +// NOTE: This macro doesn't do a whole lot. Leaving it here for completeness sake +#define USE_D3D9 + +static auto& PsGlobal = StaticRef(); + +//! Disable "This function was depracated" +#pragma warning (disable : 28159 4996) + +// 0x7455E0 - Get available videomem +HRESULT GetVideoMemInfo(LPDWORD total, LPDWORD available) { + LPDIRECTDRAW7 dd; + + if (HRESULT hr = DirectDrawCreateEx(NULL, (LPVOID*)&dd, IID_IDirectDraw7, NULL); FAILED(hr)) { + return hr; + } + + DDSCAPS2 caps; + ZeroMemory(&caps, sizeof(DDSCAPS2)); + caps.dwCaps = DDSCAPS_VIDEOMEMORY; + HRESULT hr = dd->GetAvailableVidMem(&caps, total, available); + dd->Release(); + + return hr; +} + +//! Check if D3D9 can be loaded (Originally this checked for versions 7 => 9, but GTA can only run with 9, so... :D +BOOL CheckDirectX() { + const auto hD3D9DLL = LoadLibrary("D3D9.DLL"); + if (hD3D9DLL == NULL) { + return FALSE; + } + FreeLibrary(hD3D9DLL); + return TRUE; +} + +//! 0x745840 - Check if DirectSound can be loaded +BOOL CheckDirectSound() { + LPDIRECTSOUND ds; + + if (FAILED(DirectSoundCreate(NULL, &ds, NULL))) { + return FALSE; + } + + DSCAPS caps{ sizeof(DSCAPS) }; + HRESULT hr = ds->GetCaps(&caps); + + ds->Release(); + + return SUCCEEDED(hr); +} + +// 0x7465B0 +void InitialiseLanguage() { +//#pragma warning (disable : 4302) // "Type truncation from HKL to + + // TODO: Use `GetLocaleInfoEx` + const auto sysDefaultLCID = PRIMARYLANGID(GetSystemDefaultLCID()); + const auto usrDefaultLCID = PRIMARYLANGID(GetUserDefaultLCID()); + const auto kbLayoutLCID = PRIMARYLANGID(LOWORD(GetKeyboardLayout(0))); + + FrontEndMenuManager.m_nTitleLanguage = sysDefaultLCID; + + FrontEndMenuManager.m_nTextLanguage = (int32)[&] { + switch (kbLayoutLCID) { + case LANG_GERMAN: return eLanguage::GERMAN; + case LANG_SPANISH: return eLanguage::SPANISH; + case LANG_FRENCH: return eLanguage::FRENCH; + case LANG_ITALIAN: return eLanguage::ITALIAN; + default: return eLanguage::AMERICAN; + } + }(); + + FrontEndMenuManager.m_nPrefsLanguage = [&] { + switch (usrDefaultLCID) { + case LANG_SPANISH: return eLanguage::SPANISH; + default: return eLanguage::AMERICAN; + } + }(); + + // Reload text + TheText.Unload(false); + TheText.Load(false); +} + +// 0x747420 +RwBool psInitialize() { + auto ps = &PsGlobal; + + RsGlobal.ps = ps; + + ps->lastMousePos.y = 0.f; + ps->lastMousePos.x = 0.f; + ps->fullScreen = FALSE; + ps->diInterface = NULL; + ps->diMouse = NULL; + ps->diDevice1 = NULL; + ps->diDevice2 = NULL; + + CFileMgr::Initialise(); + const auto usrdir = InitUserDirectories(); + s_PcSaveHelper.SetSaveDirectory(usrdir); + + gGameState = GAME_STATE_INITIAL; + + // TODO: Load vendor from CPUID + + // Figure out Windows version (TODO: Use `IsWindowsVersion*` from VersionHelpers.h instead) + s_OSStatus.OSVer = [&] { + OSVERSIONINFO verInfo{ sizeof(OSVERSIONINFO) }; + GetVersionEx(&verInfo); + + if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { + switch (const auto mv = verInfo.dwMajorVersion) { + case 4: return WinVer::WIN_NT; + case 5: return WinVer::WIN_2000_XP_2003; + case 6: return WinVer::WIN_VISTA_OR_LATER; + default: return mv < 4 ? WinVer::WIN_NT : WinVer::WIN_VISTA_OR_LATER; + } + } else if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + return verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 + ? WinVer::WIN_98 + : WinVer::WIN_95; + } + NOTSA_UNREACHABLE(); // If this is reached the game wouldn't run anyways, so might as well just crash it. + }(); + + if (s_OSStatus.OSVer == WinVer::WIN_95) { + MessageBoxW( + NULL, + (LPCWSTR)TheText.Get("WIN_95"), // Grand Theft Auto San Andreas cannot run on Windows 95 + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto San Andreas + MB_OK + ); + + return FALSE; + } + + // Originally figured out available dx version and only allowed dx9, but we've simplified it. + if (!CheckDirectX()) { + MessageBoxW( + NULL, + (LPCWSTR)TheText.Get("WIN_DX"), // Grand Theft Auto San Andreas requires at least DirectX version 8.1 + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto San Andreas + MB_OK + ); + return FALSE; + } + + // CheckDirectX() Checks for Dx9 only, so use that + s_OSStatus.DxVer = 0x900; + + if (!CheckDirectSound()) { + MessageBoxW( + NULL, + (LPCWSTR)TheText.Get("WIN_NSC"), // Grand Theft Auto San Andreas requires a sound card (I guess?) + (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto San Andreas + MB_ICONEXCLAMATION + ); + return FALSE; + } + + MEMORYSTATUS memstat{ sizeof(MEMORYSTATUS) }; + GlobalMemoryStatus(&memstat); // TODO: `GlobalMemoryStatusEx` + s_OSStatus.RAM.TotalPhys = memstat.dwTotalPhys; + s_OSStatus.RAM.AvailPhys = memstat.dwAvailPhys; + s_OSStatus.RAM.TotalVirtual = memstat.dwTotalVirtual; + s_OSStatus.RAM.AvailVirtual = memstat.dwAvailVirtual; + + VERIFY(SUCCEEDED(GetVideoMemInfo(&s_OSStatus.VRAM.Total, &s_OSStatus.VRAM.Avail))); + VERIFY(SUCCEEDED(CoInitialize(NULL))); + + // Load setting only after everything was checked - TODO: Move this out from here, it's not platform specific + FrontEndMenuManager.LoadSettings(); + + return TRUE; +} + +// 0x7458A0 +void psTerminate() { + // NOP +} + +// 0x7451B0 +void psWindowSetText(const char* str) { + SetWindowTextA(PSGLOBAL(window), str); +} + +// 0x7451D0 +void psErrorMessage(const char* str) { + MessageBoxA(nullptr, str, RsGlobal.appName, MB_ICONERROR | MB_TASKMODAL | MB_TOPMOST); +} + +// 0x7451F0 +void psWarningMessage(const char* str) { + MessageBoxA(nullptr, str, RsGlobal.appName, MB_ICONWARNING | MB_TASKMODAL | MB_TOPMOST); +} + +// 0x745210 +bool psCameraBeginUpdate(RwCamera* camera) { + if (RwCameraBeginUpdate(Scene.m_pRwCamera)) { + return true; + } + RsEventHandler(rsACTIVATE, nullptr); + return false; +} + +// 0x745240 +RwCamera* psCameraShowRaster(RwCamera* camera) { + auto flags = FrontEndMenuManager.m_bPrefsFrameLimiter || CLoadingScreen::m_bActive ? rwRASTERFLIPWAITVSYNC : rwRASTERFLIPDONTWAIT; + return RwCameraShowRaster(camera, PSGLOBAL(window), flags); +} + +// 0x745270 +uint32 psTimer() { + return OS_TimeMS(); +} + +// 0x7452B0 +RwImage* psGrabScreen(RwCamera* camera) { + auto* device = static_cast(RwD3D9GetCurrentD3DDevice()); + assert(device); + + D3DDISPLAYMODE displayMode{}; + VERIFY(SUCCEEDED(device->GetDisplayMode(0, &displayMode))); + + IDirect3DSurface9* surface = nullptr; + VERIFY(SUCCEEDED(device->CreateOffscreenPlainSurface(displayMode.Width, displayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr))); + VERIFY(SUCCEEDED(device->GetFrontBufferData(0, surface))); + + D3DLOCKED_RECT lockedRect{}; +#ifndef FIX_BUGS + // It's not needed as ClientToScreen func works with fullscreen mode. + if (PSGLOBAL(fullScreen)) { // todo: Doesn't work properly with III.VC.SA.WindowedMode.asi + VERIFY(SUCCEEDED(surface->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY))); + } else { +#endif + RECT rect; +#ifdef FIX_BUGS + // SA code gets the whole window of the game, which includes the titlebar etc. + // + // BUG: There should be bugs for older versions of Windows IIRC. + // One example would be Vista version of the func doesn't count Aero effects of windows. + // + // TODO: Test with dual monitors etc. + + GetClientRect(PSGLOBAL(window), &rect); + + // GetClientRect returns relative positions unlike GetWindowRect. + // i.e. will return { 0, 0, width, height }. + ClientToScreen(PSGLOBAL(window), (POINT*)(&rect)); // kinda hacky but should work. + rect.right += rect.left; + rect.bottom += rect.top; +#else + GetWindowRect(PSGLOBAL(window), &rect); +#endif + displayMode.Height = rect.bottom - rect.top; + displayMode.Width = rect.right - rect.left; + VERIFY(SUCCEEDED(surface->LockRect(&lockedRect, &rect, D3DLOCK_READONLY))); +#ifndef FIX_BUGS + } +#endif + + RwImage* image = RwImageCreate(int32(displayMode.Width), int32(displayMode.Height), 32); + if (image) { + RwImageAllocatePixels(image); + + auto* pixels = (RwRGBA*)RwImageGetPixels(image); + auto* imagePixels = (uint8*)lockedRect.pBits; + assert(pixels && imagePixels); + + for (auto h = 0u; h < displayMode.Height; h++) { + for (auto w = 0u; w < displayMode.Width; w++) { + pixels->red = imagePixels[sizeof(RwRGBA) * w + 2]; + pixels->green = imagePixels[sizeof(RwRGBA) * w + 1]; + pixels->blue = imagePixels[sizeof(RwRGBA) * w + 0]; + pixels->alpha = 255; + pixels++; + } + imagePixels += lockedRect.Pitch; + } + } + + { // FIX_BUGS + surface->UnlockRect(); + surface->Release(); + delete surface; + } + + return image; +} + +// 0x7453E0 +void psMouseSetVisibility(bool visible) { + ::ShowCursor(visible); +} + +// 0x7453F0 +void psMouseSetPos(RwV2d* pos) { + POINT point = { .x = LONG(pos->x), .y = LONG(pos->y) }; + ::ClientToScreen(PSGLOBAL(window), &point); + ::SetCursorPos(point.x, point.y); + PSGLOBAL(lastMousePos) = *pos; +} + +// 0x745470 +char* psPathnameCreate(const char* buffer) { + const auto pathSize = std::strlen(buffer) + 1u; + auto path = (char*)CMemoryMgr::Malloc(pathSize); + if (path) { + strcpy_s(path, pathSize, buffer); + + while (auto ch = std::strchr(path, '/')) { + *ch = psPathGetSeparator(); + } + } + + return path; +} + +// 0x7454E0 +void psPathnameDestroy(char* buffer) { + if (buffer) { + RwFree(buffer); + } +} + +// 0x745500 +char psPathGetSeparator() { + return '\\'; +} + +// 0x745520 +bool psInstallFileSystem() { + return true; +} + +// 0x745530 +bool psNativeTextureSupport() { + return RwD3D9DeviceSupportsDXTTexture(); +} + +// 0x745540 +RsEventStatus psDebugMessageHandler(RsEvent event, void* param) { + if (rsINITDEBUG == event) { + RwDebugSetHandler(psDebugMessageHandler); +#if defined(DEBUG) && defined(RWTRACE) + RwDebugSetTraceState(true); +#else + RwDebugSetTraceState(false); +#endif + } + + return rsEVENTNOTPROCESSED; +} + +// 0x7458B0 +bool psAlwaysOnTop(bool alwaysOnTop) { + const auto hwnd = PSGLOBAL(window); + + RECT winRect; + VERIFY(GetWindowRect(hwnd, &winRect)); + + return SetWindowPos( + hwnd, + alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, + winRect.left, winRect.top, + winRect.right - winRect.left, winRect.bottom - winRect.top, + NULL + ); +} + +// NOTSA +auto GetNativeResolutionOfCurrentSubsystem() { +#ifdef USE_D3D9 + const auto d3d = Direct3DCreate9(D3D_SDK_VERSION); +#else + const auto d3d = Direct3DCreate8(D3D_SDK_VERSION); +#endif + const notsa::ScopeGuard released3d{[d3d] { d3d->Release(); }}; + assert(d3d != NULL); + + D3DDISPLAYMODE nativeRes; + d3d->GetAdapterDisplayMode(RwEngineGetCurrentSubSystem(), &nativeRes); + + DEV_LOG("Got native resolution from RW subsystem ({}): {} x {}", RwEngineGetCurrentSubSystem(), nativeRes.Width, nativeRes.Height); + + return std::make_pair(nativeRes.Width, nativeRes.Height); +} + +BOOL CheckDefaultVideoModeSupported() { + // IMPROVEMENT/FIX_BUGS: The game will now default to native adapter + // resolution instead of 800x600. + + const auto SearchVideoMode = [](int32 width, int32 height) { + for (auto i = 0; i < RwEngineGetNumVideoModes(); i++) { + const auto vm = RwEngineGetVideoModeInfo(i); + if (vm.width == width && vm.height == height && vm.depth == 32 && (vm.flags & rwVIDEOMODEEXCLUSIVE)) { + return i; + } + } + + return -1; + }; + + if (notsa::IsFixBugs()) { + const auto&& [w, h] = GetNativeResolutionOfCurrentSubsystem(); + if (const auto vm = SearchVideoMode(w, h); vm != -1) { + GcurSelVM = vm; + return TRUE; + } + /* fallthrough if native res is not supported! */ + } + + if (const auto vm = SearchVideoMode(800, 600); vm != -1) { + GcurSelVM = vm; + return TRUE; + } + + MessageBox(NULL, "Cannot find 800x600x32 video mode", "GTA: San Andreas", IDOK); + return FALSE; +} + +// 0x7460A0 +RwUInt32 GetBestRefreshRate(RwUInt32 width, RwUInt32 height, RwUInt32 depth) { +#ifdef USE_D3D9 + const auto d3d = Direct3DCreate9(D3D_SDK_VERSION); +#else + const auto d3d = Direct3DCreate8(D3D_SDK_VERSION); +#endif + const notsa::ScopeGuard released3d{ [d3d] { d3d->Release(); } }; + + assert(d3d != NULL); + + RwUInt32 refreshRate = INT_MAX; + + const auto format = depth == 32 + ? D3DFMT_X8R8G8B8 + : depth == 24 + ? D3DFMT_R8G8B8 + : D3DFMT_R5G6B5; + +#ifdef USE_D3D9 + for (auto i = d3d->GetAdapterModeCount(GcurSelSS, format); i-- > 0;) { +#else + for (auto i = d3d->GetAdapterModeCount(GcurSel); i-- > 0;) { +#endif + D3DDISPLAYMODE mode; + +#ifdef USE_D3D9 + d3d->EnumAdapterModes(GcurSelSS, format, i, &mode); +#else + d3d->EnumAdapterModes(GcurSel, i, &mode); +#endif + + if (mode.Width != width || mode.Height != height || mode.Format != format) { + continue; + } + + if (mode.RefreshRate == 0) { + return 0; + } + + if (mode.RefreshRate < refreshRate && mode.RefreshRate >= 60) { + refreshRate = mode.RefreshRate; + } + } + + return refreshRate; +} + +// 0x746190 +bool psSelectDevice() { + const auto wnd = PSGLOBAL(window); + const auto inst = PSGLOBAL(instance); + + if (!UseDefaultVM) { + GnumSubSystems = RwEngineGetNumSubSystems(); + if (!GnumSubSystems) { + DEV_LOG("No SubSystems to select from!"); + return FALSE; + } + + /* Just to be sure ... */ + GnumSubSystems = std::min(MAX_SUBSYSTEMS, GnumSubSystems); + + /* Get the names of all the sub systems */ + for (auto i = 0; i < GnumSubSystems; i++) { + RwEngineGetSubSystemInfo(&GsubSysInfo[i], i); + } + + /* Get the default selection */ + GcurSelSS = RwEngineGetCurrentSubSystem(); + } + + MultipleSubSystems = GnumSubSystems > 1; + + // Select video mode to use + if (MultipleSubSystems && !UseDefaultVM) { + if (!CreateVidModeSelectDialog(inst, wnd)) { + return FALSE; // User failed to select video mode + } + } + + // Set selected subsystem + if (!RwEngineSetSubSystem(GcurSelSS)) { + DEV_LOG("Failed: RwEngineSetSubSystem({})", GcurSelSS); + return FALSE; + } + + DEV_LOG("GcurSelSS={}", GcurSelSS); + + if (!UseDefaultVM && !MultipleSubSystems) { + const auto vmDisplay = FrontEndMenuManager.m_nDisplayVideoMode; + if (!vmDisplay || !GetVideoModeList()[vmDisplay]) { + if (IsVMNotSelected && !CheckDefaultVideoModeSupported()) { + return FALSE; + } + } else { + GcurSelVM = FrontEndMenuManager.m_nPrefsVideoMode = vmDisplay; + } + } + + FrontEndMenuManager.m_nCurrentScreenItem = 0; + + // Set selected videomode + if (!RwEngineSetVideoMode(GcurSelVM)) { + DEV_LOG("Failed: RwEngineSetVideoMode({})", GcurSelVM); + return FALSE; + } + + DEV_LOG("GcurSelVM={}", GcurSelVM); + + if (const auto vmi = RwEngineGetVideoModeInfo(GcurSelVM); vmi.flags & rwVIDEOMODEEXCLUSIVE) { + if (const auto rr = GetBestRefreshRate(vmi.width, vmi.height, vmi.depth); rr != -1) { + DEV_LOG("Refresh Rate: {} Hz", rr); + RwD3D9EngineSetRefreshRate(rr); + } + + RsGlobal.maximumHeight = vmi.height; + RsGlobal.maximumWidth = vmi.width; + PSGLOBAL(fullScreen) = true; + } + + RwD3D9EngineSetMultiSamplingLevels(FrontEndMenuManager.m_nPrefsAntialiasing); + + return TRUE; +} + +void WinPsInjectHooks() { + RH_ScopedCategory("Win"); + RH_ScopedNamespaceName("Ps"); + + RH_ScopedGlobalInstall(psInitialize, 0x747420); + RH_ScopedGlobalInstall(psTerminate, 0x7458A0); + RH_ScopedGlobalInstall(psWindowSetText, 0x7451B0); + RH_ScopedGlobalInstall(psErrorMessage, 0x7451D0); + RH_ScopedGlobalInstall(psWarningMessage, 0x7451F0); + RH_ScopedGlobalInstall(psCameraBeginUpdate, 0x745210); + RH_ScopedGlobalInstall(psCameraShowRaster, 0x745240); + RH_ScopedGlobalInstall(psTimer, 0x745270); + RH_ScopedGlobalInstall(psGrabScreen, 0x7452B0); + RH_ScopedGlobalInstall(psMouseSetVisibility, 0x7453E0); + RH_ScopedGlobalInstall(psMouseSetPos, 0x7453F0); + RH_ScopedGlobalInstall(psPathnameCreate, 0x745470); + RH_ScopedGlobalInstall(psPathnameDestroy, 0x7454E0); + RH_ScopedGlobalInstall(psPathGetSeparator, 0x745500); + RH_ScopedGlobalInstall(psInstallFileSystem, 0x745520); + RH_ScopedGlobalInstall(psNativeTextureSupport, 0x745530); + RH_ScopedGlobalInstall(psDebugMessageHandler, 0x745540); + RH_ScopedGlobalInstall(psAlwaysOnTop, 0x7458B0); + RH_ScopedGlobalInstall(psSelectDevice, 0x746190); + + RH_ScopedGlobalInstall(InitialiseLanguage, 0x7465B0); + RH_ScopedGlobalInstall(CheckDirectSound, 0x745840); + RH_ScopedGlobalInstall(GetVideoMemInfo, 0x7455E0); +} diff --git a/source/app/platform/win/WndProc.cpp b/source/app/platform/win/WndProc.cpp new file mode 100644 index 0000000000..6a6299b457 --- /dev/null +++ b/source/app/platform/win/WndProc.cpp @@ -0,0 +1,408 @@ +/* +* This file contains WndProc and related functions +*/ + +#include "StdInc.h" + +#include "imgui_impl_win32.h" + +#include +#include +#include +#include + +#include +#include "PostEffects.h" +#include "AEAudioHardware.h" +#include "VideoMode.h" +#include "VideoPlayer.h" +#include "Input.h" +#include "Gamma.h" + +// Dear ImGui said I have to copy this here +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// 0x747820 +BOOL GTATranslateKey(RsKeyCodes* ck, LPARAM lParam, UINT vk) { + *ck = [&] { + // Handle extended keys + const auto Ext = [kf = HIWORD(lParam)](RsKeyCodes extended, RsKeyCodes unextended) { + return (kf & KF_EXTENDED) ? extended : unextended; + }; + + switch (vk) { + case VK_RETURN: return Ext(rsPADENTER, rsENTER); + case VK_CONTROL: return Ext(rsRCTRL, rsLCTRL); + case VK_MENU: return Ext(rsRALT, rsLALT); + case VK_PRIOR: return Ext(rsPGUP, rsPADPGUP); + case VK_NEXT: return Ext(rsPGDN, rsPADPGDN); + case VK_END: return Ext(rsEND, rsPADEND); + case VK_HOME: return Ext(rsHOME, rsPADHOME); + case VK_LEFT: return Ext(rsLEFT, rsPADLEFT); + case VK_UP: return Ext(rsUP, rsPADUP); + case VK_RIGHT: return Ext(rsRIGHT, rsPADRIGHT); + case VK_DOWN: return Ext(rsDOWN, rsPADDOWN); + case VK_INSERT: return Ext(rsINS, rsPADINS); + case VK_DELETE: return Ext(rsDEL, rsPADDEL); + case VK_BACK: return rsBACKSP; + case VK_TAB: return rsTAB; + case VK_PAUSE: return rsPAUSE; + case VK_CAPITAL: return rsCAPSLK; + case VK_ESCAPE: return rsESC; + case VK_LWIN: return rsLWIN; + case VK_RWIN: return rsRWIN; + case VK_APPS: return rsAPPS; + case VK_NUMPAD0: return rsPADINS; + case VK_NUMPAD1: return rsPADEND; + case VK_NUMPAD2: return rsPADDOWN; + case VK_NUMPAD3: return rsPADPGDN; + case VK_NUMPAD4: return rsPADLEFT; + case VK_NUMPAD5: return rsPAD5; + case VK_NUMPAD6: return rsPADRIGHT; + case VK_NUMPAD7: return rsPADHOME; + case VK_NUMPAD8: return rsPADUP; + case VK_NUMPAD9: return rsPADPGUP; + case VK_MULTIPLY: return rsTIMES; + case VK_ADD: return rsPLUS; + case VK_SUBTRACT: return rsMINUS; + case VK_DECIMAL: return rsPADDEL; + case VK_DIVIDE: return rsDIVIDE; + case VK_F1: return rsF1; + case VK_F2: return rsF2; + case VK_F3: return rsF3; + case VK_F4: return rsF4; + case VK_F5: return rsF5; + case VK_F6: return rsF6; + case VK_F7: return rsF7; + case VK_F8: return rsF8; + case VK_F9: return rsF9; + case VK_F10: return rsF10; + case VK_F11: return rsF11; + case VK_F12: return rsF12; + case VK_NUMLOCK: return rsNUMLOCK; + case VK_SCROLL: return rsSCROLL; + case VK_SHIFT: { + return s_OSStatus.OSVer == WinVer::WIN_98 // Will be handled later + ? rsSHIFT + : rsNULL; + } + default: { // Try mapping to regular ASCII char + const auto chr = MapVirtualKey(vk, MAPVK_VK_TO_CHAR); + if (chr <= 0xFF) { + return (RsKeyCodes)(chr); + } + break; + } + } + return rsNULL; + }(); + return *ck != rsNULL; +} + +/*! +* Process shift keys. +* Unless Win98, in which case `GTATranslateKey` should handle it. +* @addr 0x747CD0 +*/ +BOOL GTATranslateShiftKey(RsKeyCodes*) { // The in keycode is ignored, so we won't bother + if (s_OSStatus.OSVer == WinVer::WIN_98) { + return false; // Already handled by `GTATranslateKey` + } + + constexpr struct { RsKeyCodes ck; INT vk; } Keys[]{ + {rsLSHIFT, VK_LSHIFT}, + {rsRSHIFT, VK_RSHIFT}, + }; + + for (auto shouldBeDown : { false, true }) { + for (auto [ck, vk] : Keys) { + // GetKeyState reads from the message queue, + // so we must call it like the og code + const auto isDown = (HIWORD(GetKeyState(vk)) & 0x80) == 1; // Check is key pressed + if (isDown == shouldBeDown) { + RsEventHandler( + isDown ? rsKEYDOWN : rsKEYUP, + &ck + ); + } + } + } + + return true; +} + + +// 0x747EB0 +LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + const auto imio = ImGui::GetCurrentContext() ? &ImGui::GetIO() : nullptr; + if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam)) { + return true; + } + + switch (uMsg) { + case WM_SETCURSOR: { + ShowCursor(false); + SetCursor(NULL); + break; + } + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: { //< 0x74823B - wParam is a `VK_` (virtual key), lParam are the flags (See https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keyup)] + if (imio && imio->WantCaptureKeyboard) { + return 0; + } + + if (RsKeyCodes ck; GTATranslateKey(&ck, lParam, wParam)) { + RsKeyboardEventHandler( + (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) + ? rsKEYDOWN + : rsKEYUP, + &ck + ); + } + if (wParam == VK_SHIFT) { + RsKeyCodes ck; + GTATranslateShiftKey(&ck); // Original code uses this variable for storage, so we can't pass in nullptr - TODO: Remove parameter + } + return 0; + } + case WM_MOUSEMOVE: { //< 0x748323 + if (imio && imio->WantCaptureMouse) { + return 0; + } + FrontEndMenuManager.m_nMousePosWinX = GET_X_LPARAM(lParam); + FrontEndMenuManager.m_nMousePosWinY = GET_Y_LPARAM(lParam); + break; + } + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: { + if (imio && imio->WantCaptureMouse) { + return 0; + } + SetCapture(hWnd); + return 0; + } + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: { + if (imio && imio->WantCaptureMouse) { + return 0; + } + ReleaseCapture(); + return 0; + } + case WM_MOUSEWHEEL: + return 0; + case WM_SIZING: { // 0x74829E + if (RwInitialized) { + if (gGameState == GAME_STATE_IDLE) { + RsEventHandler(rsIDLE, (void*)TRUE); + } + } + + const auto wndrect = reinterpret_cast(lParam); + VERIFY(SetWindowPos( + hWnd, + HWND_TOP, + 0, 0, + wndrect->right - wndrect->left, wndrect->bottom - wndrect->top, + SWP_NOSIZE | SWP_NOMOVE + )); + + CPostEffects::SetupBackBufferVertex(); + + return 0; + } + case WM_ACTIVATEAPP: { // 0x748087 + const auto wndBeingActivated = !!wParam; + + //> 0x748087 - Set gamma (If changed) + if (gbGammaChanged) { + if (const auto dev = RwD3D9GetCurrentD3DDevice()) { + dev->SetGammaRamp(9, 0, wndBeingActivated ? &gammaTable : &savedGamma); + } + } + + // + switch (gGameState) { + case GAME_STATE_PLAYING_LOGO: + case GAME_STATE_PLAYING_INTRO: { + const auto mc = VideoPlayer::GetMediaControl(); + + OAFilterState state; + VERIFY(SUCCEEDED(mc->GetState(10, &state))); + if (wndBeingActivated) { + VideoPlayer::UpdateWindow(); + if (state == State_Running) { + break; + } + VERIFY(SUCCEEDED(mc->Run())); + VideoPlayer::WaitState(State_Running); + SetFocus(PSGLOBAL(window)); + } else if (state == State_Running) { // Window is being deactivatd, pause media + VERIFY(SUCCEEDED(mc->Pause())); + } + + break; + } + case GAME_STATE_INITIAL: { + if (!wndBeingActivated) { // 0x748171 + if (PSGLOBAL(fullScreen)) { + Windowed = true; // ??? + } + } + break; + } + } + + //> 0x748183 Clear pads + for (auto& pad : CPad::Pads) { + pad.Clear(false, true); + } + + return 0; + } + case WM_DESTROY: + case WM_CLOSE: { // 0x747EF3 + VERIFY(ClipCursor(nullptr)); + VERIFY(SUCCEEDED(WinInput::Shutdown())); + PostQuitMessage(0); + return 0; + } + case WM_SIZE: { // 0x747F04 + // Figure out new size + const auto width = LOWORD(lParam), height = HIWORD(lParam); + RwRect wndsz{ + .x = 0, .y = 0, + .w = width, .h = height, + }; + + // Nothing to do? + if (!RwInitialized || !wndsz.h || !wndsz.w) { + return 0; + } + + // Try resizing the camera + RsEventHandler(rsCAMERASIZE, &wndsz); + + // Check if it has failed + if (wndsz.w != width && wndsz.h != height) { // TODO/BUG: Shouldnt `&&` instead be `||`? + VERIFY(ReleaseCapture()); + + WINDOWPLACEMENT wndpl; + VERIFY(GetWindowPlacement(PSGLOBAL(window), &wndpl)); + + if (wndpl.showCmd == SW_SHOWMAXIMIZED) { + SendMessage(PSGLOBAL(window), WM_WINDOWPOSCHANGED, 0, 0); + } + } + + return 0; + } + case WM_ACTIVATE: { // 0x747FA3 + const auto wndBeingActivated = !!wParam; + + if (wndBeingActivated) { + CAudioEngine::ResumeAllSounds(); + ShowCursor(FALSE); + } else { + CAudioEngine::PauseAllSounds(); + VERIFY(SUCCEEDED(SetTimer(PSGLOBAL(window), 1, (UINT)(1000.f / RsGlobal.frameLimit), nullptr))); + } + + ForegroundApp = wndBeingActivated; + RsEventHandler(rsACTIVATE, (void*)wndBeingActivated); + + //> 0x74800C Clear pads + for (auto& pad : CPad::Pads) { + pad.Clear(false, true); + } + +#ifdef FIX_BUGS + break; +#else + return 0; +#endif + } + case WM_SETFOCUS: { // 0x748063 + ForegroundApp = true; + if (!FrontEndMenuManager.m_bMainMenuSwitch && !FrontEndMenuManager.m_bMenuActive) { // OnSetFocus + FrontEndMenuManager.m_bActivateMenuNextFrame = true; + } +#ifdef FIX_BUGS + break; +#else + return 0; +#endif + } + case WM_KILLFOCUS: { // 0x748054 + ForegroundApp = false; +#ifdef FIX_BUGS + break; +#else + return 0; +#endif + } + case WM_GRAPHNOTIFY: { //< 0x74842A - Dispatched from VideoPlayer::Play + switch (gGameState) { + case GAME_STATE_PLAYING_INTRO: + case GAME_STATE_PLAYING_LOGO: { + VideoPlayer::OnGraphNotify(); + break; + } + } +#ifdef FIX_BUGS + break; +#else + return 0; +#endif + } + case WM_DEVICECHANGE: { //> 0x748282 - Handle AudioHardware DVD removal + if (wParam != DBT_DEVICEREMOVECOMPLETE) { + break; + } + const auto eparams = reinterpret_cast(lParam); + if (eparams->dbch_devicetype != DBT_DEVTYP_VOLUME) { + break; + } + const auto idev = reinterpret_cast(lParam); + if ((idev->dbcv_flags & DBTF_MEDIA) == 0) { // Not a media drive? + break; + } + if (!AEAudioHardware.m_bInitialised || !AEAudioHardware.IsStreamingFromDVD()) { + break; + } + const auto dvletter = AEAudioHardware.GetDVDDriveLetter(); + if (dvletter < 'A' || (idev->dbcv_unitmask & (1 << dvletter)) == 0) { + break; + } + DEV_LOG("About to check CD drive"); + CTimer::SetCodePause(true); + if (CCutsceneMgr::IsRunning()) { + CCutsceneMgr::SkipCutscene(); + } + while (!AEAudioHardware.CheckDVD()) { + FrontEndMenuManager.NoDiskInDriveMessage(); + if (FrontEndMenuManager.m_bQuitGameNoDVD) { + DEV_LOG("Exiting game as Audio CD was not inserted"); + break; + } + } + DEV_LOG("GTA Audio DVD has been inserted"); + CTimer::SetCodePause(false); + break; + } + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +void InjectHooksWndProcStuff() { + RH_ScopedCategory("Win"); + RH_ScopedNamespaceName("Win"); + + RH_ScopedGlobalInstall(GTATranslateShiftKey, 0x747CD0); + RH_ScopedGlobalInstall(GTATranslateKey, 0x747820); + RH_ScopedGlobalInstall(__MainWndProc, 0x747EB0, {.locked = true}); // Locked because of ImGui +} diff --git a/source/app/platform/win/WndProc.h b/source/app/platform/win/WndProc.h new file mode 100644 index 0000000000..957734b29e --- /dev/null +++ b/source/app/platform/win/WndProc.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void InjectHooksWndProcStuff(); diff --git a/source/app/platform/win/win.cpp b/source/app/platform/win/win.cpp deleted file mode 100644 index bbed5c5804..0000000000 --- a/source/app/platform/win/win.cpp +++ /dev/null @@ -1,854 +0,0 @@ -#include "StdInc.h" - -#include -#include - -#include "win.h" -#include "imgui_impl_win32.h" -#include "dshow.h" - -#include "VideoPlayer.h" -#include "Gamma.h" - -// #include "InputEvents.h" -#include "platform.h" - -#include "PostEffects.h" -#include "Clouds.h" -#include "Skidmarks.h" -#include "LoadingScreen.h" -#include "VideoMode.h" -#include "ControllerConfigManager.h" -#include "Input.h" -#include "MenuManager.h" -#include - -#include "extensions/Configs/FastLoader.hpp" - -extern void WinPsInjectHooks(); - -static LPSTR AppClassName = LPSTR(APP_CLASS); - -constexpr auto NO_FOREGROUND_PAUSE = true; - -// Dear ImGui said I have to copy this here -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - -// Custom enum, generated by ChatGPT, thank you -enum class WinVer { - WIN_95, //< Windows 95 - WIN_98, //< Windows 98 - WIN_ME, //< Windows ME - WIN_NT_4, //< Windows NT 4.0 - WIN_2000_XP_2003, //< Windows 2000/XP/Server 2003 - WIN_VISTA_OR_LATER, //< Windows Vista/7/8/10 - - UNKNOWN -}; - -static auto& s_WinVer = StaticRef(); - -// forward declarations -bool IsAlreadyRunning(); -char** CommandLineToArgv(char* cmdLine, int* argCount); -LRESULT CALLBACK __MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -INT WINAPI __WinMain(HINSTANCE instance, HINSTANCE hPrevInstance, LPSTR cmdLine, INT nCmdShow); - -// @notsa -// @brief Resets the screen gamma if ever changed. -void ResetGammaWhenExiting() { - if (gbGammaChanged) { - if (auto d3dDevice = (IDirect3DDevice9*)RwD3D9GetCurrentD3DDevice()) { - d3dDevice->SetGammaRamp(0u, D3DSGR_CALIBRATE, &savedGamma); - } - gbGammaChanged = false; - } -} - -// 0x746870 -void MessageLoop() { - tagMSG msg; - while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE | PM_NOYIELD)) { - if (msg.message == WM_QUIT) { - RsGlobal.quit = true; - } else { - TranslateMessage(&msg); - DispatchMessageA(&msg); - } - } -} - -// 0x7486A0 -bool InitApplication(HINSTANCE hInstance) { - WNDCLASS windowClass = { 0 }; - windowClass.style = CS_BYTEALIGNWINDOW; - windowClass.lpfnWndProc = __MainWndProc; - windowClass.hInstance = hInstance; - windowClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON)); - windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); - windowClass.lpszClassName = AppClassName; - return RegisterClass(&windowClass); -} - -// 0x745560 -HWND InitInstance(HINSTANCE hInstance) { - RECT rect = { 0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight }; - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false); - - return CreateWindowEx( - 0, - APP_CLASS, - RsGlobal.appName, - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, - CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - nullptr, - nullptr, - hInstance, - nullptr - ); -} - -// 0x7468E0 -bool IsAlreadyRunning() { - CreateEventA(nullptr, false, true, AppClassName); - if (GetLastError() != ERROR_ALREADY_EXISTS) { - return false; - } - - HWND window = FindWindowA(AppClassName, RsGlobal.appName); - if (window) - SetForegroundWindow(window); - else - SetForegroundWindow(PSGLOBAL(window)); - - return true; -}; - -// 0x746060 -bool IsForegroundApp() { - return ForegroundApp; -} - -// 0x746480 -char** CommandLineToArgv(char* cmdLine, int* argCount) { - return plugin::CallAndReturn(cmdLine, argCount); -} - -// 0x747820 -BOOL GTATranslateKey(RsKeyCodes* ck, LPARAM lParam, UINT vk) { - *ck = [&] { - // Handle extended keys - const auto Ext = [kf = HIWORD(lParam)](RsKeyCodes extended, RsKeyCodes unextended) { - return (kf & KF_EXTENDED) ? extended : unextended; - }; - - switch (vk) { - case VK_RETURN: return Ext(rsPADENTER, rsENTER); - case VK_CONTROL: return Ext(rsRCTRL, rsLCTRL); - case VK_MENU: return Ext(rsRALT, rsLALT); - case VK_PRIOR: return Ext(rsPGUP, rsPADPGUP); - case VK_NEXT: return Ext(rsPGDN, rsPADPGDN); - case VK_END: return Ext(rsEND, rsPADEND); - case VK_HOME: return Ext(rsHOME, rsPADHOME); - case VK_LEFT: return Ext(rsLEFT, rsPADLEFT); - case VK_UP: return Ext(rsUP, rsPADUP); - case VK_RIGHT: return Ext(rsRIGHT, rsPADRIGHT); - case VK_DOWN: return Ext(rsDOWN, rsPADDOWN); - case VK_INSERT: return Ext(rsINS, rsPADINS); - case VK_DELETE: return Ext(rsDEL, rsPADDEL); - case VK_BACK: return rsBACKSP; - case VK_TAB: return rsTAB; - case VK_PAUSE: return rsPAUSE; - case VK_CAPITAL: return rsCAPSLK; - case VK_ESCAPE: return rsESC; - case VK_LWIN: return rsLWIN; - case VK_RWIN: return rsRWIN; - case VK_APPS: return rsAPPS; - case VK_NUMPAD0: return rsPADINS; - case VK_NUMPAD1: return rsPADEND; - case VK_NUMPAD2: return rsPADDOWN; - case VK_NUMPAD3: return rsPADPGDN; - case VK_NUMPAD4: return rsPADLEFT; - case VK_NUMPAD5: return rsPAD5; - case VK_NUMPAD6: return rsPADRIGHT; - case VK_NUMPAD7: return rsPADHOME; - case VK_NUMPAD8: return rsPADUP; - case VK_NUMPAD9: return rsPADPGUP; - case VK_MULTIPLY: return rsTIMES; - case VK_ADD: return rsPLUS; - case VK_SUBTRACT: return rsMINUS; - case VK_DECIMAL: return rsPADDEL; - case VK_DIVIDE: return rsDIVIDE; - case VK_F1: return rsF1; - case VK_F2: return rsF2; - case VK_F3: return rsF3; - case VK_F4: return rsF4; - case VK_F5: return rsF5; - case VK_F6: return rsF6; - case VK_F7: return rsF7; - case VK_F8: return rsF8; - case VK_F9: return rsF9; - case VK_F10: return rsF10; - case VK_F11: return rsF11; - case VK_F12: return rsF12; - case VK_NUMLOCK: return rsNUMLOCK; - case VK_SCROLL: return rsSCROLL; - case VK_SHIFT: { - return s_WinVer == WinVer::WIN_98 // Will be handled later - ? rsSHIFT - : rsNULL; - } - default: { // Try mapping to regular ASCII char - const auto chr = MapVirtualKey(vk, MAPVK_VK_TO_CHAR); - if (chr <= 0xFF) { - return (RsKeyCodes)(chr); - } - break; - } - } - return rsNULL; - }(); - return *ck != rsNULL; -} - -/*! -* Process shift keys. -* Unless Win98, in which case `GTATranslateKey` should handle it. -* @addr 0x747CD0 -*/ -BOOL GTATranslateShiftKey(RsKeyCodes*) { // The in keycode is ignored, so we won't bother - if (s_WinVer == WinVer::WIN_98) { - return false; // Already handled by `GTATranslateKey` - } - - constexpr struct { RsKeyCodes ck; INT vk; } Keys[]{ - {rsLSHIFT, VK_LSHIFT}, - {rsRSHIFT, VK_RSHIFT}, - }; - - for (auto shouldBeDown : { false, true }) { - for (auto [ck, vk] : Keys) { - // GetKeyState reads from the message queue, - // so we must call it like the og code - const auto isDown = (HIWORD(GetKeyState(vk)) & 0x80) == 1; // Check is key pressed - if (isDown == shouldBeDown) { - RsEventHandler( - isDown ? rsKEYDOWN : rsKEYUP, - &ck - ); - } - } - } - - return true; -} - -// 0x747EB0 -LRESULT CALLBACK __MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - const auto imio = ImGui::GetCurrentContext() ? &ImGui::GetIO() : nullptr; - if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam)) { - return true; - } - - switch (uMsg) { - case WM_SETCURSOR: { - ShowCursor(false); - SetCursor(NULL); - break; - } - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: { //< 0x74823B - wParam is a `VK_` (virtual key), lParam are the flags (See https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keyup)] - if (imio && imio->WantCaptureKeyboard) { - return 0; - } - - if (RsKeyCodes ck; GTATranslateKey(&ck, lParam, wParam)) { - RsKeyboardEventHandler( - (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) - ? rsKEYDOWN - : rsKEYUP, - &ck - ); - } - if (wParam == VK_SHIFT) { - RsKeyCodes ck; - GTATranslateShiftKey(&ck); // Original code uses this variable for storage, so we can't pass in nullptr - TODO: Remove parameter - } - return 0; - } - case WM_MOUSEMOVE: { //< 0x748323 - if (imio && imio->WantCaptureMouse) { - return 0; - } - FrontEndMenuManager.m_nMousePosWinX = GET_X_LPARAM(lParam); - FrontEndMenuManager.m_nMousePosWinY = GET_Y_LPARAM(lParam); - break; - } - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_MBUTTONDOWN: { - if (imio && imio->WantCaptureMouse) { - return 0; - } - SetCapture(hWnd); - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: { - if (imio && imio->WantCaptureMouse) { - return 0; - } - ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - return 0; - case WM_SIZING: { // 0x74829E - if (RwInitialized) { - if (gGameState == GAME_STATE_IDLE) { - RsEventHandler(rsIDLE, (void*)TRUE); - } - } - - const auto wndrect = reinterpret_cast(lParam); - VERIFY(SetWindowPos( - hWnd, - HWND_TOP, - 0, 0, - wndrect->right - wndrect->left, wndrect->bottom - wndrect->top, - SWP_NOSIZE | SWP_NOMOVE - )); - - CPostEffects::SetupBackBufferVertex(); - - return 0; - } - case WM_ACTIVATEAPP: { // 0x748087 - const auto wndBeingActivated = !!wParam; - - //> 0x748087 - Set gamma (If changed) - if (gbGammaChanged) { - if (const auto dev = RwD3D9GetCurrentD3DDevice()) { - dev->SetGammaRamp(9, 0, wndBeingActivated ? &gammaTable : &savedGamma); - } - } - - // - switch (gGameState) { - case GAME_STATE_PLAYING_LOGO: - case GAME_STATE_PLAYING_INTRO: { - const auto mc = VideoPlayer::GetMediaControl(); - - OAFilterState state; - VERIFY(SUCCEEDED(mc->GetState(10, &state))); - if (wndBeingActivated) { - VideoPlayer::UpdateWindow(); - if (state == State_Running) { - break; - } - VERIFY(SUCCEEDED(mc->Run())); - VideoPlayer::WaitState(State_Running); - SetFocus(PSGLOBAL(window)); - } else if (state == State_Running) { // Window is being deactivatd, pause media - VERIFY(SUCCEEDED(mc->Pause())); - } - - break; - } - case GAME_STATE_INITIAL: { - if (!wndBeingActivated) { // 0x748171 - if (PSGLOBAL(fullScreen)) { - Windowed = true; // ??? - } - } - break; - } - } - - //> 0x748183 Clear pads - for (auto& pad : CPad::Pads) { - pad.Clear(false, true); - } - - return 0; - } - case WM_DESTROY: - case WM_CLOSE: { // 0x747EF3 - VERIFY(ClipCursor(nullptr)); - VERIFY(SUCCEEDED(WinInput::Shutdown())); - PostQuitMessage(0); - return 0; - } - case WM_SIZE: { // 0x747F04 - // Figure out new size - const auto width = LOWORD(lParam), height = HIWORD(lParam); - RwRect wndsz{ - .x = 0, .y = 0, - .w = width, .h = height, - }; - - // Nothing to do? - if (!RwInitialized || !wndsz.h || !wndsz.w) { - return 0; - } - - // Try resizing the camera - RsEventHandler(rsCAMERASIZE, &wndsz); - - // Check if it has failed - if (wndsz.w != width && wndsz.h != height) { // TODO/BUG: Shouldnt `&&` instead be `||`? - VERIFY(ReleaseCapture()); - - WINDOWPLACEMENT wndpl; - VERIFY(GetWindowPlacement(PSGLOBAL(window), &wndpl)); - - if (wndpl.showCmd == SW_SHOWMAXIMIZED) { - SendMessage(PSGLOBAL(window), WM_WINDOWPOSCHANGED, 0, 0); - } - } - - return 0; - } - case WM_ACTIVATE: { // 0x747FA3 - const auto wndBeingActivated = !!wParam; - - if (wndBeingActivated) { - CAudioEngine::ResumeAllSounds(); - ShowCursor(FALSE); - } else { - CAudioEngine::PauseAllSounds(); - VERIFY(SUCCEEDED(SetTimer(PSGLOBAL(window), 1, (UINT)(1000.f / RsGlobal.frameLimit), nullptr))); - } - - ForegroundApp = wndBeingActivated; - RsEventHandler(rsACTIVATE, (void*)wndBeingActivated); - - //> 0x74800C Clear pads - for (auto& pad : CPad::Pads) { - pad.Clear(false, true); - } - -#ifdef FIX_BUGS - break; -#else - return 0; -#endif - } - case WM_SETFOCUS: { // 0x748063 - ForegroundApp = true; - if (!FrontEndMenuManager.m_bMainMenuSwitch && !FrontEndMenuManager.m_bMenuActive) { // OnSetFocus - FrontEndMenuManager.m_bActivateMenuNextFrame = true; - } -#ifdef FIX_BUGS - break; -#else - return 0; -#endif - } - case WM_KILLFOCUS: { // 0x748054 - ForegroundApp = false; -#ifdef FIX_BUGS - break; -#else - return 0; -#endif - } - case WM_GRAPHNOTIFY: { //< 0x74842A - Dispatched from VideoPlayer::Play - switch (gGameState) { - case GAME_STATE_PLAYING_INTRO: - case GAME_STATE_PLAYING_LOGO: { - VideoPlayer::OnGraphNotify(); - break; - } - } -#ifdef FIX_BUGS - break; -#else - return 0; -#endif - } - case WM_DEVICECHANGE: { //> 0x748282 - Handle AudioHardware DVD removal - if (wParam != DBT_DEVICEREMOVECOMPLETE) { - break; - } - const auto eparams = reinterpret_cast(lParam); - if (eparams->dbch_devicetype != DBT_DEVTYP_VOLUME) { - break; - } - const auto idev = reinterpret_cast(lParam); - if ((idev->dbcv_flags & DBTF_MEDIA) == 0) { // Not a media drive? - break; - } - if (!AEAudioHardware.m_bInitialised || !AEAudioHardware.IsStreamingFromDVD()) { - break; - } - const auto dvletter = AEAudioHardware.GetDVDDriveLetter(); - if (dvletter < 'A' || (idev->dbcv_unitmask & (1 << dvletter)) == 0) { - break; - } - DEV_LOG("About to check CD drive"); - CTimer::SetCodePause(true); - if (CCutsceneMgr::IsRunning()) { - CCutsceneMgr::SkipCutscene(); - } - while (!AEAudioHardware.CheckDVD()) { - FrontEndMenuManager.NoDiskInDriveMessage(); - if (FrontEndMenuManager.m_bQuitGameNoDVD) { - DEV_LOG("Exiting game as Audio CD was not inserted"); - break; - } - } - DEV_LOG("GTA Audio DVD has been inserted"); - CTimer::SetCodePause(false); - break; - } - } - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -// Code from winmain, 0x748DCF -bool ProcessGameLogic(INT nCmdShow, MSG& Msg) { - if (RsGlobal.quit || FrontEndMenuManager.m_bStartGameLoading) { - return false; - } - - // Process Window messages - if (PeekMessage(&Msg, nullptr, 0u, 0u, PM_REMOVE | PM_NOYIELD)) { - if (Msg.message == WM_QUIT) { - return false; - } - TranslateMessage(&Msg); - DispatchMessage(&Msg); - return true; - } - - // Game is in background - if (!NO_FOREGROUND_PAUSE && !ForegroundApp) { - if (isForeground) { - isForeground = false; - } - Sleep(100); - return true; - } - - // TODO: Move this out from here (It's not platform specific at all) - switch (gGameState) { - case GAME_STATE_INITIAL: { - const auto ProcessSplash = [](bool isNVidia) { - CLoadingScreen::LoadSplashes(true, isNVidia); - CLoadingScreen::Init(true, true); - CLoadingScreen::DoPCTitleFadeOut(); - CLoadingScreen::DoPCTitleFadeIn(); - CLoadingScreen::Shutdown(); - }; - if (!g_FastLoaderConfig.NoEAX) { - ProcessSplash(false); - } - if (!g_FastLoaderConfig.NoNVidia) { - ProcessSplash(true); - } - ChangeGameStateTo(GAME_STATE_LOGO); - break; - } - case GAME_STATE_LOGO: { - if (!g_FastLoaderConfig.NoLogo) { - if (!Windowed) { - VideoPlayer::Play(nCmdShow, "movies\\Logo.mpg"); - } - } - ChangeGameStateTo(g_FastLoaderConfig.NoLogo ? GAME_STATE_TITLE : GAME_STATE_PLAYING_LOGO); - break; - } - case GAME_STATE_PLAYING_LOGO: - case GAME_STATE_PLAYING_INTRO: { // 0x748B17 - CPad::UpdatePads(); - auto* pad = CPad::GetPad(); - if ( Windowed - || ControlsManager.GetJoyButtonJustDown() - || pad->NewState.CheckForInput() - || CPad::IsMouseLButtonPressed() - || CPad::IsEnterJustPressed() - || pad->IsStandardKeyJustPressed(VK_SPACE) - || CPad::IsMenuKeyJustPressed() - || CPad::IsTabJustPressed() - ) { - ChangeGameStateTo([] { - switch (gGameState) { - case GAME_STATE_PLAYING_LOGO: return GAME_STATE_TITLE; - case GAME_STATE_PLAYING_INTRO: return GAME_STATE_FRONTEND_LOADING; - default: NOTSA_UNREACHABLE(); - } - }()); - } - break; - } - case GAME_STATE_TITLE: { - if (!g_FastLoaderConfig.NoTitleOrIntro) { - VideoPlayer::Shutdown(); - VideoPlayer::Play(nCmdShow, FrontEndMenuManager.GetMovieFileName()); - } - ChangeGameStateTo(g_FastLoaderConfig.NoTitleOrIntro ? GAME_STATE_FRONTEND_LOADING : GAME_STATE_PLAYING_INTRO); - break; - } - case GAME_STATE_FRONTEND_LOADING: { - VideoPlayer::Shutdown(); - CLoadingScreen::Init(true, false); - if (!g_FastLoaderConfig.NoCopyright) { - CLoadingScreen::DoPCTitleFadeOut(); - } - if (!CGame::InitialiseEssentialsAfterRW()) { - RsGlobal.quit = true; - } - CGame::InitialiseCoreDataAfterRW(); - ChangeGameStateTo(GAME_STATE_FRONTEND_LOADED); - anisotropySupportedByGFX = (RwD3D9GetCaps()->RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0; // todo: func - break; - } - case GAME_STATE_FRONTEND_LOADED: { - FrontEndMenuManager.m_bActivateMenuNextFrame = true; - FrontEndMenuManager.m_bMainMenuSwitch = true; - if (VideoModeNotSelected) { - FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode = gCurrentVideoMode; - VideoModeNotSelected = false; - } - ChangeGameStateTo(GAME_STATE_FRONTEND_IDLE); - if (g_FastLoaderConfig.NoCopyright) { - CLoadingScreen::SkipCopyrightSplash(); - } else { - CLoadingScreen::DoPCTitleFadeIn(); - } - break; - } - case GAME_STATE_FRONTEND_IDLE: { // 0x748CB2 - WINDOWPLACEMENT wndpl{ .length = sizeof(WINDOWPLACEMENT) }; - VERIFY(GetWindowPlacement(PSGLOBAL(window), &wndpl)); - if (g_FastLoaderConfig.ShouldLoadSaveGame()) { - RsEventHandler(rsFRONTENDIDLE, nullptr); // We need to still run the frontend processing once because it has some important stuff - if ((GetAsyncKeyState(g_FastLoaderConfig.SkipSaveGameLoadKey) & 0xF000) == 0) { - g_FastLoaderConfig.StartGame(g_FastLoaderConfig.SaveGameToLoad); // Load game - } - g_FastLoaderConfig.TriedLoadingSaveGame = true; - } else if (g_FastLoaderConfig.RenderAtAllTimes || wndpl.showCmd != SW_SHOWMINIMIZED) { - RsEventHandler(rsFRONTENDIDLE, nullptr); - } - if (FrontEndMenuManager.m_bMenuActive && !FrontEndMenuManager.m_bLoadingData) { - break; - } - ChangeGameStateTo(GAME_STATE_LOADING_STARTED); - if (!FrontEndMenuManager.m_bLoadingData) { - break; - } - NOTSA_SWCFALLTHRU; // Fall down and start loading - } - case GAME_STATE_LOADING_STARTED: { - if (!g_FastLoaderConfig.NoLoadingTune) { - AudioEngine.StartLoadingTune(); - } - - InitialiseGame(); - ChangeGameStateTo(GAME_STATE_IDLE); - FrontEndMenuManager.m_bMainMenuSwitch = false; - - AudioEngine.InitialisePostLoading(); - break; - } - case GAME_STATE_IDLE: { - if (!RwInitialized) - break; - - auto v9_1 = 1000.0f / (float)RsGlobal.frameLimit; - auto v9_2 = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); - if (!FrontEndMenuManager.m_bPrefsFrameLimiter && CReplay::Mode != eReplayMode::MODE_PLAYBACK && !AudioEngine.IsBeatInfoPresent() || v9_1 < v9_2) { - RsEventHandler(rsIDLE, (void*)true); - } - break; - } - } - - if (!isForeground) { - isForeground = true; - } - - return true; -} - -// Code from winmain, 0x7489FB -void MainLoop(INT nCmdShow, MSG& Msg) { - bool bNewGameFirstTime = false; - while (true) { - RwInitialized = true; - - RwV2d pos{ SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f }; - RsMouseSetPos(&pos); - - gamma.Init(); - - // Game logic main loop - while (ProcessGameLogic(nCmdShow, Msg)); - - // 0x748DDA - RwInitialized = false; - FrontEndMenuManager.UnloadTextures(); - if (!FrontEndMenuManager.m_bStartGameLoading) { - break; - } - - // load game - CCheat::ResetCheats(); - CTimer::Stop(); - - if (FrontEndMenuManager.m_bLoadingData) { - CGame::ShutDownForRestart(); - CGame::InitialiseWhenRestarting(); - FrontEndMenuManager.m_bLoadingData = false; - } else if (bNewGameFirstTime) { - CTimer::Stop(); - ChangeGameStateTo( - FrontEndMenuManager.m_nGameState != 1 - ? GAME_STATE_LOADING_STARTED - : GAME_STATE_FRONTEND_LOADED - ); - } else { - CCheat::ResetCheats(); - CGame::ShutDownForRestart(); - CTimer::Stop(); - CGame::InitialiseWhenRestarting(); - } - - bNewGameFirstTime = false; - FrontEndMenuManager.m_nGameState = 0; - FrontEndMenuManager.m_bStartGameLoading = false; - } - -} - -// 0x748710 -INT WINAPI __WinMain(HINSTANCE instance, HINSTANCE hPrevInstance, LPSTR cmdLine, INT nCmdShow) { - SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0u, nullptr, 2); - if (IsAlreadyRunning()) { - return false; - } - - auto initializeEvent = RsEventHandler(rsINITIALIZE, nullptr); - if (rsEVENTERROR == initializeEvent) { - return false; - } - - if (!InitApplication(instance)) { - return false; - } - - cmdLine = GetCommandLine(); - int argc; - char** argv = CommandLineToArgv(cmdLine, &argc); - for (int i = 0; i < argc; i++) { - RsEventHandler(rsPREINITCOMMANDLINE, argv[i]); - } - - PSGLOBAL(window) = InitInstance(instance); - if (!PSGLOBAL(window)) { - return false; - } - PSGLOBAL(instance) = instance; - - // 0x7487CF - VERIFY(WinInput::Initialise()); - - ControlsManager.InitDefaultControlConfigMouse(WinInput::GetMouseState(), !FrontEndMenuManager.m_nController); - - // 0x748847 - if (RsEventHandler(rsRWINITIALIZE, PSGLOBAL(window)) == rsEVENTERROR) { - DestroyWindow(PSGLOBAL(window)); - RsEventHandler(rsTERMINATE, nullptr); - return false; - } - - // 0x7488EE - for (auto i = 0; i < argc; i++) { - RsEventHandler(rsCOMMANDLINE, argv[i]); - } - - if (MultipleVideoModes || PSGLOBAL(fullScreen)) { - SetWindowLongPtr(PSGLOBAL(window), GWL_STYLE, (LONG_PTR)WS_POPUP); - SetWindowPos(PSGLOBAL(window), nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); - } - - RwRect rect{ 0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight }; - RsEventHandler(rsCAMERASIZE, &rect); - - SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0u, nullptr, 2u); - SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0u, nullptr, 2u); - SystemParametersInfo(SPI_SETLOWPOWERACTIVE, 0u, nullptr, 2u); - STICKYKEYS pvParam { .cbSize = sizeof(STICKYKEYS) }; - SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam, 2u); - STICKYKEYS pvParam1 = { .cbSize = sizeof(STICKYKEYS), .dwFlags = SKF_TWOKEYSOFF }; - SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam1, 2u); - - ShowWindow(PSGLOBAL(window), nCmdShow); - UpdateWindow(PSGLOBAL(window)); - - // 0x748995 - CFileMgr::SetDirMyDocuments(); - if (auto* file = CFileMgr::OpenFile("gta_sa.set", "rb")) { - if (!ControlsManager.LoadSettings(file)) { - ControlsManager.ReinitControls(); - } - CFileMgr::CloseFile(file); - } - CFileMgr::SetDir(""); - - SetErrorMode(SEM_FAILCRITICALERRORS); - - // 0x7489FB - MSG Msg; - MainLoop(nCmdShow, Msg); - - // if game is loaded, shut it down - if (gGameState == GAME_STATE_IDLE) { - CGame::Shutdown(); - } - - // now quit 0x748E75 - AudioEngine.Shutdown(); - FreeVideoModeList(); - RsEventHandler(rsRWTERMINATE, nullptr); - DestroyWindow(PSGLOBAL(window)); - RsEventHandler(rsTERMINATE, nullptr); - free(argv); - ShowCursor(true); - - SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &pvParam, 2u); - SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1u, nullptr, 2u); // TODO: GUID_VIDEO_POWERDOWN_TIMEOUT - SystemParametersInfo(SPI_SETLOWPOWERACTIVE, 1u, nullptr, 2u); - SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1u, nullptr, 2u); - // nullsub_0x72F3C0() - SetErrorMode(0); - - return Msg.wParam; -} - -void Win32InjectHooks() { - RH_ScopedCategory("Win"); - RH_ScopedNamespaceName("Win"); - - RH_ScopedGlobalInstall(IsForegroundApp, 0x746060); - RH_ScopedGlobalInstall(IsAlreadyRunning, 0x7468E0); - RH_ScopedGlobalInstall(CommandLineToArgv, 0x746480, {.reversed = false}); - - RH_ScopedGlobalInstall(GTATranslateShiftKey, 0x747CD0); - RH_ScopedGlobalInstall(GTATranslateKey, 0x747820); - RH_ScopedGlobalInstall(__MainWndProc, 0x747EB0, { .locked = true }); - - // Unhooking these 2 after the game has started will do nothing - RH_ScopedGlobalInstall(__WinMain, 0x748710); - RH_ScopedGlobalInstall(InitInstance, 0x745560); - - WinPsInjectHooks(); - WinInput::InjectHooks(); -} diff --git a/source/app/platform/win/win.h b/source/app/platform/win/win.h deleted file mode 100644 index 8c54f816e3..0000000000 --- a/source/app/platform/win/win.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#define APP_CLASS "Grand theft auto San Andreas" - -//! Win32 fail check (returns from function automagically) -#define WIN_FCHECK(x) do { \ - if (HRESULT hr = (x); FAILED(hr)) { \ - DEV_LOG(TEXT("FAILED(hr=0x{:x}) in ") TEXT(#x) TEXT("\n"), (size_t)(hr)); \ - return; \ - } \ - } while (0) \ - -static bool& anisotropySupportedByGFX = *(bool*)0xC87FFC; -static bool& isForeground = *(bool*)0xC920EC; -static bool& Windowed = *(bool*)0xC920CC; - -void Win32InjectHooks(); - -#define IDD_DIALOG1 104 -#define IDC_DEVICESEL 1000 -#define IDC_VIDMODE 1001 -#define IDEXIT 1002 -#define IDC_SELECTDEVICE 1005 - -#define IDI_MAIN_ICON 1042 diff --git a/source/app/platform/win/win_impl.cpp b/source/app/platform/win/win_impl.cpp deleted file mode 100644 index 6194b48978..0000000000 --- a/source/app/platform/win/win_impl.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include "StdInc.h" - -#include "win.h" - -#include "platform.h" -#include "LoadingScreen.h" - -void WinPsInjectHooks() { - RH_ScopedCategory("Win"); - RH_ScopedNamespaceName("Ps"); - - RH_ScopedGlobalInstall(psInitialize, 0x747420, {.reversed = false}); - RH_ScopedGlobalInstall(psTerminate, 0x7458A0); - RH_ScopedGlobalInstall(psWindowSetText, 0x7451B0); - RH_ScopedGlobalInstall(psErrorMessage, 0x7451D0); - RH_ScopedGlobalInstall(psWarningMessage, 0x7451F0); - RH_ScopedGlobalInstall(psCameraBeginUpdate, 0x745210); - RH_ScopedGlobalInstall(psCameraShowRaster, 0x745240); - RH_ScopedGlobalInstall(psTimer, 0x745270); - RH_ScopedGlobalInstall(psGrabScreen, 0x7452B0); - RH_ScopedGlobalInstall(psMouseSetVisibility, 0x7453E0); - RH_ScopedGlobalInstall(psMouseSetPos, 0x7453F0); - RH_ScopedGlobalInstall(psPathnameCreate, 0x745470); - RH_ScopedGlobalInstall(psPathnameDestroy, 0x7454E0); - RH_ScopedGlobalInstall(psPathGetSeparator, 0x745500); - RH_ScopedGlobalInstall(psInstallFileSystem, 0x745520); - RH_ScopedGlobalInstall(psNativeTextureSupport, 0x745530); - RH_ScopedGlobalInstall(psDebugMessageHandler, 0x745540); - RH_ScopedGlobalInstall(psAlwaysOnTop, 0x7458B0); - RH_ScopedGlobalInstall(psSelectDevice, 0x746190, {.reversed = false}); -} - -// 0x747420 -bool psInitialize() { - return plugin::CallAndReturn(); -} - -// 0x7458A0 -void psTerminate() { - // NOP -} - -// 0x7451B0 -void psWindowSetText(const char* str) { - SetWindowTextA(PSGLOBAL(window), str); -} - -// 0x7451D0 -void psErrorMessage(const char* str) { - MessageBoxA(nullptr, str, RsGlobal.appName, MB_ICONERROR | MB_TASKMODAL | MB_TOPMOST); -} - -// 0x7451F0 -void psWarningMessage(const char* str) { - MessageBoxA(nullptr, str, RsGlobal.appName, MB_ICONWARNING | MB_TASKMODAL | MB_TOPMOST); -} - -// 0x745210 -bool psCameraBeginUpdate(RwCamera* camera) { - if (RwCameraBeginUpdate(Scene.m_pRwCamera)) { - return true; - } - RsEventHandler(rsACTIVATE, nullptr); - return false; -} - -// 0x745240 -RwCamera* psCameraShowRaster(RwCamera* camera) { - auto flags = FrontEndMenuManager.m_bPrefsFrameLimiter || CLoadingScreen::m_bActive ? rwRASTERFLIPWAITVSYNC : rwRASTERFLIPDONTWAIT; - return RwCameraShowRaster(camera, PSGLOBAL(window), flags); -} - -// 0x745270 -uint32 psTimer() { - return OS_TimeMS(); -} - -// 0x7452B0 -RwImage* psGrabScreen(RwCamera* camera) { - auto* device = static_cast(RwD3D9GetCurrentD3DDevice()); - assert(device); - - D3DDISPLAYMODE displayMode{}; - VERIFY(SUCCEEDED(device->GetDisplayMode(0, &displayMode))); - - IDirect3DSurface9* surface = nullptr; - VERIFY(SUCCEEDED(device->CreateOffscreenPlainSurface(displayMode.Width, displayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr))); - VERIFY(SUCCEEDED(device->GetFrontBufferData(0, surface))); - - D3DLOCKED_RECT lockedRect{}; -#ifndef FIX_BUGS - // It's not needed as ClientToScreen func works with fullscreen mode. - if (PSGLOBAL(fullScreen)) { // todo: Doesn't work properly with III.VC.SA.WindowedMode.asi - VERIFY(SUCCEEDED(surface->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY))); - } else { -#endif - RECT rect; -#ifdef FIX_BUGS - // SA code gets the whole window of the game, which includes the titlebar etc. - // - // BUG: There should be bugs for older versions of Windows IIRC. - // One example would be Vista version of the func doesn't count Aero effects of windows. - // - // TODO: Test with dual monitors etc. - - GetClientRect(PSGLOBAL(window), &rect); - - // GetClientRect returns relative positions unlike GetWindowRect. - // i.e. will return { 0, 0, width, height }. - ClientToScreen(PSGLOBAL(window), (POINT*)(&rect)); // kinda hacky but should work. - rect.right += rect.left; - rect.bottom += rect.top; -#else - GetWindowRect(PSGLOBAL(window), &rect); -#endif - displayMode.Height = rect.bottom - rect.top; - displayMode.Width = rect.right - rect.left; - VERIFY(SUCCEEDED(surface->LockRect(&lockedRect, &rect, D3DLOCK_READONLY))); -#ifndef FIX_BUGS - } -#endif - - RwImage* image = RwImageCreate(int32(displayMode.Width), int32(displayMode.Height), 32); - if (image) { - RwImageAllocatePixels(image); - - auto* pixels = (RwRGBA*)RwImageGetPixels(image); - auto* imagePixels = (uint8*)lockedRect.pBits; - assert(pixels && imagePixels); - - for (auto h = 0u; h < displayMode.Height; h++) { - for (auto w = 0u; w < displayMode.Width; w++) { - pixels->red = imagePixels[sizeof(RwRGBA) * w + 2]; - pixels->green = imagePixels[sizeof(RwRGBA) * w + 1]; - pixels->blue = imagePixels[sizeof(RwRGBA) * w + 0]; - pixels->alpha = 255; - pixels++; - } - imagePixels += lockedRect.Pitch; - } - } - - { // FIX_BUGS - surface->UnlockRect(); - surface->Release(); - delete surface; - } - - return image; -} - -// 0x7453E0 -void psMouseSetVisibility(bool visible) { - ::ShowCursor(visible); -} - -// 0x7453F0 -void psMouseSetPos(RwV2d* pos) { - POINT point = { .x = LONG(pos->x), .y = LONG(pos->y) }; - ::ClientToScreen(PSGLOBAL(window), &point); - ::SetCursorPos(point.x, point.y); - PSGLOBAL(lastMousePos) = *pos; -} - -// 0x745470 -char* psPathnameCreate(const char* buffer) { - const auto pathSize = std::strlen(buffer) + 1u; - auto path = (char*)CMemoryMgr::Malloc(pathSize); - if (path) { - strcpy_s(path, pathSize, buffer); - - while (auto ch = std::strchr(path, '/')) { - *ch = psPathGetSeparator(); - } - } - - return path; -} - -// 0x7454E0 -void psPathnameDestroy(char* buffer) { - if (buffer) { - RwFree(buffer); - } -} - -// 0x745500 -char psPathGetSeparator() { - return '\\'; -} - -// 0x745520 -bool psInstallFileSystem() { - return true; -} - -// 0x745530 -bool psNativeTextureSupport() { - return RwD3D9DeviceSupportsDXTTexture(); -} - -// 0x745540 -RsEventStatus psDebugMessageHandler(RsEvent event, void* param) { - if (rsINITDEBUG == event) { - RwDebugSetHandler(psDebugMessageHandler); -#if defined(DEBUG) && defined(RWTRACE) - RwDebugSetTraceState(true); -#else - RwDebugSetTraceState(false); -#endif - } - - return rsEVENTNOTPROCESSED; -} - -// 0x7458B0 -bool psAlwaysOnTop(bool alwaysOnTop) { - RECT winRect; - - HWND hwnd = PSGLOBAL(window); - GetWindowRect(hwnd, &winRect); - - if (alwaysOnTop) { - return (bool)SetWindowPos(hwnd, HWND_TOPMOST, winRect.left, winRect.top, winRect.right - winRect.left, winRect.bottom - winRect.top, 0); - } else { - return (bool)SetWindowPos(hwnd, HWND_NOTOPMOST, winRect.left, winRect.top, winRect.right - winRect.left, winRect.bottom - winRect.top, 0); - } -} - -// 0x746190 -bool psSelectDevice() { - return plugin::CallAndReturn(); -} diff --git a/source/game_sa/ControllerConfigManager.cpp b/source/game_sa/ControllerConfigManager.cpp index c7f6c8b54c..117c975f0c 100644 --- a/source/game_sa/ControllerConfigManager.cpp +++ b/source/game_sa/ControllerConfigManager.cpp @@ -18,6 +18,9 @@ void CControllerConfigManager::InjectHooks() { RH_ScopedInstall(SetMouseButtonAssociatedWithAction, 0x52F590); RH_ScopedInstall(StoreMouseButtonState, 0x52DA30, { .reversed = false }); RH_ScopedInstall(UpdateJoyInConfigMenus_ButtonDown, 0x52DAB0, { .reversed = false }); + RH_ScopedInstall(UpdateJoy_ButtonDown, 0x530F42); + RH_ScopedInstall(UpdateJoy_ButtonUp, 0x531070); + RH_ScopedInstall(StoreJoyButtonStates, 0x52F510); RH_ScopedInstall(AffectControllerStateOn_ButtonDown_DebugStuff, 0x52DC10, { .reversed = false }); RH_ScopedInstall(UpdateJoyInConfigMenus_ButtonUp, 0x52DC20, { .reversed = false }); RH_ScopedInstall(AffectControllerStateOn_ButtonUp_DebugStuff, 0x52DD80, { .reversed = false }); @@ -50,6 +53,35 @@ CControllerConfigManager* CControllerConfigManager::Constructor() { return this; } +// 0x52F510 +void CControllerConfigManager::StoreJoyButtonStates() { // Name unknown (I made it up) + for (auto&& [idx, bs] : notsa::enumerate(m_ButtonStates)) { + bs = m_NewJoyState.rgbButtons[idx] >> 7; + } +} + +// NOTSA [Code combined from 0x7448B0 and 0x744930] +void CControllerConfigManager::HandleJoyButtonUpDown(int32 joyNo, bool isDown) { + StoreJoyButtonStates(); + const auto forceConfigMenuMode = !isDown && notsa::contains({ MODE_FLYBY, MODE_FIXED }, TheCamera.GetActiveCamera().m_nMode); // Probably leftover debug stuff? + for (auto i = isDown ? 1u : 2u; i < std::size(m_ButtonStates); i++) { // TODO: Why is this starting from 1/2? + const auto padBtn = (ePadButton)((m_ButtonStates[i - 1] == isDown) ? i : 0); // This doesn't make sense + if (forceConfigMenuMode || FrontEndMenuManager.m_bMenuActive || joyNo != 0) { + if (isDown) { + UpdateJoyInConfigMenus_ButtonDown(padBtn, joyNo); + } else { + UpdateJoyInConfigMenus_ButtonUp(padBtn, joyNo); + } + } else { + if (isDown) { + UpdateJoy_ButtonDown(padBtn, 3); + } else { + UpdateJoy_ButtonUp(padBtn, 3); + } + } + } +} + // 0x530530 bool CControllerConfigManager::LoadSettings(FILESTREAM file) { return plugin::CallMethodAndReturn(this, file); @@ -143,6 +175,11 @@ void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonDown(ePadButton butt plugin::CallMethod<0x52DAB0, CControllerConfigManager*, ePadButton, int32>(this, button, padNumber); } +// 0x530F42 +void CControllerConfigManager::UpdateJoy_ButtonDown(ePadButton button, int32 unk) { + plugin::CallMethod<0x530F42>(this, button, unk); +} + // unused // 0x52DC10 void CControllerConfigManager::AffectControllerStateOn_ButtonDown_DebugStuff(int32, eControllerType) { @@ -154,6 +191,11 @@ void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonUp(ePadButton button plugin::CallMethod<0x52DC20, CControllerConfigManager*, ePadButton, int32>(this, button, padNumber); } +// 0x531070 +void CControllerConfigManager::UpdateJoy_ButtonUp(ePadButton button, int32 unk) { + plugin::CallMethod<0x531070>(this, button, unk); +} + // unused // 0x52DD80 void CControllerConfigManager::AffectControllerStateOn_ButtonUp_DebugStuff(int32, eControllerType) { @@ -190,7 +232,7 @@ bool CControllerConfigManager::GetIsKeyboardKeyJustDown(RsKeyCodes key) { bool CControllerConfigManager::GetIsMouseButtonDown(RsKeyCodes key) { return plugin::CallMethodAndReturn(this, key); } - + // 0x52F020 bool CControllerConfigManager::GetIsMouseButtonUp(RsKeyCodes key) { return plugin::CallMethodAndReturn(this, key); diff --git a/source/game_sa/ControllerConfigManager.h b/source/game_sa/ControllerConfigManager.h index 001d997091..14090f4a31 100644 --- a/source/game_sa/ControllerConfigManager.h +++ b/source/game_sa/ControllerConfigManager.h @@ -101,6 +101,19 @@ struct CControllerAction { CControllerKey Keys[4]; }; +struct CPadConfig { + int32 field_0{}; + bool present{}; // Device exists + bool zAxisPresent{}; // Has property DIJOFS_Z + bool rzAxisPresent{}; // Has property DIJOFS_RZ +private: + char __align{}; +public: + int32 vendorId{}; + int32 productId{}; +}; +static inline auto& PadConfigs = StaticRef, 0xC92144>(); + using ControlName = char[40]; class CControllerConfigManager { @@ -110,8 +123,8 @@ class CControllerConfigManager { DIJOYSTATE2 m_OldJoyState; DIJOYSTATE2 m_NewJoyState; - ControlName m_arrControllerActionName[59]; - bool m_ButtonStates[17]; + char m_arrControllerActionName[59][40]; // todo: 182 + bool m_ButtonStates[17]; // True if down, false if up or missing CControllerAction m_Actions[59]; bool m_bStickL_X_Rgh_Lft_MovementBothDown[4]; @@ -127,6 +140,7 @@ class CControllerConfigManager { CControllerConfigManager(); CControllerConfigManager* Constructor(); + bool LoadSettings(FILESTREAM file); void SaveSettings(FILESTREAM file); @@ -139,8 +153,10 @@ class CControllerConfigManager { void StoreMouseButtonState(eMouseButtons button, bool state); void UpdateJoyInConfigMenus_ButtonDown(ePadButton button, int32 padNumber); + void UpdateJoy_ButtonDown(ePadButton button, int32 unk); void AffectControllerStateOn_ButtonDown_DebugStuff(int32, eControllerType); void UpdateJoyInConfigMenus_ButtonUp(ePadButton button, int32 padNumber); + void UpdateJoy_ButtonUp(ePadButton button, int32 unk); void AffectControllerStateOn_ButtonUp_DebugStuff(int32, eControllerType); void ClearSimButtonPressCheckers(); @@ -155,6 +171,8 @@ class CControllerConfigManager { eActionType GetActionType(eControllerAction action); char* GetControllerSettingTextMouse(eControllerAction action); char* GetControllerSettingTextJoystick(eControllerAction action); + void StoreJoyButtonStates(); + void HandleJoyButtonUpDown(int32 joyNo, bool isDown); // NOTSA void ClearSettingsAssociatedWithAction(eControllerAction action, eControllerType type); void MakeControllerActionsBlank(); diff --git a/source/game_sa/CutsceneMgr.cpp b/source/game_sa/CutsceneMgr.cpp index 0fdb6376b5..990ed5e1ab 100644 --- a/source/game_sa/CutsceneMgr.cpp +++ b/source/game_sa/CutsceneMgr.cpp @@ -5,9 +5,7 @@ Do not delete this comment block. Respect others' work! */ #include "StdInc.h" - -#include // TODO: Remove (Included because of isForeground) - +#include #include #include "Fx.h" diff --git a/source/game_sa/LoadingScreen.h b/source/game_sa/LoadingScreen.h index 9737b6e264..88e25601ba 100644 --- a/source/game_sa/LoadingScreen.h +++ b/source/game_sa/LoadingScreen.h @@ -7,7 +7,6 @@ #pragma once class CSprite2d; - class CLoadingScreen { public: static constexpr size_t MAX_SPLASHES = 7u; diff --git a/source/game_sa/MenuManager.h b/source/game_sa/MenuManager.h index 5e82792f59..a1a2d9a351 100644 --- a/source/game_sa/MenuManager.h +++ b/source/game_sa/MenuManager.h @@ -108,8 +108,8 @@ class CMenuManager { CVector2D m_vMousePos; // Red marker position (world coordinates) bool m_bMapLoaded; - int32 m_nTitleLanguage; - int32 m_nTextLanguage; + int32 m_nTitleLanguage; // Value is PRIMARYLANGID(GetSystemDefaultLCID()) + int32 m_nTextLanguage; // TODO: Change to `eLanguage` eLanguage m_nPrefsLanguage; eLanguage m_nPreviousLanguage; int32 m_nLanguageF0x88; diff --git a/source/game_sa/RenderWare/rw/rwplcore.cpp b/source/game_sa/RenderWare/rw/rwplcore.cpp index 79a7401199..393c24f8ac 100644 --- a/source/game_sa/RenderWare/rw/rwplcore.cpp +++ b/source/game_sa/RenderWare/rw/rwplcore.cpp @@ -378,8 +378,8 @@ RwInt32 RwEngineGetNumVideoModes() { return ((RwInt32(__cdecl *)(void))0x7F2CC0)(); } -RwVideoMode* RwEngineGetVideoModeInfo(RwVideoMode* modeinfo, RwInt32 modeIndex) { - return ((RwVideoMode*(__cdecl *)(RwVideoMode*, RwInt32))0x7F2CF0)(modeinfo, modeIndex); +RwVideoMode RwEngineGetVideoModeInfo(RwInt32 modeIndex) { + return ((RwVideoMode(__cdecl *)(RwInt32))0x7F2CF0)(modeIndex); } RwInt32 RwEngineGetCurrentVideoMode() { diff --git a/source/game_sa/RenderWare/rw/rwplcore.h b/source/game_sa/RenderWare/rw/rwplcore.h index c8d23f1dfb..506f07c7d5 100644 --- a/source/game_sa/RenderWare/rw/rwplcore.h +++ b/source/game_sa/RenderWare/rw/rwplcore.h @@ -5329,7 +5329,7 @@ RwSubSystemInfo* RwEngineGetSubSystemInfo(RwSubSystemInfo* subSystemInfo, RwInt3 RwInt32 RwEngineGetCurrentSubSystem(); // 0x7F2C60 RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex); // 0x7F2C90 RwInt32 RwEngineGetNumVideoModes(); // 0x7F2CC0 -RwVideoMode* RwEngineGetVideoModeInfo(RwVideoMode* modeinfo, RwInt32 modeIndex); // 0x7F2CF0 +RwVideoMode RwEngineGetVideoModeInfo(RwInt32 modeIndex); // 0x7F2CF0 RwInt32 RwEngineGetCurrentVideoMode(); // 0x7F2D20 RwBool RwEngineSetVideoMode(RwInt32 modeIndex); // 0x7F2D50 RwInt32 RwEngineGetTextureMemorySize(); // 0x7F2D80 diff --git a/source/premake5.lua b/source/premake5.lua index 6a593319c2..a15641234b 100644 --- a/source/premake5.lua +++ b/source/premake5.lua @@ -48,7 +48,13 @@ project "gta_reversed" "vorbis", "vorbisenc", "vorbisfile", - "imgui" + "imgui", + "ddraw.lib", + "Winmm.lib", + "dxguid.lib", + "strmiids.lib", + "dsound.lib", + "d3d9.lib" } libdirs {