Fully implemented the new ai_check_drugs function

Added an additional 'Healing Item' flag to ext flags for item protos.
Added new EngineTweaks files and TweaksFile option.
Internal fix for HOOK_REMOVEINVENOBJ.
This commit is contained in:
NovaRain
2021-08-23 10:15:51 +08:00
parent 42d79aec0b
commit c0737d9bea
14 changed files with 187 additions and 56 deletions
+13
View File
@@ -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
+7 -2
View File
@@ -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
+12 -10
View File
@@ -108,23 +108,25 @@ static void __declspec(naked) combat_ai_hack() {
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_;
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);
+16 -16
View File
@@ -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);
+36
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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() {
}
+22
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
void EngineTweaks_Init();
void EngineTweaks_Exit();
+33 -4
View File
@@ -620,15 +620,44 @@ 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
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
mov args[12], ecx; // RMOBJ_* (called func)
xor esi, esi;
xor ecx, 0x47761D; // from item_move_func_
cmovz esi, ebp; // target
@@ -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);
+2
View File
@@ -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);
+4 -2
View File
@@ -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);
}
+14 -9
View File
@@ -84,7 +84,7 @@ void __stdcall sfgame_ai_check_drugs(TGameObj* source) {
break;
}
if (sfgame_IsHealingItem(itemFind) && !fo_item_remove_mult(source, itemFind, 1)) { // HOOK_REMOVEINVENOBJ
if (sfgame_IsHealingItem(itemFind) && !sfgame_item_remove_mult(source, itemFind, 1, 0x4286F0)) { // HOOK_REMOVEINVENOBJ
if (!sfgame_UseDrugItemFunc(source, itemFind)) { // HOOK_USEOBJON
drugWasUsed = true;
}
@@ -134,7 +134,7 @@ 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_IsHealingItem(item) && !sfgame_item_remove_mult(source, item, 1, 0x4286F0)) { // HOOK_REMOVEINVENOBJ
if (!sfgame_UseDrugItemFunc(source, item)) { // HOOK_USEOBJON
drugWasUsed = true;
usedCount++;
@@ -170,7 +170,7 @@ void __stdcall sfgame_ai_check_drugs(TGameObj* source) {
}
}
if (lastItem && !fo_item_remove_mult(source, lastItem, 1)) { // HOOK_REMOVEINVENOBJ
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
+2
View File
@@ -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);
+2
View File
@@ -220,6 +220,7 @@
<ClInclude Include="DebugEditor.h" />
<ClInclude Include="Define.h" />
<ClInclude Include="Elevators.h" />
<ClInclude Include="EngineTweaks.h" />
<ClInclude Include="Explosions.h" />
<ClInclude Include="FalloutEngine.h" />
<ClInclude Include="FalloutFuncOffs_def.h" />
@@ -304,6 +305,7 @@
<ClCompile Include="Credits.cpp" />
<ClCompile Include="Criticals.cpp" />
<ClCompile Include="ddraw.cpp" />
<ClCompile Include="EngineTweaks.cpp" />
<ClCompile Include="Graphics.cpp" />
<ClCompile Include="DebugEditor.cpp" />
<ClCompile Include="IniReader.cpp" />
+6
View File
@@ -262,6 +262,9 @@
<ClInclude Include="stdafx.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="EngineTweaks.h">
<Filter>Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Console.cpp">
@@ -439,6 +442,9 @@
<ClCompile Include="stdafx.cpp">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="EngineTweaks.cpp">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="exports.def" />
+5
View File
@@ -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();