diff --git a/artifacts/config_files/Tweaks.ini b/artifacts/config_files/Tweaks.ini new file mode 100644 index 00000000..44f5609b --- /dev/null +++ b/artifacts/config_files/Tweaks.ini @@ -0,0 +1,13 @@ +;Game engine tweaks + +[Main] + + +[Items] +;Override the PIDs of the standard healing drugs that AI will use in combat +;Set to -1 if you don't want AI to identify this item as a healing drug +;Note: Starting from sfall 4.3.1/3.8.31, you can set a new flag (FlagsExt: 0x04000000) +;in the proto for other items, so AI will use them for healing in combat as well +STIMPAK=40 +SUPER_STIMPAK=144 +HEALING_POWDER=237 diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index b179ade4..de1174aa 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -444,7 +444,7 @@ NPCsTryToSpendExtraAP=0 ;Note that enabling this option can affect the weapon of choice for some NPCs AIBestWeaponFix=1 -;Set to 1 to fix NPCs not taking chem_primary_desire in AI.txt as drug use preference when using drugs in their inventory +;Set to 1 to fix NPCs not taking chem_primary_desire in AI.txt as a preference list when using drugs in their inventory ;Set to 2 to allow NPCs to use only the drugs listed in chem_primary_desire and healing drugs (stimpaks and healing powder) ;Note: chem_primary_desire w/o fixes prevents the specified item PID from being consumed if all three values in the list are the same PID ;chem_primary_desire also works as a priority list of drug items the NPC will try to pick up in combat when they are on the ground @@ -782,7 +782,8 @@ CreditsAtBottom=0 ;Point the next line to an ini file containing the replacement skill data ;SkillsFile=sfall\Skills.ini -;To add additional perks to the game, uncomment the next line and set it to point to a file containing perk information +;To add additional perks to the game, uncomment the next line and point to a file containing perk information +;See the Perks.ini in the modders pack for an example file ;PerksFile=sfall\Perks.ini ;To add additional books to the game, uncomment the next line and point to a file containing book information @@ -792,6 +793,10 @@ CreditsAtBottom=0 ;Point to an ini file containing elevator data ;ElevatorsFile=sfall\Elevators.ini +;Allows you to change some engine parameters for the game mechanics +;See the Tweaks.ini in the modders pack for an example file +;TweaksFile=sfall\Tweaks.ini + ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Debugging] ;Extra sfall configuration settings that can be used by modders diff --git a/sfall/AI.cpp b/sfall/AI.cpp index fea57c1f..3299b4d7 100644 --- a/sfall/AI.cpp +++ b/sfall/AI.cpp @@ -103,28 +103,30 @@ static void __declspec(naked) ai_try_attack_hook_runFix() { static void __declspec(naked) combat_ai_hack() { static const DWORD combat_ai_hack_Ret = 0x42B204; __asm { - mov edx, [ebx + 0x10]; // cap.min_hp + mov edx, [ebx + 0x10]; // cap.min_hp cmp eax, edx; - jl tryHeal; // curr_hp < min_hp + jl tryHeal; // curr_hp < min_hp end: add esp, 4; - jmp combat_ai_hack_Ret; + jmp combat_ai_hack_Ret; // jump to call ai_check_drugs_ tryHeal: - mov eax, esi; - call ai_check_drugs_; - cmp [esi + health], edx; // edx - minimum hp, below which NPC will run away + push ecx; + push esi; // mov eax, esi; + call sfgame_ai_check_drugs; // call ai_check_drugs_; + pop ecx; + cmp [esi + health], edx; // edx - minimum hp, below which NPC will run away jge end; retn; // flee } } -static void __declspec(naked) ai_check_drugs_hook() { +/*static void __declspec(naked) ai_check_drugs_hook() { __asm { call stat_level_; // current hp mov edx, dword ptr [esp + 0x34 - 0x1C + 4]; // ai cap mov edx, [edx + 0x10]; // min_hp cmp eax, edx; // curr_hp < cap.min_hp - cmovl edi, edx; + cmovl edi, edx; // min_hp <- cap.min_hp retn; } } @@ -163,7 +165,7 @@ dontUse: xor eax, eax; retn; } -} +}*/ //////////////////////////////////////////////////////////////////////////////// @@ -657,12 +659,12 @@ void AI_Init() { // Fix to allow fleeing NPC to use drugs MakeCall(0x42B1DC, combat_ai_hack); // Fix for AI not checking minimum hp properly for using stimpaks (prevents premature fleeing) - HookCall(0x428579, ai_check_drugs_hook); + //HookCall(0x428579, ai_check_drugs_hook); // Fix to prevent the use of healing drugs when not necessary - HookCall(0x4287D7, ai_check_drugs_hook_healing); - SafeWrite8(0x4285A8, 2); // set noInvenItem = 2 - SafeWrite8(0x4287A0, 0x8C); // jnz > jl (noInvenItem < 1) + //HookCall(0x4287D7, ai_check_drugs_hook_healing); + //SafeWrite8(0x4285A8, 2); // set noInvenItem = 2 + //SafeWrite8(0x4287A0, 0x8C); // jnz > jl (noInvenItem < 1) // Fix for NPC stuck in fleeing mode when the hit chance of a target was too low HookCall(0x42B1E3, combat_ai_hook_FleeFix); diff --git a/sfall/BugFixes.cpp b/sfall/BugFixes.cpp index 37cd4740..49dbd575 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -2012,7 +2012,7 @@ skip: } } -static DWORD firstItemDrug = -1; +/*static DWORD firstItemDrug = -1; // when there are no more items in the inventory static void __declspec(naked) ai_check_drugs_hack_break() { @@ -2061,7 +2061,7 @@ skip: add esp, 4; jmp ai_check_drugs_hack_Loop; // goto begin loop, search next item } -} +}*/ static void __declspec(naked) cai_cap_save_hook() { __asm { @@ -3628,20 +3628,20 @@ void BugFixes_Init() // Display a pop-up message box about death from radiation HookCall(0x42D733, process_rads_hook_msg); - int drugUsePerfFix = GetConfigInt("Misc", "AIDrugUsePerfFix", 0); - if (drugUsePerfFix > 0) { - dlog("Applying AI drug use preference fix.", DL_FIX); - if (drugUsePerfFix == 1) { - // Fix for AI not taking chem_primary_desire in AI.txt as a preference list when using drugs in the inventory - MakeCall(0x42869D, ai_check_drugs_hack_break); - MakeCall(0x4286AB, ai_check_drugs_hack_check, 1); - MakeCall(0x4286C7, ai_check_drugs_hack_use); - } - // Fix to allow using only the drugs listed in chem_primary_desire and healing drugs (stimpaks and healing powder) - SafeWrite8(0x4286B1, CODETYPE_JumpZ); // jnz > jz (ai_check_drugs_) - SafeWrite8(0x4286C5, CODETYPE_JumpNZ); // jz > jnz (ai_check_drugs_) - dlogr(" Done", DL_FIX); - } + //int drugUsePerfFix = GetConfigInt("Misc", "AIDrugUsePerfFix", 0); + //if (drugUsePerfFix > 0) { + // dlog("Applying AI drug use preference fix.", DL_FIX); + // if (drugUsePerfFix == 1) { + // // Fix for AI not taking chem_primary_desire in AI.txt as a preference list when using drugs in the inventory + // MakeCall(0x42869D, ai_check_drugs_hack_break); + // MakeCall(0x4286AB, ai_check_drugs_hack_check, 1); + // MakeCall(0x4286C7, ai_check_drugs_hack_use); + // } + // // Fix to allow using only the drugs listed in chem_primary_desire and healing drugs (stimpaks and healing powder) + // SafeWrite8(0x4286B1, CODETYPE_JumpZ); // jnz > jz (ai_check_drugs_) + // SafeWrite8(0x4286C5, CODETYPE_JumpNZ); // jz > jnz (ai_check_drugs_) + // dlogr(" Done", DL_FIX); + //} // Fix for chem_primary_desire values in party member AI packets not being saved correctly HookCall(0x42803E, cai_cap_save_hook); diff --git a/sfall/EngineTweaks.cpp b/sfall/EngineTweaks.cpp new file mode 100644 index 00000000..32dd7444 --- /dev/null +++ b/sfall/EngineTweaks.cpp @@ -0,0 +1,36 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include "FalloutEngine.h" + +#include "ReplacementFuncs.h" + +void EngineTweaks_Init() { + std::string tweaksFile = GetConfigString("Misc", "TweaksFile", "", MAX_PATH); + if (!tweaksFile.empty()) { + const char* cTweaksFile = tweaksFile.insert(0, ".\\").c_str(); + + sfgame_SetHealingPID(0, IniGetInt("Items", "STIMPAK", PID_STIMPAK, cTweaksFile)); + sfgame_SetHealingPID(1, IniGetInt("Items", "SUPER_STIMPAK", PID_SUPER_STIMPAK, cTweaksFile)); + sfgame_SetHealingPID(2, IniGetInt("Items", "HEALING_POWDER", PID_HEALING_POWDER, cTweaksFile)); + } +} + +void EngineTweaks_Exit() { +} diff --git a/sfall/EngineTweaks.h b/sfall/EngineTweaks.h new file mode 100644 index 00000000..5f85cc72 --- /dev/null +++ b/sfall/EngineTweaks.h @@ -0,0 +1,22 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void EngineTweaks_Init(); +void EngineTweaks_Exit(); diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index abf83735..0daabce8 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -620,18 +620,47 @@ defaultHandler: } } +/* +static void RemoveInvenObjHook_Script(TGameObj* source, TGameObj* item, long count, long rmType) { + BeginHook(); + argCount = 5; + + args[0] = (DWORD)source; + args[1] = (DWORD)item; + args[2] = count; + args[3] = rmType; // RMOBJ_* + args[4] = 0; // target only from item_move_func_ + + RunHookScript(HOOK_REMOVEINVENOBJ); + EndHook(); +} + +void RemoveInvenObjHook_Invoke(TGameObj* source, TGameObj* item, long count, long rmType) { + if (HookHasScript(HOOK_REMOVEINVENOBJ)) RemoveInvenObjHook_Script(source, item, count, rmType); +} +*/ + +static long rmObjType = -1; + +void __stdcall SetRemoveObjectType(long rmType) { + rmObjType = rmType; +} + static void __declspec(naked) RemoveObjHook() { static const DWORD RemoveObjHookRet = 0x477497; __asm { - mov ecx, [esp + 8]; // call addr + mov ecx, [esp + 8]; // call addr + cmp rmObjType, -1; + cmovne ecx, rmObjType; + mov rmObjType, -1; HookBegin; - mov args[0], eax; // source - mov args[4], edx; // item - mov args[8], ebx; // count - mov args[12], ecx; // called func - xor esi, esi; - xor ecx, 0x47761D; // from item_move_func_ - cmovz esi, ebp; // target + mov args[0], eax; // source + mov args[4], edx; // item + mov args[8], ebx; // count + mov args[12], ecx; // RMOBJ_* (called func) + xor esi, esi; + xor ecx, 0x47761D; // from item_move_func_ + cmovz esi, ebp; // target mov args[16], esi; push edi; push ebp; @@ -1893,9 +1922,9 @@ void HookScripts_Init() { HookCalls(UseObjOnHook, useObjOnHkAddr); // the following hooks allows to catch drug use of AI and from action cursor const DWORD drugUseObjOnHkAddr[] = { - 0x4285DF, // ai_check_drugs - 0x4286F8, // ai_check_drugs - 0x4287F8, // ai_check_drugs + //0x4285DF, // ai_check_drugs + //0x4286F8, // ai_check_drugs + //0x4287F8, // ai_check_drugs 0x473573 // inven_action_cursor }; HookCalls(Drug_UseObjOnHook, drugUseObjOnHkAddr); diff --git a/sfall/HookScripts.h b/sfall/HookScripts.h index f3655744..46de8f38 100644 --- a/sfall/HookScripts.h +++ b/sfall/HookScripts.h @@ -81,6 +81,8 @@ int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &roun void InvenUnwield_HookDrop(); void InvenUnwield_HookMove(); +void __stdcall SetRemoveObjectType(long rmType); + void __stdcall AdjustFidHook(DWORD vanillaFid); long __stdcall CalcApCostHook_Invoke(TGameObj* source, long hitMode, long isCalled, long cost, TGameObj* weapon); diff --git a/sfall/Inventory.cpp b/sfall/Inventory.cpp index 508e2eb9..47f5bfc7 100644 --- a/sfall/Inventory.cpp +++ b/sfall/Inventory.cpp @@ -304,8 +304,10 @@ static void __declspec(naked) gdControlUpdateInfo_hack() { //////////////////////////////////////////////////////////////////////////////// static char superStimMsg[128]; +static const long SUPER_STIMPAK = 1; + static int __fastcall SuperStimFix(TGameObj* item, TGameObj* target) { - if (item->protoId != PID_SUPER_STIMPAK || !target || target->IsNotCritter()) { + if (item->protoId != sfgame_GetHealingPID(SUPER_STIMPAK) || !target || target->IsNotCritter()) { return 0; } @@ -671,7 +673,7 @@ void Inventory_Init() { SafeWrite32(0x472632, widthWeight); if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) { - Translate_Get("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!", superStimMsg); + Translate_Get("sfall", "SuperStimExploitMsg", "You cannot use this item on someone who is not injured!", superStimMsg); MakeCall(0x49C3D9, protinst_use_item_on_hack); } diff --git a/sfall/ReplacementFuncs.cpp b/sfall/ReplacementFuncs.cpp index cb3e26e0..4cbf4cf2 100644 --- a/sfall/ReplacementFuncs.cpp +++ b/sfall/ReplacementFuncs.cpp @@ -84,8 +84,8 @@ void __stdcall sfgame_ai_check_drugs(TGameObj* source) { break; } - if (sfgame_IsHealingItem(itemFind) && !fo_item_remove_mult(source, itemFind, 1)) { // HOOK_REMOVEINVENOBJ - if (!sfgame_UseDrugItemFunc(source, itemFind)) { // HOOK_USEOBJON + if (sfgame_IsHealingItem(itemFind) && !sfgame_item_remove_mult(source, itemFind, 1, 0x4286F0)) { // HOOK_REMOVEINVENOBJ + if (!sfgame_UseDrugItemFunc(source, itemFind)) { // HOOK_USEOBJON drugWasUsed = true; } @@ -134,8 +134,8 @@ void __stdcall sfgame_ai_check_drugs(TGameObj* source) { // if the preference counter is less than 3, then AI can use the drug if (counter < 3) { // if the item is NOT a healing drug - if (!sfgame_IsHealingItem(item) && !fo_item_remove_mult(source, item, 1)) { // HOOK_REMOVEINVENOBJ - if (!sfgame_UseDrugItemFunc(source, item)) { // HOOK_USEOBJON + if (!sfgame_IsHealingItem(item) && !sfgame_item_remove_mult(source, item, 1, 0x4286F0)) { // HOOK_REMOVEINVENOBJ + if (!sfgame_UseDrugItemFunc(source, item)) { // HOOK_USEOBJON drugWasUsed = true; usedCount++; } @@ -170,8 +170,8 @@ void __stdcall sfgame_ai_check_drugs(TGameObj* source) { } } - if (lastItem && !fo_item_remove_mult(source, lastItem, 1)) { // HOOK_REMOVEINVENOBJ - if (!sfgame_UseDrugItemFunc(source, lastItem)) lastItem = nullptr; // HOOK_USEOBJON + if (lastItem && !sfgame_item_remove_mult(source, lastItem, 1, 0x4286F0)) { // HOOK_REMOVEINVENOBJ + if (!sfgame_UseDrugItemFunc(source, lastItem)) lastItem = nullptr; // HOOK_USEOBJON source->critter.decreaseAP(aiUseItemAPCost); } @@ -340,6 +340,11 @@ long __stdcall sfgame_item_d_take_drug(TGameObj* source, TGameObj* item) { return -1; // cancel the drug use } +long __stdcall sfgame_item_remove_mult(TGameObj* source, TGameObj* item, long count, long rmType) { + SetRemoveObjectType(rmType); + return fo_item_remove_mult(source, item, count); +} + long __stdcall sfgame_item_count(TGameObj* who, TGameObj* item) { for (int i = 0; i < who->invenSize; i++) { TGameObj::InvenItem* tableItem = &who->invenTable[i]; @@ -887,15 +892,15 @@ static void __declspec(naked) tile_num_beyond_hack() { void InitReplacementHacks() { // Replace ai_check_drugs_ function for code fixes and checking healing items - //MakeJump(ai_check_drugs_, ai_check_drugs_hack); // 0x428480 + MakeJump(ai_check_drugs_, ai_check_drugs_hack); // 0x428480 // Change ai_can_use_drug_ function code to check healing items - //MakeCall(0x429BDE, ai_can_use_drug_hack, 6); - //SafeWrite8(0x429BE9, CODETYPE_JumpNZ); // jz > jnz - //SafeWrite8(0x429BF1, CODETYPE_JumpShort); // jnz > jmp + MakeCall(0x429BDE, ai_can_use_drug_hack, 6); + SafeWrite8(0x429BE9, CODETYPE_JumpNZ); // jz > jnz + SafeWrite8(0x429BF1, CODETYPE_JumpShort); // jnz > jmp - //drugUsePerfFixMode = GetConfigInt("Misc", "AIDrugUsePerfFix", 0); - //if (drugUsePerfFixMode > 0) dlogr("Applying AI drug use preference fix.", DL_FIX); + drugUsePerfFixMode = GetConfigInt("Misc", "AIDrugUsePerfFix", 0); + if (drugUsePerfFixMode > 0) dlogr("Applying AI drug use preference fix.", DL_FIX); // Replace adjust_fid_ function MakeJump(adjust_fid_, adjust_fid_hack); // 0x4716E8 diff --git a/sfall/ReplacementFuncs.h b/sfall/ReplacementFuncs.h index 2866e0b4..e3e2f599 100644 --- a/sfall/ReplacementFuncs.h +++ b/sfall/ReplacementFuncs.h @@ -47,6 +47,8 @@ bool __stdcall sfgame_UseDrugItemFunc(TGameObj* source, TGameObj* item); // Implementation of item_d_take_ engine function with the HOOK_USEOBJON hook long __stdcall sfgame_item_d_take_drug(TGameObj* source, TGameObj* item); +long __stdcall sfgame_item_remove_mult(TGameObj* source, TGameObj* item, long count, long rmType); + long __stdcall sfgame_item_count(TGameObj* who, TGameObj* item); long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long hitMode); diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index 2e153eec..4321e628 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -220,6 +220,7 @@ + @@ -304,6 +305,7 @@ + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index 6a5397ca..2abb8fdf 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -262,6 +262,9 @@ Headers + + Headers + @@ -439,6 +442,9 @@ Source + + Source + diff --git a/sfall/main.cpp b/sfall/main.cpp index cb6f208a..4ca8fc81 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -36,6 +36,7 @@ #include "DamageMod.h" #include "DebugEditor.h" #include "Elevators.h" +#include "EngineTweaks.h" #include "Explosions.h" #include "ExtraSaveSlots.h" #include "FileSystem.h" @@ -111,6 +112,9 @@ static void InitModules() { dlogr("Running LoadGameHook_Init().", DL_INIT); LoadGameHook_Init(); + dlogr("Running EngineTweaks_Init().", DL_INIT); + EngineTweaks_Init(); + dlogr("Running Books_Init().", DL_INIT); Books_Init(); @@ -237,6 +241,7 @@ static void InitModules() { static void __stdcall OnExit() { Graphics_Exit(); + EngineTweaks_Exit(); Books_Exit(); Movies_Exit(); Interface_Exit();