From 507e6c8f3cd890a802fd2f9cbce48be60cadc34f Mon Sep 17 00:00:00 2001 From: ashbob999 Date: Sat, 15 Jun 2024 14:53:51 +0100 Subject: [PATCH 1/2] Implemented the dependencies module --- src/openlrr/OpenLRR.cpp | 2 +- src/openlrr/game/object/Dependencies.cpp | 254 ++++++++++++++++++++++- src/openlrr/game/object/Dependencies.h | 48 ++--- src/openlrr/interop.cpp | 36 +++- src/openlrr/interop.h | 3 +- 5 files changed, 305 insertions(+), 38 deletions(-) diff --git a/src/openlrr/OpenLRR.cpp b/src/openlrr/OpenLRR.cpp index 2a4b9e0..631bbf3 100644 --- a/src/openlrr/OpenLRR.cpp +++ b/src/openlrr/OpenLRR.cpp @@ -772,7 +772,7 @@ void __cdecl OpenLRR_HandleCommand(HWND hWnd, uint16 wmId, uint16 wmSrc) case IDM_UNLOCKBUILD: //std::printf("IDM_UNLOCKBUILD\n"); - Dependencies_SetEnabled(!LegoRR::Dependencies_IsEnabled()); + LegoRR::Dependencies_SetEnabled(!LegoRR::Dependencies_IsEnabled()); break; case IDM_NOBUILDCOSTS: diff --git a/src/openlrr/game/object/Dependencies.cpp b/src/openlrr/game/object/Dependencies.cpp index 65b21c6..6acd9c0 100644 --- a/src/openlrr/game/object/Dependencies.cpp +++ b/src/openlrr/game/object/Dependencies.cpp @@ -2,6 +2,10 @@ // #include "Dependencies.h" +#include "../Game.h" +#include "../object/Stats.h" + +#include "../../engine/core/Utils.h" /********************************************************************************** @@ -22,36 +26,264 @@ LegoRR::Dependencies_Globs & LegoRR::dependencyGlobs = *(LegoRR::Dependencies_Gl #pragma region Functions // -//void __cdecl LegoRR::Dependencies_SetEnabled(bool32 on); +void __cdecl LegoRR::Dependencies_SetEnabled(bool32 on) +{ + dependencyGlobs.disabled = (on == 0); +} // -//void __cdecl LegoRR::Dependencies_Reset_ClearAllLevelFlags_10c(void); +void __cdecl LegoRR::Dependencies_Reset_ClearAllLevelFlags_10c(void) +{ + for (uint32 i = 0; i < LegoObject_Type_Count; i++) { + for (uint32 j = 0; j < LegoObject_ID_Count; j++) { + for (uint32 k = 0; k < OBJECT_MAXLEVELS; k++) { + dependencyGlobs.table[i][j].levelFlags[k] &= ~(DependencyFlags::DEPENDENCY_FLAG_UNK_4 | DependencyFlags::DEPENDENCY_FLAG_UNK_8 | DependencyFlags::DEPENDENCY_FLAG_UNK_100); + } + } + } +} // -//void __cdecl LegoRR::Dependencies_Initialise(const Gods98::Config* config, const char* gameName); +void __cdecl LegoRR::Dependencies_Initialise(const Gods98::Config* config, const char* gameName) +{ + char* targetParts[3]; + char* requireParts[10]; + char* requireObjParts[2]; + + for (const Gods98::Config* prop = Gods98::Config_FindArray(config, Config_ID(gameName, "Dependencies")); prop != nullptr; prop = Gods98::Config_GetNextItem(prop)) { + char* itemName = Gods98::Util_StrCpy(Gods98::Config_GetItemName(prop)); + uint32 numParts = Gods98::Util_Tokenise(itemName, targetParts, ":"); + + LegoObject_Type objectType; + LegoObject_ID objectID; + if (Lego_GetObjectByName(targetParts[1], &objectType, &objectID, nullptr)) { + int objLevel = 0; + + int cmp = ::_stricmp(targetParts[0], "HitOnceStay"); + + if (numParts == 3) { + objLevel = std::atoi(targetParts[2]); + Stats_GetLevels(objectType, objectID); + if (cmp == 0) { + dependencyGlobs.table[objectType][objectID].levelFlags[objLevel] |= DependencyFlags::DEPENDENCY_FLAG_HITONCESTAY; + } + + dependencyGlobs.table[objectType][objectID].manualLevel = true; + } + else { + objLevel = 0; + if (cmp == 0) { + for (uint32 i = 0; i < OBJECT_MAXLEVELS; i++) { + dependencyGlobs.table[objectType][objectID].levelFlags[i] |= DependencyFlags::DEPENDENCY_FLAG_HITONCESTAY; + } + } + + dependencyGlobs.table[objectType][objectID].manualLevel = false; + } + + char* dataString = Gods98::Util_StrCpy(Gods98::Config_GetDataString(prop)); + int numRequirements = Gods98::Util_Tokenise(dataString, requireParts, ","); + + DependencyRequirement* requirements = (DependencyRequirement*)Gods98::Mem_Alloc(numRequirements * sizeof(DependencyRequirement)); + dependencyGlobs.table[objectType][objectID].requirements[objLevel] = requirements; + dependencyGlobs.table[objectType][objectID].numRequirements[objLevel] = numRequirements; + + if (numRequirements != 0) { + // The first requirement in the config, is the last in the array + // Because the first requirement is displaysed on the right of the list + for (int i = numRequirements - 1, partIndex = 0; i >= 0; i--, partIndex++) { + int numParts = Gods98::Util_Tokenise(requireParts[partIndex], requireObjParts, ":"); + + LegoObject_Type requireObjType; + LegoObject_ID requireObjID; + if (Lego_GetObjectByName(requireObjParts[0], &requireObjType, &requireObjID, nullptr)) { + dependencyGlobs.table[objectType][objectID].requirements[objLevel][i].objType = requireObjType; + dependencyGlobs.table[objectType][objectID].requirements[objLevel][i].objID = requireObjID; + + if (numParts == 2) { + int requireObjLevel = std::atoi(requireObjParts[1]); + Stats_GetLevels(requireObjType, requireObjID); + dependencyGlobs.table[objectType][objectID].requirements[objLevel][i].objLevel = requireObjLevel; + dependencyGlobs.table[objectType][objectID].requirements[objLevel][i].hasLevel = true; + } + else { + dependencyGlobs.table[objectType][objectID].requirements[objLevel][i].hasLevel = false; + } + } + } + } + Gods98::Mem_Free(dataString); + } + + Gods98::Mem_Free(itemName); + } + + Dependencies_Prepare_Unk(); +} + +// Checks whether all dependencies for an object are currently met // -//bool32 __cdecl LegoRR::Dependencies_Object_FUN_0040add0(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +bool32 __cdecl LegoRR::Dependencies_Object_FUN_0040add0(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel) +{ + if (!dependencyGlobs.disabled) { + DependencyFlags* flags = &dependencyGlobs.table[objType][objID].levelFlags[objLevel]; + + if ((*flags & DependencyFlags::DEPENDENCY_FLAG_UNK_2) == 0) { + DependencyRequirement* requirements; + uint32 count; + Dependencies_Object_GetRequirements(objType, objID, objLevel, &requirements, &count); + + if (objType != LegoObject_Type::LegoObject_None) { + for (uint32 i = 0; i < count; i++) { + bool hasRequirement = false; + for (auto obj : objectListSet.EnumerateSkipUpgradeParts()) { + if (Dependencies_LiveObject_CallbackCheck_FUN_0040ae70(obj, &requirements[i])) { + hasRequirement = true; + break; + } + } + //bool hasRequirement = LegoObject_RunThroughListsSkipUpgradeParts(Dependencies_LiveObject_CallbackCheck_FUN_0040ae70, (void*) &requirements[i]); + if (!hasRequirement) { + return false; + } + } + } + + if ((*flags & DependencyFlags::DEPENDENCY_FLAG_HITONCESTAY) != DependencyFlags::DEPENDENCY_FLAG_NONE) { + *flags |= DependencyFlags::DEPENDENCY_FLAG_UNK_2; + } + } + } + return true; +} +// Checks if a live object satisfies the dependency requirement // -//bool32 __cdecl LegoRR::Dependencies_LiveObject_CallbackCheck_FUN_0040ae70(LegoObject* liveObj, LegoObject* otherObj); +bool32 __cdecl LegoRR::Dependencies_LiveObject_CallbackCheck_FUN_0040ae70(LegoObject* liveObj, DependencyRequirement* otherObj) +{ + if (!LegoObject_CanStoredObjectTypeBeSpawned(otherObj->objType)) { + if (otherObj->objType != liveObj->type) { + return false; + } + + if (otherObj->objID != liveObj->id) { + return false; + } + + if (otherObj->hasLevel && (liveObj->objLevel < otherObj->objLevel)) { + return false; + } + } + return true; +} // -//void __cdecl LegoRR::Dependencies_Object_GetRequirements(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count); +void __cdecl LegoRR::Dependencies_Object_GetRequirements(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count) +{ + if (dependencyGlobs.table[objType][objID].manualLevel) { + *requirements = dependencyGlobs.table[objType][objID].requirements[objLevel]; + *count = dependencyGlobs.table[objType][objID].numRequirements[objLevel]; + } + *requirements = dependencyGlobs.table[objType][objID].requirements[0]; + *count = dependencyGlobs.table[objType][objID].numRequirements[0]; +} // -//void __cdecl LegoRR::Dependencies_Object_Unlock(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks); +void __cdecl LegoRR::Dependencies_Object_Unlock(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks) +{ + unlocks->count = 0; + + for (uint32 i = 0; i < LegoObject_Type_Count; i++) { + for (uint32 j = 0; j < LegoObject_ID_Count; j++) { + if (!dependencyGlobs.table[i][j].manualLevel) { + DependencyRequirement* requirements; + uint32 numRequirements; + Dependencies_Object_GetRequirements((LegoObject_Type)i, (LegoObject_ID)j, 0, &requirements, &numRequirements); + + for (uint32 reqIndex = 0; reqIndex < numRequirements; reqIndex++) { + if (requirements[reqIndex].objType == objType && requirements[reqIndex].objID == objID && (requirements[reqIndex].objLevel == 0 || requirements[reqIndex].objLevel == objLevel)) { + unlocks->objTypes[unlocks->count] = (LegoObject_Type)i; + unlocks->objIDs[unlocks->count] = (LegoObject_ID)j; + unlocks->objHasLevels[unlocks->count] = false; + unlocks->count++; + } + } + } + else { + for (uint32 level = 0; level < OBJECT_MAXLEVELS; level++) { + DependencyRequirement* requirements; + uint32 numRequirements; + Dependencies_Object_GetRequirements((LegoObject_Type)i, (LegoObject_ID)j, level, &requirements, &numRequirements); + + for (uint32 reqIndex = 0; reqIndex < numRequirements; reqIndex++) { + if (requirements[reqIndex].objType == objType && requirements[reqIndex].objID == objID && (requirements[reqIndex].objLevel == 0 || requirements[reqIndex].objLevel == objLevel)) { + unlocks->objTypes[unlocks->count] = (LegoObject_Type)i; + unlocks->objIDs[unlocks->count] = (LegoObject_ID)j; + unlocks->objHasLevels[unlocks->count] = false; + unlocks->count++; + } + } + } + } + } + } +} // -//void __cdecl LegoRR::Dependencies_Prepare_Unk(void); +void __cdecl LegoRR::Dependencies_Prepare_Unk(void) +{ + for (uint32 objType = 0; objType < LegoObject_Type_Count; objType++) { + for (uint32 objID = 0; objID < LegoObject_ID_Count; objID++) { + for (uint32 objLevel = 0; objLevel < OBJECT_MAXLEVELS; objLevel++) { + if (objLevel == 1 && dependencyGlobs.table[objType][objID].manualLevel) { + break; + } + + DependencyFlags flags = dependencyGlobs.table[objType][objID].levelFlags[objLevel]; + if ((flags & DependencyFlags::DEPENDENCY_FLAG_UNK_8) == 0) { + if (!Dependencies_Object_FUN_0040add0((LegoObject_Type)objType, (LegoObject_ID)objID, objLevel)) { + if ((flags & DependencyFlags::DEPENDENCY_FLAG_UNK_4) != 0) { + flags &= ~DependencyFlags::DEPENDENCY_FLAG_UNK_4; + flags |= DependencyFlags::DEPENDENCY_FLAG_UNK_8; + } + } + else { + if ((flags & DependencyFlags::DEPENDENCY_FLAG_UNK_4) == 0) { + flags |= DependencyFlags::DEPENDENCY_FLAG_UNK_4; + } + else { + flags &= ~DependencyFlags::DEPENDENCY_FLAG_UNK_4; + flags |= DependencyFlags::DEPENDENCY_FLAG_UNK_8; + } + } + + dependencyGlobs.table[objType][objID].levelFlags[objLevel] = flags; + } + } + } + } +} // -//bool32 __cdecl LegoRR::Dependencies_Object_IsLevelFlag4(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +bool32 __cdecl LegoRR::Dependencies_Object_IsLevelFlag4(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel) +{ + if (dependencyGlobs.table[objType][objID].manualLevel) { + return dependencyGlobs.table[objType][objID].levelFlags[objLevel] & DependencyFlags::DEPENDENCY_FLAG_UNK_4; + } + return dependencyGlobs.table[objType][objID].levelFlags[0] & DependencyFlags::DEPENDENCY_FLAG_UNK_4; +} // -//void __cdecl LegoRR::Dependencies_Object_AddLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +void __cdecl LegoRR::Dependencies_Object_AddLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel) +{ + dependencyGlobs.table[objType][objID].levelFlags[objLevel] |= DependencyFlags::DEPENDENCY_FLAG_UNK_100; +} // -//bool32 __cdecl LegoRR::Dependencies_Object_GetLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +bool32 __cdecl LegoRR::Dependencies_Object_GetLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel) +{ + return dependencyGlobs.table[objType][objID].levelFlags[objLevel] & DependencyFlags::DEPENDENCY_FLAG_UNK_100; +} #pragma endregion diff --git a/src/openlrr/game/object/Dependencies.h b/src/openlrr/game/object/Dependencies.h index 453ba41..1c6cf19 100644 --- a/src/openlrr/game/object/Dependencies.h +++ b/src/openlrr/game/object/Dependencies.h @@ -36,8 +36,8 @@ namespace LegoRR enum DependencyFlags : uint32 // [LegoRR/Dependencies.c|flags:0x4|type:uint] { DEPENDENCY_FLAG_NONE = 0, - DEPENDENCY_FLAG_HITONCESTAY = 0x1, - DEPENDENCY_FLAG_UNK_2 = 0x2, + DEPENDENCY_FLAG_HITONCESTAY = 0x1, // Specifies that the dependencies only have to previously been met + DEPENDENCY_FLAG_UNK_2 = 0x2, // Set when the dependencies have previously been met DEPENDENCY_FLAG_UNK_4 = 0x4, DEPENDENCY_FLAG_UNK_8 = 0x8, DEPENDENCY_FLAG_UNK_100 = 0x100, @@ -126,48 +126,48 @@ inline bool Dependencies_IsEnabled() { return !dependencyGlobs.disabled; } // -#define Dependencies_SetEnabled ((void (__cdecl* )(bool32 on))0x0040aa40) -//void __cdecl Dependencies_SetEnabled(bool32 on); +//#define Dependencies_SetEnabled ((void (__cdecl* )(bool32 on))0x0040aa40) +void __cdecl Dependencies_SetEnabled(bool32 on); // -#define Dependencies_Reset_ClearAllLevelFlags_10c ((void (__cdecl* )(void))0x0040aa60) -//void __cdecl Dependencies_Reset_ClearAllLevelFlags_10c(void); +//#define Dependencies_Reset_ClearAllLevelFlags_10c ((void (__cdecl* )(void))0x0040aa60) +void __cdecl Dependencies_Reset_ClearAllLevelFlags_10c(void); // -#define Dependencies_Initialise ((void (__cdecl* )(const Gods98::Config* config, const char* gameName))0x0040aaa0) -//void __cdecl Dependencies_Initialise(const Gods98::Config* config, const char* gameName); +//#define Dependencies_Initialise ((void (__cdecl* )(const Gods98::Config* config, const char* gameName))0x0040aaa0) +void __cdecl Dependencies_Initialise(const Gods98::Config* config, const char* gameName); // -#define Dependencies_Object_FUN_0040add0 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040add0) -//bool32 __cdecl Dependencies_Object_FUN_0040add0(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +//#define Dependencies_Object_FUN_0040add0 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040add0) +bool32 __cdecl Dependencies_Object_FUN_0040add0(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); // -#define Dependencies_LiveObject_CallbackCheck_FUN_0040ae70 ((bool32 (__cdecl* )(LegoObject* liveObj, LegoObject* otherObj))0x0040ae70) -//bool32 __cdecl Dependencies_LiveObject_CallbackCheck_FUN_0040ae70(LegoObject* liveObj, LegoObject* otherObj); +//#define Dependencies_LiveObject_CallbackCheck_FUN_0040ae70 ((bool32 (__cdecl* )(LegoObject* liveObj, DependencyRequirement* otherObj))0x0040ae70) +bool32 __cdecl Dependencies_LiveObject_CallbackCheck_FUN_0040ae70(LegoObject* liveObj, DependencyRequirement* otherObj); // -#define Dependencies_Object_GetRequirements ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count))0x0040aec0) -//void __cdecl Dependencies_Object_GetRequirements(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count); +//#define Dependencies_Object_GetRequirements ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count))0x0040aec0) +void __cdecl Dependencies_Object_GetRequirements(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, OUT DependencyRequirement** requirements, OUT uint32* count); // -#define Dependencies_Object_Unlock ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks))0x0040af30) -//void __cdecl Dependencies_Object_Unlock(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks); +//#define Dependencies_Object_Unlock ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks))0x0040af30) +void __cdecl Dependencies_Object_Unlock(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel, DependencyUnlocks* unlocks); // -#define Dependencies_Prepare_Unk ((void (__cdecl* )(void))0x0040b0e0) -//void __cdecl Dependencies_Prepare_Unk(void); +//#define Dependencies_Prepare_Unk ((void (__cdecl* )(void))0x0040b0e0) +void __cdecl Dependencies_Prepare_Unk(void); // -#define Dependencies_Object_IsLevelFlag4 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b180) -//bool32 __cdecl Dependencies_Object_IsLevelFlag4(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +//#define Dependencies_Object_IsLevelFlag4 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b180) +bool32 __cdecl Dependencies_Object_IsLevelFlag4(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); // -#define Dependencies_Object_AddLevelFlag_100 ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b1d0) -//void __cdecl Dependencies_Object_AddLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +//#define Dependencies_Object_AddLevelFlag_100 ((void (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b1d0) +void __cdecl Dependencies_Object_AddLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); // -#define Dependencies_Object_GetLevelFlag_100 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b210) -//bool32 __cdecl Dependencies_Object_GetLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); +//#define Dependencies_Object_GetLevelFlag_100 ((bool32 (__cdecl* )(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel))0x0040b210) +bool32 __cdecl Dependencies_Object_GetLevelFlag_100(LegoObject_Type objType, LegoObject_ID objID, uint32 objLevel); #pragma endregion diff --git a/src/openlrr/interop.cpp b/src/openlrr/interop.cpp index 4f4538b..3b4f587 100644 --- a/src/openlrr/interop.cpp +++ b/src/openlrr/interop.cpp @@ -67,7 +67,8 @@ #include "game/object/BezierCurve.h" #include "game/object/Building.h" #include "game/object/Collision.h" -#include "game/object/Creature.h" +#include "game/object/Creature.h" +#include "game/object/Dependencies.h" #include "game/object/MeshLOD.h" #include "game/object/Object.h" #include "game/object/ObjectRecall.h" @@ -2024,6 +2025,38 @@ bool interop_hook_LegoRR_DamageText(void) result &= hook_write_jmpret(0x0040aa10, LegoRR::DamageText_CanShow); return_interop(result); +} + +bool interop_hook_LegoRR_Dependencies(void) +{ + bool result = true; + + // used by: Lego_HandleWorldDebugKeys, OpenLRR_HandleCommand + result &= hook_write_jmpret(0x0040aa40, LegoRR::Dependencies_SetEnabled); + // used by: Level_Free + result &= hook_write_jmpret(0x0040aa60, LegoRR::Dependencies_Reset_ClearAllLevelFlags_10c); + // used by: Lego_Initialise + result &= hook_write_jmpret(0x0040aaa0, LegoRR::Dependencies_Initialise); + // used by: Lego_HandleWorldDebugKeys, AITask_DoBuildPath_AtPosition, Dependencies_Prepare_Unk, + // Interface_ChangeObjectIconFlag1_FUN_0041c730, Interface_HandleMenuItem + result &= hook_write_jmpret(0x0040add0, LegoRR::Dependencies_Object_FUN_0040add0); + // used by: Dependencies_Object_FUN_0040add0, Interface_FUN_0041ebd0 + result &= hook_write_jmpret(0x0040ae70, LegoRR::Dependencies_LiveObject_CallbackCheck_FUN_0040ae70); + // used by: Dependencies_Object_FUN_0040add0, Dependencies_Object_Unlock, Dependencies_Object_Unlock, + // Interface_FUN_0041ebd0, Interface_FUN_0041edb0 + result &= hook_write_jmpret(0x0040aec0, LegoRR::Dependencies_Object_GetRequirements); + // used by: HelpWindow_Object_Unlock + result &= hook_write_jmpret(0x0040af30, LegoRR::Dependencies_Object_Unlock); + // used by: Dependencies_Initialise, HelpWindow_RecallDependencies + result &= hook_write_jmpret(0x0040b0e0, LegoRR::Dependencies_Prepare_Unk); + // used by: HelpWindow_Object_Unlock + result &= hook_write_jmpret(0x0040b180, LegoRR::Dependencies_Object_IsLevelFlag4); + // used by: HelpWindow_RecallDependencies + result &= hook_write_jmpret(0x0040b1d0, LegoRR::Dependencies_Object_AddLevelFlag_100); + // used by: HelpWindow_RecallDependencies + result &= hook_write_jmpret(0x0040b210, LegoRR::Dependencies_Object_GetLevelFlag_100); + + return_interop(result); } bool interop_hook_LegoRR_Effect(void) @@ -4531,6 +4564,7 @@ bool interop_hook_all(void) result &= interop_hook_LegoRR_Creature(); result &= interop_hook_LegoRR_Credits(); result &= interop_hook_LegoRR_DamageText(); + result &= interop_hook_LegoRR_Dependencies(); result &= interop_hook_LegoRR_Effect(); result &= interop_hook_LegoRR_ElectricFence(); result &= interop_hook_LegoRR_Encyclopedia(); diff --git a/src/openlrr/interop.h b/src/openlrr/interop.h index 3cb9049..1f23d62 100644 --- a/src/openlrr/interop.h +++ b/src/openlrr/interop.h @@ -63,7 +63,8 @@ bool interop_hook_LegoRR_Collision(void); bool interop_hook_LegoRR_Construction(void); bool interop_hook_LegoRR_Creature(void); bool interop_hook_LegoRR_Credits(void); -bool interop_hook_LegoRR_DamageText(void); +bool interop_hook_LegoRR_DamageText(void); +bool interop_hook_LegoRR_Dependencies(void); bool interop_hook_LegoRR_Effect(void); bool interop_hook_LegoRR_ElectricFence(void); bool interop_hook_LegoRR_Encyclopedia(void); From dbcb4d6ac94f2a891bea8cda48e7c5ba20f98ace Mon Sep 17 00:00:00 2001 From: ashbob999 Date: Sun, 14 Jul 2024 17:51:50 +0100 Subject: [PATCH 2/2] Implemented the InfoMessages functions --- src/openlrr/OpenLRR.cpp | 2 +- src/openlrr/game/interface/InfoMessages.cpp | 818 +++++++++++++++++++- src/openlrr/game/interface/InfoMessages.h | 189 +++-- src/openlrr/interop.cpp | 94 +++ src/openlrr/interop.h | 1 + 5 files changed, 979 insertions(+), 125 deletions(-) diff --git a/src/openlrr/OpenLRR.cpp b/src/openlrr/OpenLRR.cpp index 631bbf3..77fcafe 100644 --- a/src/openlrr/OpenLRR.cpp +++ b/src/openlrr/OpenLRR.cpp @@ -402,7 +402,7 @@ void __cdecl OpenLRR_HandleCommand(HWND hWnd, uint16 wmId, uint16 wmSrc) case IDM_AUTOGAMESPEED: //std::printf("IDM_AUTOGAMESPEED\n"); - Info_SetAutoGameSpeed(!LegoRR::Info_IsAutoGameSpeed()); + LegoRR::Info_SetAutoGameSpeed(!LegoRR::Info_IsAutoGameSpeed()); updateOptionsMenuCycles(); break; diff --git a/src/openlrr/game/interface/InfoMessages.cpp b/src/openlrr/game/interface/InfoMessages.cpp index b2978a4..96483d6 100644 --- a/src/openlrr/game/interface/InfoMessages.cpp +++ b/src/openlrr/game/interface/InfoMessages.cpp @@ -1,7 +1,17 @@ // InfoMessages.cpp : // -#include "InfoMessages.h" +#include "InfoMessages.h" + +#include "Interface.h" +#include "Panels.h" +#include "ScrollInfo.h" +#include "../Game.h" +#include "../audio/SFX.h" +#include "../interface/Pointers.h" +#include "../mission/NERPsFunctions.h" + +#include "../../engine/Main.h" /********************************************************************************** @@ -22,117 +32,839 @@ LegoRR::Info_Globs & LegoRR::infoGlobs = *(LegoRR::Info_Globs*)0x004dd658; #pragma region Functions // -//void __cdecl LegoRR::Info_Initialise(Gods98::Font* font); +void __cdecl LegoRR::Info_Initialise(Gods98::Font* font) +{ + Info_RegisterName(Info_CrystalFound); + Info_RegisterName(Info_OreSeamFound); + Info_RegisterName(Info_RockMonster); + Info_RegisterName(Info_LavaRockMonster); + Info_RegisterName(Info_IceRockMonster); + Info_RegisterName(Info_UnderAttack); + Info_RegisterName(Info_Landslide); + Info_RegisterName(Info_CaveIn); + Info_RegisterName(Info_Constructed); + Info_RegisterName(Info_CavernLocated); + Info_RegisterName(Info_LegoManDeath); + Info_RegisterName(Info_VehicleDeath); + Info_RegisterName(Info_BuildingDeath); + Info_RegisterName(Info_DynamitePlaced); + Info_RegisterName(Info_NoPower); + Info_RegisterName(Info_PowerDrain); + Info_RegisterName(Info_AirDepleting); + Info_RegisterName(Info_AirLow); + Info_RegisterName(Info_AirOut); + Info_RegisterName(Info_AirRestored); + Info_RegisterName(Info_TrainDriver); + Info_RegisterName(Info_TrainDynamite); + Info_RegisterName(Info_TrainRepair); + Info_RegisterName(Info_TrainPilot); + Info_RegisterName(Info_TrainSailor); + Info_RegisterName(Info_TrainScanner); + Info_RegisterName(Info_OreCollected); + Info_RegisterName(Info_WallDug); + Info_RegisterName(Info_WallReinforced); + Info_RegisterName(Info_CrystalPower); + Info_RegisterName(Info_LavaErode); + Info_RegisterName(Info_SlugEmerge); + Info_RegisterName(Info_PathCompleted); + Info_RegisterName(Info_FoundMinifigure); + Info_RegisterName(Info_CanUpgradeMinifigure); + Info_RegisterName(Info_CanTrainMinifigure); + Info_RegisterName(Info_CrystalSeamFound); + Info_RegisterName(Info_GenericSeamFound); + Info_RegisterName(Info_GenericDeath); + Info_RegisterName(Info_GenericMonster); + + infoGlobs.font = font; + infoGlobs.flags |= Info_GlobFlags::INFO_GLOB_FLAG_UNK_8 | Info_GlobFlags::INFO_GLOB_FLAG_AUTOGAMESPEED; +} // -//bool32 __cdecl LegoRR::Info_GetInfoType(const char* infoName, OUT Info_Type* infoType); +bool32 __cdecl LegoRR::Info_GetInfoType(const char* infoName, OUT Info_Type* infoType) +{ + for (int i = 0; i < Info_Type_Count; i++) { + int cmp = ::_stricmp(infoGlobs.infoName[i], infoName); + if (cmp == 0) { + *infoType = (Info_Type)i; + return true; + } + } + + return false; +} // -//void* __cdecl LegoRR::Info_GetTypePtr4(Info_Type infoType); +void* __cdecl LegoRR::Info_GetTypePtr4(Info_Type infoType) +{ + return infoGlobs.infoDataTable[infoType].ptr_4; +} // -//void __cdecl LegoRR::Info_SetOverFlowImageFile(const char* filename); +void __cdecl LegoRR::Info_SetOverFlowImageFile(const char* filename) +{ + infoGlobs.OverFlowImage = Gods98::Image_LoadBMPScaled(filename, 0, 0); + Gods98::Image_SetPenZeroTrans(infoGlobs.OverFlowImage); +} // -//void __cdecl LegoRR::Info_SetAutoGameSpeed(bool32 autoOn); +void __cdecl LegoRR::Info_SetAutoGameSpeed(bool32 autoOn) +{ + if (autoOn) { + infoGlobs.flags |= Info_GlobFlags::INFO_GLOB_FLAG_AUTOGAMESPEED; + } + else { + infoGlobs.flags &= ~Info_GlobFlags::INFO_GLOB_FLAG_AUTOGAMESPEED; + } +} // -//void __cdecl LegoRR::Info_SetTypeChangeGameSpeed(Info_Type infoType, bool32 changeSpeedOn); +void __cdecl LegoRR::Info_SetTypeChangeGameSpeed(Info_Type infoType, bool32 changeSpeedOn) +{ + if (changeSpeedOn) { + infoGlobs.infoDataTable[infoType].flags |= InfoDataFlags::INFOTYPE_FLAG_CHANGEGAMESPEED; + } + else { + infoGlobs.infoDataTable[infoType].flags &= ~InfoDataFlags::INFOTYPE_FLAG_CHANGEGAMESPEED; + } +} // -//void __cdecl LegoRR::Info_SetTypeText(Info_Type infoType, const char* text); +void __cdecl LegoRR::Info_SetTypeText(Info_Type infoType, const char* text) +{ + char* buffer = infoGlobs.infoDataTable[infoType].text; + if (buffer != nullptr) { + Gods98::Mem_Free(buffer); + } + + Info_SetText_internal(text, &infoGlobs.infoDataTable[infoType].text); +} // -//void __cdecl LegoRR::Info_SetTypeFlag_20000(Info_Type infoType, bool32 setFlag20000); +void __cdecl LegoRR::Info_SetTypeFlag_20000(Info_Type infoType, bool32 setFlag20000) +{ + if (setFlag20000) { + infoGlobs.infoDataTable[infoType].flags |= InfoDataFlags::INFOTYPE_FLAG_UNK_20000; + } + else { + infoGlobs.infoDataTable[infoType].flags &= ~InfoDataFlags::INFOTYPE_FLAG_UNK_20000; + } +} // -//void __cdecl LegoRR::Info_SetText_internal(const char* text, char** pInfoText); +void __cdecl LegoRR::Info_SetText_internal(const char* text, char** pInfoText) +{ + char imagePointers[8]; + uint32 imagePointersCount = 0; + + char* pp = *pInfoText; + + const char* ptr = text; + uint32 charsLeft = 0xFFFFFFFF; + + while (*ptr != '\0') { + if (charsLeft == 0) { + break; + } + + charsLeft--; + ptr++; + } + + char* infoText = (char*)Gods98::Mem_Alloc(~charsLeft + 0xB); + *pInfoText = infoText; + + char* infoTextPtr = infoText; + + // Converts underlines to spaces, \n to a newline character. + // Loads a pointer to an image spacified between $, and changes it to %b. + // Stores the lowest byte of each image pointer at the end (after null byte). + + ptr = text; + while (*ptr != '\0') { + char c = *ptr; + + if (c == '_') { + *infoTextPtr = ' '; + } + else if (c == '$') { + ptr++; + + char infoNameBuffer[32]; + int infoNameSize = 0; + + while (*ptr != '$') { + infoNameBuffer[infoNameSize] = *ptr; + infoNameSize++; + ptr++; + } + + infoNameBuffer[infoNameSize] = '\0'; + + Gods98::Image* image = nullptr; + + Pointer_Type pointerType; + if (!Pointer_GetType(infoNameBuffer, &pointerType)) { + Info_Type infoType; + if (Info_GetInfoType(infoNameBuffer, &infoType)) { + image = (Gods98::Image*)Info_GetTypePtr4(infoType); + } + } + else { + image = Pointer_GetImage(pointerType).image; + } + + if (image != nullptr) { + /// TODO: Check what this is actually meant to display as %b is not supported + *infoTextPtr = '%'; + infoTextPtr++; + *infoTextPtr = 'b'; + + imagePointers[imagePointersCount] = (char)image; + imagePointersCount++; + } + } + else if (c == '\\' && *(ptr + 1) == 'n') { + *infoTextPtr = '\n'; + ptr++; + } + else { + *infoTextPtr = c; + } + + infoTextPtr++; + ptr++; + } + + *infoTextPtr = '\0'; + infoTextPtr++; + + for (uint32 i = 0; i < 5; i++) { + if (i < imagePointersCount) { + *infoTextPtr = imagePointers[i]; + } + else { + *infoTextPtr = '\0'; + } + + infoTextPtr++; + } +} // -//void __cdecl LegoRR::Info_SetTypeImageFile(Info_Type infoType, const char* filename); +void __cdecl LegoRR::Info_SetTypeImageFile(Info_Type infoType, const char* filename) +{ + Gods98::Image* image = Gods98::Image_LoadBMPScaled(filename, 0, 0); + infoGlobs.infoDataTable[infoType].ptr_4 = image; + Gods98::Image_SetPenZeroTrans(image); +} // -//void __cdecl LegoRR::Info_SetTypeSFX(Info_Type infoType, SFX_ID sfxID); +void __cdecl LegoRR::Info_SetTypeSFX(Info_Type infoType, SFX_ID sfxID) +{ + infoGlobs.infoDataTable[infoType].sfxType = sfxID; +} // -//bool32 __cdecl LegoRR::Info_EnumerateMessageInstances(sint32 handle, InfoEnumerateCallback callback, void* data); +bool32 __cdecl LegoRR::Info_EnumerateMessageInstances(sint32 handle, InfoEnumerateCallback callback, void* data) +{ + if (infoGlobs.infoMessageTable[handle].instanceCount == 0) { + return false; + } + + InfoMessageInstance* instance = infoGlobs.infoMessageTable[handle].instance; + if (instance == nullptr) { + return false; + } + + while (instance != nullptr) { + if (callback(instance, data)) { + return true; + } + + instance = instance->next; + } + + return false; +} // -//void __cdecl LegoRR::Info_AddMessageInstance(sint32 handle, InfoMessageInstance* instance); +void __cdecl LegoRR::Info_AddMessageInstance(sint32 handle, InfoMessageInstance* instance) +{ + InfoMessageInstance* curr = infoGlobs.infoMessageTable[handle].instance; + if (curr == nullptr) { + infoGlobs.infoMessageTable[handle].instance = instance; + instance->next = nullptr; + infoGlobs.infoMessageTable[handle].instanceCount++; + return; + } + + int count = 1; + InfoMessageInstance* next = curr->next; + + while (next != nullptr) { + count++; + + /* Maximum of 9 instances for a single type, pop the oldest one. */ + if (count == 9) { + Info_RemoveMessageInstance(handle, 0); + } + + curr = curr->next; + next = curr->next; + } + + curr->next = instance; + instance->next = nullptr; + infoGlobs.infoMessageTable[handle].instanceCount++; +} // -//LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_GetMessageInstance(sint32 handle, sint32 instanceIndex); +LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_GetMessageInstance(sint32 handle, sint32 instanceIndex) +{ + if (instanceIndex >= 0 && instanceIndex < static_cast(infoGlobs.infoMessageTable[handle].instanceCount)) { + InfoMessageInstance* instance = infoGlobs.infoMessageTable[handle].instance; + + while (instanceIndex != 0) { + instance = instance->next; + instanceIndex--; + } + + return instance; + } + return nullptr; +} // -//LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_RemoveMessageInstance(sint32 handle, sint32 instanceIndex); +LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_RemoveMessageInstance(sint32 handle, sint32 instanceIndex) +{ + InfoMessageInstance* instance = Info_GetMessageInstance(handle, instanceIndex); + if (instance == nullptr) { + return nullptr; + } + + InfoMessageInstance* prevInstance = Info_GetMessageInstance(handle, instanceIndex - 1); + if (instanceIndex == 0) { + infoGlobs.infoMessageTable->instance = instance->next; + } + else { + prevInstance->next = instance->next; + } + + infoGlobs.infoMessageTable[handle].instanceCount--; + return instance; +} // -//bool32 __cdecl LegoRR::Info_Callback_FindObjectReference(InfoMessageInstance* instance, void* search); +bool32 __cdecl LegoRR::Info_Callback_FindObjectReference(InfoMessageInstance* instance, void* search) +{ + SearchInfoObject_8* searchInfoObject = (SearchInfoObject_8*)search; + if (instance->object == searchInfoObject->object) { + return true; + } + searchInfoObject->index++; + return false; +} // -//void __cdecl LegoRR::Info_RemoveObjectReferences(LegoObject* liveObj); +void __cdecl LegoRR::Info_RemoveObjectReferences(LegoObject* liveObj) +{ + if (infoGlobs.infoMessageCount != 0) { + uint32 handle = 0; + while (handle < infoGlobs.infoMessageCount) { + /* Repeatedly search for all info message instances that reference this object until none are left. */ + SearchInfoObject_8 search = { liveObj, 0 }; + + while (Info_EnumerateMessageInstances(handle, Info_Callback_FindObjectReference, &search)) { + /// FIXME: Free the messgae instance + Info_RemoveMessageInstance(handle, search.index); + search.index = 0; + } + + if (infoGlobs.infoMessageTable[handle].instanceCount == 0) { + Info_RemoveMessage(handle); + } + else { + handle++; + } + } + } +} // -//bool32 __cdecl LegoRR::Info_Callback_FindBlockPos(InfoMessageInstance* infoInstance, void* search); +bool32 __cdecl LegoRR::Info_Callback_FindBlockPos(InfoMessageInstance* infoInstance, void* search) +{ + SearchInfoBlockPos_8* searchInfoBlockPos = (SearchInfoBlockPos_8*)search; + + if (infoInstance->blockPos.x == searchInfoBlockPos->pBlockPos->x && + infoInstance->blockPos.y == searchInfoBlockPos->pBlockPos->y) { + return true; + } + + searchInfoBlockPos->index++; + return false; +} // -//void __cdecl LegoRR::Info_RemoveAllAtBlockPos(const Point2I* blockPos); +void __cdecl LegoRR::Info_RemoveAllAtBlockPos(const Point2I* blockPos) +{ + if (infoGlobs.infoMessageCount != 0) { + uint32 handle = 0; + while (handle < infoGlobs.infoMessageCount) { + SearchInfoBlockPos_8 search = { blockPos, 0 }; + + if (infoGlobs.infoMessageTable[handle].infoType == Info_Type::Info_Landslide || + infoGlobs.infoMessageTable[handle].infoType == Info_Type::Info_CaveIn) { + while (Info_EnumerateMessageInstances(handle, Info_Callback_FindBlockPos, &search)) { + /// FIXME: Free the message instance + Info_RemoveMessageInstance(handle, search.index); + search.index = 0; + } + + if (infoGlobs.infoMessageTable[handle].instanceCount == 0) { + Info_RemoveMessage(handle); + handle--; + } + } + + handle++; + } + } +} // -//bool32 __cdecl LegoRR::Info_Callback_FindObjectAndBlockPos(InfoMessageInstance* instance, void* search); +bool32 __cdecl LegoRR::Info_Callback_FindObjectAndBlockPos(InfoMessageInstance* instance, void* search) +{ + SearchInfoObjectBlockPos_8* searchInfoObjectBlockPos = (SearchInfoObjectBlockPos_8*)search; + + const Point2I* searchBlockPos = searchInfoObjectBlockPos->optBlockPos; + LegoObject* searchObj = searchInfoObjectBlockPos->optObject; + + if (searchObj != nullptr || searchBlockPos != nullptr) { + if (searchObj == nullptr || searchObj != instance->object) { + if (searchBlockPos == nullptr || searchBlockPos->x != instance->blockPos.x || + searchBlockPos->y != instance->blockPos.y) { + return false; + } + } + + } + + return true; +} // -//bool32 __cdecl LegoRR::Info_HasTypeAtObjectOrBlockPos(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); +bool32 __cdecl LegoRR::Info_HasTypeAtObjectOrBlockPos(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos) +{ + if (infoType != Info_Type::Info_LegoManDeath && infoType != Info_Type::Info_BuildingDeath && + infoType != Info_Type::Info_VehicleDeath && infoType != Info_Type::Info_GenericDeath) { + if (infoGlobs.infoMessageCount != 0) { + SearchInfoObjectBlockPos_8 search = { liveObj,blockPos }; + + for (uint32 handle = 0; handle < infoGlobs.infoMessageCount; handle++) { + if (infoType == infoGlobs.infoMessageTable[handle].infoType) { + if (Info_EnumerateMessageInstances(handle, Info_Callback_FindObjectAndBlockPos, &search)) { + return true; + } + } + } + } + } + + return false; +} // -//LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_CreateInstance(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text); +LegoRR::InfoMessageInstance* __cdecl LegoRR::Info_CreateInstance(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text) +{ + InfoMessageInstance* instance = (InfoMessageInstance*)Gods98::Mem_Alloc(sizeof(InfoMessageInstance)); + if (instance == nullptr) { + return nullptr; + } + + if (blockPos == nullptr) { + instance->blockPos.x = -1; + instance->blockPos.y = -1; + } + else { + instance->blockPos = *blockPos; + } + + instance->object = liveObj; + + if (text != nullptr) { + Info_SetText_internal(text, &instance->text); + } + else { + instance->text = nullptr; + } + + return instance; +} // -//void __cdecl LegoRR::Info_SetFlag4(bool32 state); +void __cdecl LegoRR::Info_SetFlag4(bool32 state) +{ + if (state) { + infoGlobs.flags |= Info_GlobFlags::INFO_GLOB_FLAG_UNK_4; + } + else { + infoGlobs.flags &= ~Info_GlobFlags::INFO_GLOB_FLAG_UNK_4; + } +} // -//bool32 __cdecl LegoRR::Info_HasTypeText(Info_Type infoType); +bool32 __cdecl LegoRR::Info_HasTypeText(Info_Type infoType) +{ + return infoGlobs.infoDataTable[infoType].text != nullptr; +} // -//sint32 __cdecl LegoRR::Info_FindExistingMessageType(Info_Type infoType); - +sint32 __cdecl LegoRR::Info_FindExistingMessageType(Info_Type infoType) +{ + for (uint32 i = 0; i < infoGlobs.infoMessageCount; i++) { + if (infoGlobs.infoMessageTable[i].infoType == infoType) { + return i; + } + } + + return -1; +} + +// Calculates the y position of the info message // -//real32 __cdecl LegoRR::Info_FUN_00419a80(void); +real32 __cdecl LegoRR::Info_FUN_00419a80(void) +{ + real32 value = 330.0; + + if (infoGlobs.infoMessageCount != 0) { + for (int count = infoGlobs.infoMessageCount; count >= 0; count--) { + Info_Type infoType = infoGlobs.infoMessageTable[count].infoType; + value -= infoGlobs.infoDataTable[infoType].float_c; + } + } + + return value; +} // -//void __cdecl LegoRR::Info_Send(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); +void __cdecl LegoRR::Info_Send(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos) +{ + if (infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_4) { + if ((NERPFunc__GetTutorialFlags(nullptr) & TutorialFlags::TUTORIAL_FLAG_NOINFO) == TutorialFlags::TUTORIAL_FLAG_NONE) { + InfoData* infoData = &infoGlobs.infoDataTable[infoType]; + + if (infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_8 && + infoData->flags != InfoDataFlags::INFOTYPE_FLAG_NONE && + (infoData->flags & InfoDataFlags::INFOTYPE_FLAG_UNK_20000) != InfoDataFlags::INFOTYPE_FLAG_NONE && + infoData->float_c <= 0.0) { + infoData->float_c = 25.0; + + if (infoType == Info_Type::Info_LegoManDeath || infoType == Info_Type::Info_BuildingDeath || infoType == Info_Type::Info_VehicleDeath) { + infoGlobs.infoDataTable[Info_Type::Info_LegoManDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_VehicleDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_BuildingDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_GenericDeath].float_c = 25.0; + } + + SFX_Random_SetAndPlayGlobalSample(infoData->sfxType, nullptr); + } + + if (!Info_HasTypeAtObjectOrBlockPos(infoType, liveObj, blockPos)) { + if (Info_HasTypeText(infoType)) { + if (infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_8 && + (infoData->flags & InfoDataFlags::INFOTYPE_FLAG_UNK_20000) == InfoDataFlags::INFOTYPE_FLAG_NONE && + infoData->float_c <= 0.0) { + infoData->float_c = 25.0; + + if (infoType == Info_Type::Info_LegoManDeath || infoType == Info_Type::Info_BuildingDeath || + infoType == Info_Type::Info_VehicleDeath || infoType == Info_Type::Info_GenericDeath) { + infoGlobs.infoDataTable[Info_Type::Info_LegoManDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_VehicleDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_BuildingDeath].float_c = 25.0; + infoGlobs.infoDataTable[Info_Type::Info_GenericDeath].float_c = 25.0; + } + + SFX_Random_SetAndPlayGlobalSample(infoData->sfxType, nullptr); + } + + if (infoGlobs.infoMessageCount != Info_Type_Count) { + int handle = Info_FindExistingMessageType(infoType); + + if (handle == -1) { + InfoMessage* infoMessage = &infoGlobs.infoMessageTable[infoGlobs.infoMessageCount]; + + handle = infoGlobs.infoMessageCount; + infoGlobs.infoMessageCount++; + + infoMessage->float_c = -(float)((Gods98::Image*)(infoData->ptr_4))->width; + infoGlobs.infoMessageTable[handle].next = Info_FUN_00419a80(); + } + + infoGlobs.infoMessageTable[handle].infoType = infoType; + InfoMessageInstance* instance = Info_CreateInstance(blockPos, liveObj, opt_text); + Info_AddMessageInstance(handle, instance); + + uint32 progMode = Gods98::Main_ProgrammerMode(); + if (progMode < 5 && (infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_AUTOGAMESPEED) && + (infoData->flags & InfoDataFlags::INFOTYPE_FLAG_CHANGEGAMESPEED) != InfoDataFlags::INFOTYPE_FLAG_NONE) { + float gameSpeed = Lego_GetGameSpeed(); + if (gameSpeed > 1.0) { + Lego_SetGameSpeed(1.0); + } + } + } + } + } + } + } +} // -//void __cdecl LegoRR::Info_GotoFirst(void); +void __cdecl LegoRR::Info_GotoFirst(void) +{ + if (infoGlobs.infoMessageCount != 0 && infoGlobs.infoMessageTable[0].instanceCount != 0) { + if (infoGlobs.infoMessageTable[0].instance->object != nullptr && + infoGlobs.infoMessageTable[0].instance->blockPos.x > 0 && + infoGlobs.infoMessageTable[0].instance->blockPos.y > 0) { + Lego_Goto(infoGlobs.infoMessageTable[0].instance->object, &infoGlobs.infoMessageTable[0].instance->blockPos, false); + } + } +} // -//void __cdecl LegoRR::Info_UpdateMessage(uint32 handle); +void __cdecl LegoRR::Info_UpdateMessage(uint32 handle) +{ + if (handle < infoGlobs.infoMessageCount) { + if (handle != 0) { + // Copies the selected message to index 0, and shifts the rest up + // The 'next' member is not swapped, as this causes extra movement, which differs from the original + InfoMessage infoMessage = infoGlobs.infoMessageTable[handle]; + + for (int i = handle; i != 0; i--) { + infoGlobs.infoMessageTable[i].instance = infoGlobs.infoMessageTable[i - 1].instance; + infoGlobs.infoMessageTable[i].instanceCount = infoGlobs.infoMessageTable[i - 1].instanceCount; + infoGlobs.infoMessageTable[i].float_c = infoGlobs.infoMessageTable[i - 1].float_c; + infoGlobs.infoMessageTable[i].infoType = infoGlobs.infoMessageTable[i - 1].infoType; + //infoGlobs.infoMessageTable[i].next = infoGlobs.infoMessageTable[i - 1].next; + } + + float next = infoGlobs.infoMessageTable[0].next; + infoGlobs.infoMessageTable[0] = infoMessage; + infoGlobs.infoMessageTable[0].next = next; + } + + infoGlobs.flags |= (Info_GlobFlags::INFO_GLOB_FLAG_UNK_1 | Info_GlobFlags::INFO_GLOB_FLAG_UNK_2); + } +} // -//void __cdecl LegoRR::Info_PopFirstMessage(void); +void __cdecl LegoRR::Info_PopFirstMessage(void) +{ + if (infoGlobs.infoMessageCount != 0) { + Info_RemoveMessage(0); + } +} // -//void __cdecl LegoRR::Info_RemoveMessage(uint32 handle); +void __cdecl LegoRR::Info_RemoveMessage(uint32 handle) +{ + if (handle < infoGlobs.infoMessageCount) { + /// FIXME: Free the message instance + InfoMessageInstance* instance = Info_RemoveMessageInstance(handle, 0); + if (instance != nullptr && instance->text != nullptr) { + Gods98::Mem_Free(instance->text); + } + + if (infoGlobs.infoMessageTable[handle].instanceCount == 0) { + for (uint32 i = 0; i < infoGlobs.infoMessageCount - 1; i++) { + infoGlobs.infoMessageTable[i].instance = infoGlobs.infoMessageTable[i + 1].instance; + infoGlobs.infoMessageTable[i].instanceCount = infoGlobs.infoMessageTable[i + 1].instanceCount; + infoGlobs.infoMessageTable[i].float_c = infoGlobs.infoMessageTable[i + 1].float_c; + infoGlobs.infoMessageTable[i].infoType = infoGlobs.infoMessageTable[i + 1].infoType; + infoGlobs.infoMessageTable[i].next = infoGlobs.infoMessageTable[i + 1].next; + } + + infoGlobs.infoMessageCount--; + } + + if (handle == 0) { + infoGlobs.flags &= ~Info_GlobFlags::INFO_GLOB_FLAG_UNK_1; + } + } +} // -//void __cdecl LegoRR::Info_ClearAllMessages(void); +void __cdecl LegoRR::Info_ClearAllMessages(void) +{ + while (infoGlobs.infoMessageCount != 0) { + Info_RemoveMessage(0); + } +} // -//void __cdecl LegoRR::Info_Draw(real32 elapsedAbs); +void __cdecl LegoRR::Info_Draw(real32 elapsedAbs) +{ + if (infoGlobs.infoMessageCount == 0) { + return; + } + + Point2F pos = { 0.0,330.0 }; + + for (uint32 i = 0; i < infoGlobs.infoMessageCount; i++) { + InfoMessage* infoMessage = &infoGlobs.infoMessageTable[i]; + + Gods98::Image* image = (Gods98::Image*)infoGlobs.infoDataTable[infoMessage->infoType].ptr_4; + + if (infoMessage->next < pos.y) { + infoMessage->next -= elapsedAbs * -8.0f; + } + if (infoMessage->next > pos.y) { + infoMessage->next = pos.y; + } + + if (infoMessage->float_c < 0.0) { + infoMessage->float_c -= elapsedAbs * -3.0f; + } + if (infoMessage->float_c > 0.0) { + infoMessage->float_c = 0.0; + } + + pos = { infoMessage->float_c, infoMessage->next }; + Gods98::Image_DisplayScaled(image, nullptr, &pos, nullptr); + + if (pos.y < 0.0) { + pos.y = 0.0; + Gods98::Image_DisplayScaled(infoGlobs.OverFlowImage, nullptr, &pos, nullptr); + break; + } + + sint32 y = static_cast(pos.y) + 10; + sint32 x = static_cast(pos.x) + 2; + + Gods98::Font_PrintF(Interface_GetFont(), x, y, "%i", infoMessage->instanceCount); + + pos.y -= image->height; + } +} // -//void __cdecl LegoRR::Info_DrawPanel(real32 elapsedAbs); - +void __cdecl LegoRR::Info_DrawPanel(real32 elapsedAbs) +{ + if ((infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_1) == Info_GlobFlags::INFO_GLOB_FLAG_NONE || + infoGlobs.infoMessageCount == 0) { + if ((panelGlobs.panelTable[Panel_Information].flags & PanelDataFlags::PANEL_FLAG_CLOSED) == 0) { + Panel_ToggleOpenClosed(Panel_Information); + } + } + else { + if ((panelGlobs.panelTable[Panel_Information].flags & PanelDataFlags::PANEL_FLAG_OPEN) == 0) { + Panel_ToggleOpenClosed(Panel_Information); + } + + if ((infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_2) != Info_GlobFlags::INFO_GLOB_FLAG_NONE) { + Panel_TextWindow_Clear(panelGlobs.infoTextWnd); + + const char* message = infoGlobs.infoMessageTable[0].instance->text; + if (message == nullptr) { + message = infoGlobs.infoDataTable[infoGlobs.infoMessageTable[0].infoType].text; + } + + uint32 count = 0xFFFFFFFF; + const char* ptr = message; + while (*ptr != '\0') { + if (count == 0) { + break; + } + + count--; + ptr++; + } + count = ~count + 1; + + if (message[count] == '\0') { + Panel_TextWindow_PrintF(panelGlobs.infoTextWnd, message); + } + else { + Panel_TextWindow_PrintF(panelGlobs.infoTextWnd, message, (uint32)message[count], (uint32)message[count + 1], + (uint32)message[count + 2], (uint32)message[count + 3], (uint32)message[count + 4]); + } + + Panel_TextWindow_Update(panelGlobs.infoTextWnd, infoGlobs.int_6ec, elapsedAbs); + Info_FUN_0041a180(); + infoGlobs.flags |= Info_GlobFlags::INFO_GLOB_FLAG_UNK_2; + } + } + + Panel_TextWindow_Update(panelGlobs.infoTextWnd, infoGlobs.int_6ec, elapsedAbs); +} + +// Handles left mouse button press // -//bool32 __cdecl LegoRR::Info_Update_FUN_0041a0d0(sint32 mouseX, sint32 mouseY, bool32 leftReleased); - +bool32 __cdecl LegoRR::Info_Update_FUN_0041a0d0(sint32 mouseX, sint32 mouseY, bool32 leftReleased) +{ + if (infoGlobs.infoMessageCount == 0) { + return false; + } + + for (uint32 i = 0; i < infoGlobs.infoMessageCount; i++) { + const InfoMessage* infoMessage = &infoGlobs.infoMessageTable[i]; + const InfoData* infoData = &infoGlobs.infoDataTable[infoMessage->infoType]; + + const Gods98::Image* image = (Gods98::Image*)infoData->ptr_4; + + if (0 <= mouseX && mouseX <= static_cast(image->width)) { + if (infoMessage->next <= mouseY && mouseY <= infoMessage->next + image->height) { + if (leftReleased) { + if (((infoGlobs.flags & Info_GlobFlags::INFO_GLOB_FLAG_UNK_1) != Info_GlobFlags::INFO_GLOB_FLAG_NONE) && (i == 0)) { + Panel_ToggleOpenClosed(Panel_Information); + infoGlobs.flags &= ~Info_GlobFlags::INFO_GLOB_FLAG_UNK_1; + return true; + } + + Info_UpdateMessage(i); + } + return true; + } + } + } + + return false; +} + +// Works updates whether the info is scrollable // -//void __cdecl LegoRR::Info_FUN_0041a180(void); - +void __cdecl LegoRR::Info_FUN_0041a180(void) +{ + uint32 lineCount; + uint32 lineCapacity; + Panel_TextWindow_GetInfo(panelGlobs.infoTextWnd, &lineCount, &lineCapacity); + + int availableLines = (int)lineCount - (int)lineCapacity; + if (availableLines < 0) { + availableLines = 0; + } + + ScrollInfo_SetSubStruct28_Fields1C_20(0, availableLines, 0); + Info_UpdateInt6EC_FromScrollInfo(); +} + +// Sets the scroll line position // -//void __cdecl LegoRR::Info_UpdateInt6EC_FromScrollInfo(void); +void __cdecl LegoRR::Info_UpdateInt6EC_FromScrollInfo(void) +{ + sint32 field1c; + sint32 field20; + ScrollInfo_GetSubStruct28_Fields1C_20(0, &field1c, &field20); + + infoGlobs.int_6ec = field1c - field20; +} // -//void __cdecl LegoRR::Info_FUN_0041a1f0(real32 elapsed); +void __cdecl LegoRR::Info_FUN_0041a1f0(real32 elapsed) +{ + for (int i = 0; i < Info_Type_Count; i++) { + infoGlobs.infoDataTable[i].float_c -= elapsed; + } +} #pragma endregion diff --git a/src/openlrr/game/interface/InfoMessages.h b/src/openlrr/game/interface/InfoMessages.h index 440cd72..2f380f0 100644 --- a/src/openlrr/game/interface/InfoMessages.h +++ b/src/openlrr/game/interface/InfoMessages.h @@ -23,7 +23,7 @@ struct InfoMessageInstance; ******** Constants **********************************************************************************/ -#pragma region Constants +#pragma region Constants #pragma endregion @@ -100,7 +100,7 @@ struct InfoMessage // [LegoRR/Info.c|struct:0x14] /*04,4*/ uint32 instanceCount; /*08,4*/ Info_Type infoType; /*0c,4*/ real32 float_c; - /*10,4*/ InfoMessage* next; + /*10,4*/ real32 next; /*14*/ }; assert_sizeof(InfoMessage, 0x14); @@ -114,11 +114,38 @@ struct Info_Globs // [LegoRR/Info.c|struct:0x6f4|tags:GLOBS] /*6e0,4*/ uint32 infoMessageCount; /*6e4,4*/ Gods98::Font* font; /*6e8,4*/ Gods98::Image* OverFlowImage; - /*6ec,4*/ sint32 int_6ec; + /*6ec,4*/ sint32 int_6ec; // To do with the scroll position of the info message text /*6f0,4*/ Info_GlobFlags flags; /*6f4*/ }; -assert_sizeof(Info_Globs, 0x6f4); +assert_sizeof(Info_Globs, 0x6f4); + + +struct SearchInfoObject_8 // [LegoRR/search.c|struct:0x8] +{ + /*0,4*/ LegoObject* object; + /*4,4*/ uint32 index; + /*8*/ +}; +assert_sizeof(SearchInfoObject_8, 0x8); + + +struct SearchInfoBlockPos_8 // [LegoRR/search.c|struct:0x8] +{ + /*0,4*/ const Point2I* pBlockPos; + /*4,4*/ uint32 index; + /*8*/ +}; +assert_sizeof(SearchInfoBlockPos_8, 0x8); + + +struct SearchInfoObjectBlockPos_8 // [LegoRR/search.c|struct:0x8] +{ + /*0,4*/ LegoObject* optObject; + /*4,4*/ const Point2I* optBlockPos; + /*8*/ +}; +assert_sizeof(SearchInfoObjectBlockPos_8, 0x8); #pragma endregion @@ -155,161 +182,161 @@ __inline bool32 Info_IsAutoGameSpeed(void) { return (infoGlobs.flags & INFO_GLOB // -#define Info_Initialise ((void (__cdecl* )(Gods98::Font* font))0x00419160) -//void __cdecl Info_Initialise(Gods98::Font* font); +//#define Info_Initialise ((void (__cdecl* )(Gods98::Font* font))0x00419160) +void __cdecl Info_Initialise(Gods98::Font* font); // -#define Info_GetInfoType ((bool32 (__cdecl* )(const char* infoName, OUT Info_Type* infoType))0x00419310) -//bool32 __cdecl Info_GetInfoType(const char* infoName, OUT Info_Type* infoType); +//#define Info_GetInfoType ((bool32 (__cdecl* )(const char* infoName, OUT Info_Type* infoType))0x00419310) +bool32 __cdecl Info_GetInfoType(const char* infoName, OUT Info_Type* infoType); // -#define Info_GetTypePtr4 ((void* (__cdecl* )(Info_Type infoType))0x00419350) -//void* __cdecl Info_GetTypePtr4(Info_Type infoType); +//#define Info_GetTypePtr4 ((void* (__cdecl* )(Info_Type infoType))0x00419350) +void* __cdecl Info_GetTypePtr4(Info_Type infoType); // -#define Info_SetOverFlowImageFile ((void (__cdecl* )(const char* filename))0x00419360) -//void __cdecl Info_SetOverFlowImageFile(const char* filename); +//#define Info_SetOverFlowImageFile ((void (__cdecl* )(const char* filename))0x00419360) +void __cdecl Info_SetOverFlowImageFile(const char* filename); // -#define Info_SetAutoGameSpeed ((void (__cdecl* )(bool32 autoOn))0x00419380) -//void __cdecl Info_SetAutoGameSpeed(bool32 autoOn); +//#define Info_SetAutoGameSpeed ((void (__cdecl* )(bool32 autoOn))0x00419380) +void __cdecl Info_SetAutoGameSpeed(bool32 autoOn); // -#define Info_SetTypeChangeGameSpeed ((void (__cdecl* )(Info_Type infoType, bool32 changeSpeedOn))0x004193a0) -//void __cdecl Info_SetTypeChangeGameSpeed(Info_Type infoType, bool32 changeSpeedOn); +//#define Info_SetTypeChangeGameSpeed ((void (__cdecl* )(Info_Type infoType, bool32 changeSpeedOn))0x004193a0) +void __cdecl Info_SetTypeChangeGameSpeed(Info_Type infoType, bool32 changeSpeedOn); // -#define Info_SetTypeText ((void (__cdecl* )(Info_Type infoType, const char* text))0x004193e0) -//void __cdecl Info_SetTypeText(Info_Type infoType, const char* text); +//#define Info_SetTypeText ((void (__cdecl* )(Info_Type infoType, const char* text))0x004193e0) +void __cdecl Info_SetTypeText(Info_Type infoType, const char* text); // -#define Info_SetTypeFlag_20000 ((void (__cdecl* )(Info_Type infoType, bool32 setFlag20000))0x00419420) -//void __cdecl Info_SetTypeFlag_20000(Info_Type infoType, bool32 setFlag20000); +//#define Info_SetTypeFlag_20000 ((void (__cdecl* )(Info_Type infoType, bool32 setFlag20000))0x00419420) +void __cdecl Info_SetTypeFlag_20000(Info_Type infoType, bool32 setFlag20000); // -#define Info_SetText_internal ((void (__cdecl* )(const char* text, char** pInfoText))0x00419460) -//void __cdecl Info_SetText_internal(const char* text, char** pInfoText); +//#define Info_SetText_internal ((void (__cdecl* )(const char* text, char** pInfoText))0x00419460) +void __cdecl Info_SetText_internal(const char* text, char** pInfoText); // -#define Info_SetTypeImageFile ((void (__cdecl* )(Info_Type infoType, const char* filename))0x00419580) -//void __cdecl Info_SetTypeImageFile(Info_Type infoType, const char* filename); +//#define Info_SetTypeImageFile ((void (__cdecl* )(Info_Type infoType, const char* filename))0x00419580) +void __cdecl Info_SetTypeImageFile(Info_Type infoType, const char* filename); // -#define Info_SetTypeSFX ((void (__cdecl* )(Info_Type infoType, SFX_ID sfxID))0x004195b0) -//void __cdecl Info_SetTypeSFX(Info_Type infoType, SFX_ID sfxID); +//#define Info_SetTypeSFX ((void (__cdecl* )(Info_Type infoType, SFX_ID sfxID))0x004195b0) +void __cdecl Info_SetTypeSFX(Info_Type infoType, SFX_ID sfxID); // -#define Info_EnumerateMessageInstances ((bool32 (__cdecl* )(sint32 handle, InfoEnumerateCallback callback, void* data))0x004195d0) -//bool32 __cdecl Info_EnumerateMessageInstances(sint32 handle, InfoEnumerateCallback callback, void* data); +//#define Info_EnumerateMessageInstances ((bool32 (__cdecl* )(sint32 handle, InfoEnumerateCallback callback, void* data))0x004195d0) +bool32 __cdecl Info_EnumerateMessageInstances(sint32 handle, InfoEnumerateCallback callback, void* data); // -#define Info_AddMessageInstance ((void (__cdecl* )(sint32 handle, InfoMessageInstance* instance))0x00419620) -//void __cdecl Info_AddMessageInstance(sint32 handle, InfoMessageInstance* instance); +//#define Info_AddMessageInstance ((void (__cdecl* )(sint32 handle, InfoMessageInstance* instance))0x00419620) +void __cdecl Info_AddMessageInstance(sint32 handle, InfoMessageInstance* instance); // -#define Info_GetMessageInstance ((InfoMessageInstance* (__cdecl* )(sint32 handle, sint32 instanceIndex))0x004196b0) -//InfoMessageInstance* __cdecl Info_GetMessageInstance(sint32 handle, sint32 instanceIndex); +//#define Info_GetMessageInstance ((InfoMessageInstance* (__cdecl* )(sint32 handle, sint32 instanceIndex))0x004196b0) +InfoMessageInstance* __cdecl Info_GetMessageInstance(sint32 handle, sint32 instanceIndex); // -#define Info_RemoveMessageInstance ((InfoMessageInstance* (__cdecl* )(sint32 handle, sint32 instanceIndex))0x004196e0) -//InfoMessageInstance* __cdecl Info_RemoveMessageInstance(sint32 handle, sint32 instanceIndex); +//#define Info_RemoveMessageInstance ((InfoMessageInstance* (__cdecl* )(sint32 handle, sint32 instanceIndex))0x004196e0) +InfoMessageInstance* __cdecl Info_RemoveMessageInstance(sint32 handle, sint32 instanceIndex); -// Finds the index of the InfoMessageInstance that references the specified object. +// Finds the index of the InfoMessageInstance that references the specified object. // DATA: SearchInfoObject_8* // -#define Info_Callback_FindObjectReference ((bool32 (__cdecl* )(InfoMessageInstance* instance, void* search))0x00419740) -//bool32 __cdecl Info_Callback_FindObjectReference(InfoMessageInstance* instance, void* search); +//#define Info_Callback_FindObjectReference ((bool32 (__cdecl* )(InfoMessageInstance* instance, void* search))0x00419740) +bool32 __cdecl Info_Callback_FindObjectReference(InfoMessageInstance* instance, void* search); // Removes all info messages referencing the specified object. // -#define Info_RemoveObjectReferences ((void (__cdecl* )(LegoObject* liveObj))0x00419760) -//void __cdecl Info_RemoveObjectReferences(LegoObject* liveObj); +//#define Info_RemoveObjectReferences ((void (__cdecl* )(LegoObject* liveObj))0x00419760) +void __cdecl Info_RemoveObjectReferences(LegoObject* liveObj); // DATA: SearchInfoBlockPos_8* // -#define Info_Callback_FindBlockPos ((bool32 (__cdecl* )(InfoMessageInstance* infoInstance, void* search))0x004197f0) -//bool32 __cdecl Info_Callback_FindBlockPos(InfoMessageInstance* infoInstance, void* search); +//#define Info_Callback_FindBlockPos ((bool32 (__cdecl* )(InfoMessageInstance* infoInstance, void* search))0x004197f0) +bool32 __cdecl Info_Callback_FindBlockPos(InfoMessageInstance* infoInstance, void* search); // -#define Info_RemoveAllAtBlockPos ((void (__cdecl* )(const Point2I* blockPos))0x00419820) -//void __cdecl Info_RemoveAllAtBlockPos(const Point2I* blockPos); +//#define Info_RemoveAllAtBlockPos ((void (__cdecl* )(const Point2I* blockPos))0x00419820) +void __cdecl Info_RemoveAllAtBlockPos(const Point2I* blockPos); // DATA: SearchInfoObjectBlockPos_8* // -#define Info_Callback_FindObjectAndBlockPos ((bool32 (__cdecl* )(InfoMessageInstance* instance, void* search))0x004198d0) -//bool32 __cdecl Info_Callback_FindObjectAndBlockPos(InfoMessageInstance* instance, void* search); +//#define Info_Callback_FindObjectAndBlockPos ((bool32 (__cdecl* )(InfoMessageInstance* instance, void* search))0x004198d0) +bool32 __cdecl Info_Callback_FindObjectAndBlockPos(InfoMessageInstance* instance, void* search); // -#define Info_HasTypeAtObjectOrBlockPos ((bool32 (__cdecl* )(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos))0x00419920) -//bool32 __cdecl Info_HasTypeAtObjectOrBlockPos(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); +//#define Info_HasTypeAtObjectOrBlockPos ((bool32 (__cdecl* )(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos))0x00419920) +bool32 __cdecl Info_HasTypeAtObjectOrBlockPos(Info_Type infoType, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); // -#define Info_CreateInstance ((InfoMessageInstance* (__cdecl* )(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text))0x004199b0) -//InfoMessageInstance* __cdecl Info_CreateInstance(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text); +//#define Info_CreateInstance ((InfoMessageInstance* (__cdecl* )(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text))0x004199b0) +InfoMessageInstance* __cdecl Info_CreateInstance(OPTIONAL const Point2I* blockPos, OPTIONAL LegoObject* liveObj, OPTIONAL const char* text); // -#define Info_SetFlag4 ((void (__cdecl* )(bool32 state))0x00419a10) -//void __cdecl Info_SetFlag4(bool32 state); +//#define Info_SetFlag4 ((void (__cdecl* )(bool32 state))0x00419a10) +void __cdecl Info_SetFlag4(bool32 state); // -#define Info_HasTypeText ((bool32 (__cdecl* )(Info_Type infoType))0x00419a30) -//bool32 __cdecl Info_HasTypeText(Info_Type infoType); +//#define Info_HasTypeText ((bool32 (__cdecl* )(Info_Type infoType))0x00419a30) +bool32 __cdecl Info_HasTypeText(Info_Type infoType); // -#define Info_FindExistingMessageType ((sint32 (__cdecl* )(Info_Type infoType))0x00419a50) -//sint32 __cdecl Info_FindExistingMessageType(Info_Type infoType); +//#define Info_FindExistingMessageType ((sint32 (__cdecl* )(Info_Type infoType))0x00419a50) +sint32 __cdecl Info_FindExistingMessageType(Info_Type infoType); // -#define Info_FUN_00419a80 ((real32 (__cdecl* )(void))0x00419a80) -//real32 __cdecl Info_FUN_00419a80(void); +//#define Info_FUN_00419a80 ((real32 (__cdecl* )(void))0x00419a80) +real32 __cdecl Info_FUN_00419a80(void); // -#define Info_Send ((void (__cdecl* )(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos))0x00419ab0) -//void __cdecl Info_Send(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); +//#define Info_Send ((void (__cdecl* )(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos))0x00419ab0) +void __cdecl Info_Send(Info_Type infoType, OPTIONAL const char* opt_text, OPTIONAL LegoObject* liveObj, OPTIONAL const Point2I* blockPos); // -#define Info_GotoFirst ((void (__cdecl* )(void))0x00419cd0) -//void __cdecl Info_GotoFirst(void); +//#define Info_GotoFirst ((void (__cdecl* )(void))0x00419cd0) +void __cdecl Info_GotoFirst(void); // -#define Info_UpdateMessage ((void (__cdecl* )(uint32 handle))0x00419d10) -//void __cdecl Info_UpdateMessage(uint32 handle); +//#define Info_UpdateMessage ((void (__cdecl* )(uint32 handle))0x00419d10) +void __cdecl Info_UpdateMessage(uint32 handle); // -#define Info_PopFirstMessage ((void (__cdecl* )(void))0x00419d90) -//void __cdecl Info_PopFirstMessage(void); +//#define Info_PopFirstMessage ((void (__cdecl* )(void))0x00419d90) +void __cdecl Info_PopFirstMessage(void); // -#define Info_RemoveMessage ((void (__cdecl* )(uint32 handle))0x00419db0) -//void __cdecl Info_RemoveMessage(uint32 handle); +//#define Info_RemoveMessage ((void (__cdecl* )(uint32 handle))0x00419db0) +void __cdecl Info_RemoveMessage(uint32 handle); // -#define Info_ClearAllMessages ((void (__cdecl* )(void))0x00419e40) -//void __cdecl Info_ClearAllMessages(void); +//#define Info_ClearAllMessages ((void (__cdecl* )(void))0x00419e40) +void __cdecl Info_ClearAllMessages(void); // -#define Info_Draw ((void (__cdecl* )(real32 elapsedAbs))0x00419e60) -//void __cdecl Info_Draw(real32 elapsedAbs); +//#define Info_Draw ((void (__cdecl* )(real32 elapsedAbs))0x00419e60) +void __cdecl Info_Draw(real32 elapsedAbs); // -#define Info_DrawPanel ((void (__cdecl* )(real32 elapsedAbs))0x00419fb0) -//void __cdecl Info_DrawPanel(real32 elapsedAbs); +//#define Info_DrawPanel ((void (__cdecl* )(real32 elapsedAbs))0x00419fb0) +void __cdecl Info_DrawPanel(real32 elapsedAbs); // -#define Info_Update_FUN_0041a0d0 ((bool32 (__cdecl* )(sint32 mouseX, sint32 mouseY, bool32 leftReleased))0x0041a0d0) -//bool32 __cdecl Info_Update_FUN_0041a0d0(sint32 mouseX, sint32 mouseY, bool32 leftReleased); +//#define Info_Update_FUN_0041a0d0 ((bool32 (__cdecl* )(sint32 mouseX, sint32 mouseY, bool32 leftReleased))0x0041a0d0) +bool32 __cdecl Info_Update_FUN_0041a0d0(sint32 mouseX, sint32 mouseY, bool32 leftReleased); // -#define Info_FUN_0041a180 ((void (__cdecl* )(void))0x0041a180) -//void __cdecl Info_FUN_0041a180(void); +//#define Info_FUN_0041a180 ((void (__cdecl* )(void))0x0041a180) +void __cdecl Info_FUN_0041a180(void); // -#define Info_UpdateInt6EC_FromScrollInfo ((void (__cdecl* )(void))0x0041a1c0) -//void __cdecl Info_UpdateInt6EC_FromScrollInfo(void); +//#define Info_UpdateInt6EC_FromScrollInfo ((void (__cdecl* )(void))0x0041a1c0) +void __cdecl Info_UpdateInt6EC_FromScrollInfo(void); // -#define Info_FUN_0041a1f0 ((void (__cdecl* )(real32 elapsed))0x0041a1f0) -//void __cdecl Info_FUN_0041a1f0(real32 elapsed); +//#define Info_FUN_0041a1f0 ((void (__cdecl* )(real32 elapsed))0x0041a1f0) +void __cdecl Info_FUN_0041a1f0(real32 elapsed); #pragma endregion diff --git a/src/openlrr/interop.cpp b/src/openlrr/interop.cpp index 3b4f587..41e0e13 100644 --- a/src/openlrr/interop.cpp +++ b/src/openlrr/interop.cpp @@ -51,6 +51,7 @@ #include "game/interface/hud/ObjInfo.h" #include "game/interface/Advisor.h" #include "game/interface/Encyclopedia.h" +#include "game/interface/InfoMessages.h" #include "game/interface/Interface.h" #include "game/interface/Panels.h" #include "game/interface/Pointers.h" @@ -2898,6 +2899,98 @@ bool interop_hook_LegoRR_Game(void) return_interop(result); } +bool interop_hook_LegoRR_InfoMessages(void) +{ + bool result = true; + + // used by: Lego_Initialise + result &= hook_write_jmpret(0x00419160, LegoRR::Info_Initialise); + // used by: Info_SetText_internal, Lego_LoadInfoMessages + result &= hook_write_jmpret(0x00419310, LegoRR::Info_GetInfoType); + // used by: Info_SetText_internal + result &= hook_write_jmpret(0x00419350, LegoRR::Info_GetTypePtr4); + // used by: Info_Initialise + result &= hook_write_jmpret(0x00419360, LegoRR::Info_SetOverFlowImageFile); + // used by: Front_Callback_CycleAutoGameSpeed + result &= hook_write_jmpret(0x00419380, LegoRR::Info_SetAutoGameSpeed); + // used by: Lego_LoadInfoMessages + result &= hook_write_jmpret(0x004193a0, LegoRR::Info_SetTypeChangeGameSpeed); + // used by: Lego_LoadInfoMessages + result &= hook_write_jmpret(0x004193e0, LegoRR::Info_SetTypeText); + // used by: Lego_LoadInfoMessages + result &= hook_write_jmpret(0x00419420, LegoRR::Info_SetTypeFlag_20000); + // used by: Info_SetTypeText, Info_CreateInstance + result &= hook_write_jmpret(0x00419460, LegoRR::Info_SetText_internal); + // used by: Lego_LoadInfoMessages + result &= hook_write_jmpret(0x00419580, LegoRR::Info_SetTypeImageFile); + // used by: Lego_LoadInfoMessages + result &= hook_write_jmpret(0x004195b0, LegoRR::Info_SetTypeSFX); + // used by: Info_RemoveObjectReferences, Info_RemoveAllAtBlockPos, + // Info_HasTypeAtObjectOrBlockPos + result &= hook_write_jmpret(0x004195d0, LegoRR::Info_EnumerateMessageInstances); + // used by: Info_Send + result &= hook_write_jmpret(0x00419620, LegoRR::Info_AddMessageInstance); + // used by: Info_RemoveMessageInstance + result &= hook_write_jmpret(0x004196b0, LegoRR::Info_GetMessageInstance); + // used by: Info_AddMessageInstance, Info_RemoveObjectReferences, + // Info_RemoveAllAtBlockPos, Info_RemoveMessage + result &= hook_write_jmpret(0x004196e0, LegoRR::Info_RemoveMessageInstance); + // used by: Info_RemoveObjectReferences + result &= hook_write_jmpret(0x00419740, LegoRR::Info_Callback_FindObjectReference); + // used by: LegoObject_Remove + result &= hook_write_jmpret(0x00419760, LegoRR::Info_RemoveObjectReferences); + // used by: Info_RemoveAllAtBlockPos + result &= hook_write_jmpret(0x004197f0, LegoRR::Info_Callback_FindBlockPos); + // used by: Level_Block_ClearRubbleLayer + result &= hook_write_jmpret(0x00419820, LegoRR::Info_RemoveAllAtBlockPos); + // used by: Info_HasTypeAtObjectOrBlockPos + result &= hook_write_jmpret(0x004198d0, LegoRR::Info_Callback_FindObjectAndBlockPos); + // used by: Info_Send + result &= hook_write_jmpret(0x00419920, LegoRR::Info_HasTypeAtObjectOrBlockPos); + // used by: Info_Send + result &= hook_write_jmpret(0x004199b0, LegoRR::Info_CreateInstance); + // used by: Lego_LoadLevel, Level_Free + result &= hook_write_jmpret(0x00419a10, LegoRR::Info_SetFlag4); + // used by: Info_Send + result &= hook_write_jmpret(0x00419a30, LegoRR::Info_HasTypeText); + // used by: Info_Send + result &= hook_write_jmpret(0x00419a50, LegoRR::Info_FindExistingMessageType); + // used by: Info_Send + result &= hook_write_jmpret(0x00419a80, LegoRR::Info_FUN_00419a80); + // used by: Construction_Zone_CompleteBuilding, Erode_Update, Fallin_Update, + // Fallin_Block_FUN_0040f260, Lego_MainLoop, Lego_HandleWorldDebugKeys, + // Lego_UpdateFallins, Level_DestroyWall, Level_Block_Reinforce, + // HiddenObject_ExposeBlock, LegoObject_AddThisDrainedCrystals, Level_GenerateCrystal, + // LegoObject_TryGenerateSlug, LegoObject_TryGenerateRMonster, LegoObject_UpdateRemoval, + // LegoObject_UpdateElapsedTimes, LegoObject_Callback_Update, LegoObject_ProccessCarriedObjects, + // LegoObject_TrainMiniFigure_instantunk, LegoObject_FUN_00447880, LegoObject_SimpleObject_MoveAnimation + result &= hook_write_jmpret(0x00419ab0, LegoRR::Info_Send); + // used by: Panel_CheckCollision + result &= hook_write_jmpret(0x00419cd0, LegoRR::Info_GotoFirst); + // used by: Info_Update_FUN_0041a0d0, Panel_CheckCollision + result &= hook_write_jmpret(0x00419d10, LegoRR::Info_UpdateMessage); + // used by: Panel_CheckCollision + result &= hook_write_jmpret(0x00419d90, LegoRR::Info_PopFirstMessage); + // used by: Info_ClearAllMessages, Info_PopFirstMessage, Info_RemoveAllAtBlockPos, Info_RemoveObjectReferences + result &= hook_write_jmpret(0x00419db0, LegoRR::Info_RemoveMessage); + // used by: Level_Free + result &= hook_write_jmpret(0x00419e40, LegoRR::Info_ClearAllMessages); + // used by: Lego_MainLoop + result &= hook_write_jmpret(0x00419e60, LegoRR::Info_Draw); + // used by: Lego_MainLoop + result &= hook_write_jmpret(0x00419fb0, LegoRR::Info_DrawPanel); + // used by: Lego_HandleWorld + result &= hook_write_jmpret(0x0041a0d0, LegoRR::Info_Update_FUN_0041a0d0); + // used by: Info_DrawPanel + result &= hook_write_jmpret(0x0041a180, LegoRR::Info_FUN_0041a180); + // used by: Info_FUN_0041a180, Panel_CheckCollision + result &= hook_write_jmpret(0x0041a1c0, LegoRR::Info_UpdateInt6EC_FromScrollInfo); + // used by: Lego_MainLoop + result &= hook_write_jmpret(0x0041a1f0, LegoRR::Info_FUN_0041a1f0); + + return_interop(result); +} + bool interop_hook_LegoRR_Interface(void) { bool result = true; @@ -4572,6 +4665,7 @@ bool interop_hook_all(void) result &= interop_hook_LegoRR_Fallin(); result &= interop_hook_LegoRR_FrontEnd(); result &= interop_hook_LegoRR_Game(); + result &= interop_hook_LegoRR_InfoMessages(); result &= interop_hook_LegoRR_Interface(); result &= interop_hook_LegoRR_LegoCamera(); result &= interop_hook_LegoRR_LightEffects(); diff --git a/src/openlrr/interop.h b/src/openlrr/interop.h index 1f23d62..931e461 100644 --- a/src/openlrr/interop.h +++ b/src/openlrr/interop.h @@ -72,6 +72,7 @@ bool interop_hook_LegoRR_Erosion(void); bool interop_hook_LegoRR_Fallin(void); bool interop_hook_LegoRR_FrontEnd(void); bool interop_hook_LegoRR_Game(void); +bool interop_hook_LegoRR_InfoMessages(void); bool interop_hook_LegoRR_Interface(void); bool interop_hook_LegoRR_LegoCamera(void); bool interop_hook_LegoRR_LightEffects(void);