From 6b2a734d706f516db0e29de7c23c5f0a687b1acd Mon Sep 17 00:00:00 2001 From: CCHyper <73803386+CCHyper@users.noreply.github.com> Date: Tue, 27 Dec 2022 13:42:32 +0000 Subject: [PATCH] Adds collection of patches to remove hardcoded properties from various game object types. --- src/extensions/rules/rulesext.cpp | 222 +++++++++++++++++++++++++++++- src/extensions/rules/rulesext.h | 2 +- src/vinifera/vinifera_hooks.cpp | 71 ++++++++++ 3 files changed, 292 insertions(+), 3 deletions(-) diff --git a/src/extensions/rules/rulesext.cpp b/src/extensions/rules/rulesext.cpp index 8d01fe9ab..54af27747 100644 --- a/src/extensions/rules/rulesext.cpp +++ b/src/extensions/rules/rulesext.cpp @@ -37,6 +37,9 @@ #include "wwcrc.h" #include "noinit.h" #include "swizzle.h" +#include "addon.h" +#include "session.h" +#include "tibsun_inline.h" #include "vinifera_saveload.h" #include "asserthandler.h" #include "debughandler.h" @@ -78,6 +81,18 @@ RulesClassExtension::RulesClassExtension(const RulesClass *this_ptr) : IsShowSuperWeaponTimers(true) { //if (this_ptr) EXT_DEBUG_TRACE("RulesClassExtension::RulesClassExtension - 0x%08X\n", (uintptr_t)(ThisPtr)); + + /** + * Due to the changes made when addressing issues #632, 633, and 635, we + * need change the default engineer capture values. These values are from + * Red Alert 1 MPLAYER.INI, and they match the expected hardcoded behavior + * of the Multi Engineer logic in the release version of Tiberian Sun. + * + * Fixing the default values here ensures Multi-Engineer works in Tiberian Sun + * without manually fixing up the ini data (which is required for Firestorm). + */ + This()->EngineerDamage = 0.33f; + This()->EngineerCaptureLevel = 0.66f; } @@ -276,7 +291,7 @@ void RulesClassExtension::Process(CCINIClass &ini) /** * Fixup various inconsistencies in the original INI files. */ - Fixups(); + Fixups(ini); } @@ -510,8 +525,210 @@ void RulesClassExtension::Check() * * @author: CCHyper */ -void RulesClassExtension::Fixups() +void RulesClassExtension::Fixups(CCINIClass &ini) { + DEBUG_INFO("Rules::Fixups(enter)\n"); + + /** + * These are the CRC values for the unmodified ini files, TS2.03EN. + */ + static const int Unmodified_RuleINI_CRC = 0x9F3ECD2A; + static const int Unmodified_FSRuleINI_CRC = 0xA0738E22; + + /** + * Fetch the unique crc values for both rule databases. + */ + int rule_crc = RuleINI->Get_Unique_ID(); + DEV_DEBUG_INFO("Rules: RuleINI CRC = %lX\n", rule_crc); + + int fsrule_crc = FSRuleINI.Get_Unique_ID(); + if (Addon_Installed(ADDON_FIRESTORM)) { + DEV_DEBUG_INFO("Rules: FSRuleINI CRC = %lX\n", fsrule_crc); + } + + /** + * Check to see if the ini files have been modified. + */ + bool is_rule_unmodified = false; + if (rule_crc == Unmodified_RuleINI_CRC) { + DEBUG_INFO("Rules: RuleINI is unmodified (version 2.03).\n"); + is_rule_unmodified = true; + } + bool is_fsrule_unmodified = false; + if (Addon_Installed(ADDON_FIRESTORM)) { + if (fsrule_crc == Unmodified_FSRuleINI_CRC) { + DEBUG_INFO("Rules: FSRuleINI is unmodified (version 2.03).\n"); + is_fsrule_unmodified = true; + } + } + + /** + * Detect which unmodified ini file we are currently processing. + */ + bool is_ruleini = false; + if (ini.Get_Unique_ID() == Unmodified_RuleINI_CRC) { + DEV_DEBUG_INFO("Rules: Current INI is RuleINI.\n"); + is_ruleini = true; + } + bool is_fsruleini = false; + if (Addon_Installed(ADDON_FIRESTORM) && ini.Get_Unique_ID() == Unmodified_FSRuleINI_CRC) { + DEV_DEBUG_INFO("Rules: Current INI is FSRuleINI.\n"); + is_fsruleini = true; + } + + /** + * #issue-554 + * + * Various game object types have hardcoded overrides in the binary. We now + * remove those overrides so they can be modified via RULES.INI, but we still + * need to retain the expected gameplay in the original game. + * + * So, we make sure the game has detected the original, unmodified RULES.INI + * and perform this fixup's for each of the object types. + * + * Match criteria; + * - Are we currently processing RuleINI? + * - Does the name of the object exist? + */ + if (is_ruleini && is_rule_unmodified) { + + ObjectTypeClass *objecttype = nullptr; + TechnoTypeClass *technotype = nullptr; + BuildingTypeClass *buildingtype = nullptr; + WarheadTypeClass *warheadtype = nullptr; + WeaponTypeClass *weapontype = nullptr; + TiberiumClass *tiberium = nullptr; + + /** + * TechnoType "HMEC" is expected to have "Strength" with the value of "1200". + * + * We do an additional check of RTTI_UNITTYPE just to make sure it + * is the expected type. + */ + objecttype = const_cast(ObjectTypeClass::As_Pointer("HMEC")); + if (objecttype && objecttype->What_Am_I() == RTTI_UNITTYPE) { + DEBUG_WARNING("Rules: Changing \"MaxStrength\" for ObjectType \"%s\" to \"%d\"!\n", objecttype->Name(), 1200); + objecttype->MaxStrength = 1200; + } + + /** + * TechnoType "GAFSDF" is expected to have "GuardRange" with the value of "5", + * and "Cost" with the value of "250". + * + * We do an additional check of RTTI_BUILDINGTYPE just to make sure it + * is the expected type. + */ + technotype = const_cast(TechnoTypeClass::As_Pointer("GAFSDF")); + if (technotype && technotype->What_Am_I() == RTTI_BUILDINGTYPE) { + DEBUG_WARNING("Rules: Changing \"ThreatRange\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), Cell_To_Lepton(5)); + DEBUG_WARNING("Rules: Changing \"Cost\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), 250); + technotype->ThreatRange = Cell_To_Lepton(5); + technotype->Cost = 250; + } + + /** + * TechnoType "GAWALL" is expected to have "GuardRange" with the value of "5", + * and "Cost" with the value of "250". + * + * We do an additional check of RTTI_BUILDINGTYPE just to make sure it + * is the expected type. + */ + technotype = const_cast(TechnoTypeClass::As_Pointer("GAWALL")); + if (technotype && technotype->What_Am_I() == RTTI_BUILDINGTYPE) { + DEBUG_WARNING("Rules: Changing \"ThreatRange\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), Cell_To_Lepton(5)); + DEBUG_WARNING("Rules: Changing \"Cost\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), 250); + technotype->ThreatRange = Cell_To_Lepton(5); + technotype->Cost = 250; + } + + /** + * TechnoType "NAWALL" is expected to have "GuardRange" with the value of "5", + * and "Cost" with the value of "250". + * + * We do an additional check of RTTI_BUILDINGTYPE just to make sure it + * is the expected type. + */ + technotype = const_cast(TechnoTypeClass::As_Pointer("NAWALL")); + if (technotype && technotype->What_Am_I() == RTTI_BUILDINGTYPE) { + DEBUG_WARNING("Rules: Changing \"ThreatRange\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), Cell_To_Lepton(5)); + DEBUG_WARNING("Rules: Changing \"Cost\" for TechnoType \"%s\" to \"%d\"!\n", technotype->Name(), 250); + technotype->ThreatRange = Cell_To_Lepton(5); + technotype->Cost = 250; + } + + /** + * TechnoType "E2" is expected to have "Explodes" with the value of "yes". + * + * We do an additional check of RTTI_INFANTRYTYPE just to make sure it + * is the expected type. + */ + technotype = const_cast(TechnoTypeClass::As_Pointer("E2")); + if (technotype && technotype->What_Am_I() == RTTI_INFANTRYTYPE) { + DEBUG_WARNING("Rules: Changing \"IsExploding\" for TechnoType \"%s\" to \"%s\"!\n", technotype->Name(), "true"); + technotype->IsExploding = true; + } + + /** + * BuildingType "NAFNCE" is expected to have "BaseNormal" with the value of "no". + */ + buildingtype = const_cast(BuildingTypeClass::As_Pointer("NAFNCE")); + if (buildingtype) { + DEBUG_WARNING("Rules: Changing \"IsBase\" for BuildingType \"%s\" to \"%s\"!\n", buildingtype->Name(), "false"); + buildingtype->IsBase = false; + } + + /** + * BuildingType "NAPOST" is expected to have "BaseNormal" with the value of "no". + */ + buildingtype = const_cast(BuildingTypeClass::As_Pointer("NAPOST")); + if (buildingtype) { + DEBUG_WARNING("Rules: Changing \"IsBase\" for BuildingType \"%s\" to \"%s\"!\n", buildingtype->Name(), "false"); + buildingtype->IsBase = false; + } + + /** + * WarheadType "ARTYHE" is expected to have "ProneDamage" with the value of "0.3" + * and "Verses" with the values of "0.4, 0.85, 0.68, 0.35, 0.35". + */ + warheadtype = const_cast(WarheadTypeClass::As_Pointer("ARTYHE")); + if (warheadtype && Session.Type != GAME_NORMAL) { + DEBUG_WARNING("Rules: Changing \"ProneFactor\" for WarheadType \"%s\" to \"%.1f\"!\n", warheadtype->Name(), 0.3); + DEBUG_WARNING("Rules: Changing \"Modifier[ARMOR_NONE]\" for WarheadType \"%s\" to \"%.2f\"!\n", warheadtype->Name(), 0.4); + DEBUG_WARNING("Rules: Changing \"Modifier[ARMOR_WOOD]\" for WarheadType \"%s\" to \"%.2f\"!\n", warheadtype->Name(), 0.85); + DEBUG_WARNING("Rules: Changing \"Modifier[ARMOR_ALUMINUM]\" for WarheadType \"%s\" to \"%.2f\"!\n", warheadtype->Name(), 0.68); + DEBUG_WARNING("Rules: Changing \"Modifier[ARMOR_STEEL]\" for WarheadType \"%s\" to \"%.2f\"!\n", warheadtype->Name(), 0.35); + DEBUG_WARNING("Rules: Changing \"Modifier[ARMOR_CONCRETE]\" for WarheadType \"%s\" to \"%.2f\"!\n", warheadtype->Name(), 0.35); + warheadtype->ProneFactor = 0.3; + warheadtype->Modifier[ARMOR_NONE] = 0.4; + warheadtype->Modifier[ARMOR_WOOD] = 0.85; + warheadtype->Modifier[ARMOR_ALUMINUM] = 0.68; + warheadtype->Modifier[ARMOR_STEEL] = 0.35; + warheadtype->Modifier[ARMOR_CONCRETE] = 0.35; + } + + /** + * WeaponType "155mm" is expected to have "ROF" with the value of "150", + * and "Damage" with the value of "115". + */ + weapontype = const_cast(WeaponTypeClass::As_Pointer("155mm")); + if (weapontype && Session.Type != GAME_NORMAL) { + DEBUG_WARNING("Rules: Changing \"ROF\" for WeaponType \"%s\" to \"%d\"!\n", weapontype->Name(), 150); + DEBUG_WARNING("Rules: Changing \"Attack\" for WeaponType \"%s\" to \"%d\"!\n", weapontype->Name(), 115); + weapontype->ROF = 150; + weapontype->Attack = 115; + } + + /** + * TiberiumType "Vinifera" is expected to have "Power" with the value of "17". + */ + tiberium = const_cast(TiberiumClass::As_Pointer("Vinifera")); + if (tiberium) { + DEBUG_WARNING("Rules: Changing \"IsBase\" for TiberiumType \"%s\" to \"%d\"!\n", tiberium->Name(), 17); + tiberium->Power = 17; + } + + } + HouseTypeClass *housetype = HouseTypes[HOUSE_NOD]; if (housetype) { @@ -573,4 +790,5 @@ void RulesClassExtension::Fixups() } + DEBUG_INFO("Rules::Fixups(exit)\n"); } diff --git a/src/extensions/rules/rulesext.h b/src/extensions/rules/rulesext.h index d8c32ba21..9b3b27a18 100644 --- a/src/extensions/rules/rulesext.h +++ b/src/extensions/rules/rulesext.h @@ -67,7 +67,7 @@ class RulesClassExtension final : public GlobalExtensionClass private: void Check(); - void Fixups(); + void Fixups(CCINIClass &ini); public: /** diff --git a/src/vinifera/vinifera_hooks.cpp b/src/vinifera/vinifera_hooks.cpp index 4eb5d17a6..7d553fa52 100644 --- a/src/vinifera/vinifera_hooks.cpp +++ b/src/vinifera/vinifera_hooks.cpp @@ -50,6 +50,70 @@ #include "asserthandler.h" +/** + * #issue-554 + * + * This function patches out various hardcoded properties for game types. + * + * @author: CCHyper + */ +static void Vinifera_Remove_Hardcoded_Type_Properties() +{ + /** + * Removes hardcoded "Strength=1200" from ObjectType with the name "HMEC". + */ + Patch_Byte_Range(0x00588C5F, 0x90, 6); + Patch_Jump(0x00588C6B, 0x00588C7E); + + /** + * Removes hardcoded values; + * "GuardRange=5" + * "Cost=250" + * + * from TechnoTypes with the names "GAFSDF", "GAWALL" and/or "NAWALL" + */ + Patch_Jump(0x0063BAC8, 0x0063BB6E); + Patch_Jump(0x0063C8E2, 0x0063C988); + + /** + * Removes hardcoded "Explodes=yes" from TechnoType with the name "E2". + */ + Patch_Byte_Range(0x0063BB82, 0x90, 6); + Patch_Jump(0x0063BB8E, 0x0063BBA1); + + /** + * Removes hardcoded "BaseNormal=no" from BuildingTypes with the names "NAFNCE" and/or "NAPOST" + */ + Patch_Byte_Range(0x00440C38, 0x90, 6); + Patch_Jump(0x00440C44, 0x00440C69); + + /** + * Removes hardcoded values; + * "ProneDamage=0.3" + * "Verses=0.4, 0.85, 0.68, 0.35, 0.35" + * + * from WarheadType with the name "ARTYHE". These hardcoded properties + * only applied to multiplayer games, and not the singleplayer campaign. + */ + Patch_Jump(0x0066F4C6, 0x0066F566); + + /** + * Removes hardcoded values; + * "ROF=150" + * "Damage=115" + * + * from WeaponType with the name "155mm". These hardcoded properties + * only applied to multiplayer games, and not the singleplayer campaign. + */ + Patch_Jump(0x00681250, 0x0068129D); + + /** + * Removes hardcoded "Power=17" from TiberiumType "Vinifera". + */ + Patch_Jump(0x00644DB8, 0x00644DD4); +} + + /** * This function is for intercepting the calls to Detach_This_From_All to also * process the object through the extension interface. @@ -890,6 +954,13 @@ void Vinifera_Hooks() Patch_Byte_Range(0x00580377, 0x90, 10); // NewMenuClass::Process_Game_Select #endif + /** + * Remove various hardcoded game object type properties. + * + * See RulesClassExtension::Fixups for more information. + */ + Vinifera_Remove_Hardcoded_Type_Properties(); + /** * Various patches to intercept the games object tracking and heap processing. */