Added a new argument to HOOK_TOHIT (#339)

Added hook type 3 to HOOK_WITHINPERCEPTION (when AI selects a target)

Extended the upper limit of set_pickpocket_max and set_hit_chance_max
script functions to 999.

Added a fix for combat_is_shot_blocked_ not taking the flags of critters
in the line of fire into account.

Minor edits to ASM code of some hooks.
This commit is contained in:
NovaRain
2020-09-22 22:16:57 +08:00
parent 7414c8a361
commit 0a48ed3048
7 changed files with 82 additions and 61 deletions
+9 -5
View File
@@ -80,13 +80,14 @@ HOOK_TOHIT (hs_tohit.int)
Runs when Fallout is calculating the chances of an attack striking a target Runs when Fallout is calculating the chances of an attack striking a target
Runs after the hit chance is fully calculated normally, including applying the 95% cap Runs after the hit chance is fully calculated normally, including applying the 95% cap
int arg1 - The unmodified hit chance int arg1 - The hit chance (capped)
critter arg2 - The attacker critter arg2 - The attacker
critter arg3 - The target of the attack critter arg3 - The target of the attack
int arg4 - The targeted bodypart int arg4 - The targeted bodypart
int arg5 - Source tile (may differ from attacker's tile, when AI is considering potential fire position) int arg5 - Source tile (may differ from attacker's tile, when AI is considering potential fire position)
int arg6 - Attack Type (one of ATKTYPE_*) int arg6 - Attack Type (one of ATKTYPE_*)
int arg7 - Ranged flag (0 or 1, actually passed 1 even for unarmed attacks, may be a vanilla bug) int arg7 - Ranged flag. 1 means the hit chance is calculated by taking into account the bonuses/penalties of the distance to the target
int arg8 - The raw hit chance before applying the cap
int ret1 - the new hit chance int ret1 - the new hit chance
@@ -192,7 +193,7 @@ Critter arg1 - The critter that just died
HOOK_FINDTARGET (hs_findtarget.int) HOOK_FINDTARGET (hs_findtarget.int)
Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. Runs when the AI is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the AI caps of the attacker.
This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way.
The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries, but this is fixed in sfall 4.2.3/3.8.23. The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries, but this is fixed in sfall 4.2.3/3.8.23.
@@ -417,8 +418,11 @@ This is fired after the default calculation is made.
Critter arg1 - Watcher object Critter arg1 - Watcher object
Obj arg2 - Target object Obj arg2 - Target object
int arg3 - Result of vanilla function: 1 - within perception range, 0 - otherwise int arg3 - Result of vanilla function: 1 - within perception range, 0 - otherwise
int arg4 - Type of hook: 1 - when being called from obj_can_see_obj script function, 2 when being called from obj_can_hear_obj script function (need to set ObjCanHearObjFix=1 in ddraw.ini), 0 for all other cases int arg4 - Type of hook:
1 - when being called from obj_can_see_obj script function
2 - when being called from obj_can_hear_obj script function (need to set ObjCanHearObjFix=1 in ddraw.ini)
3 - when AI determines whether it sees a potential target when selecting attack targets
0 - all other cases
int ret1 - overrides the returned result of the function: 0 - not in range (can't see), 1 - in range (will see if not blocked), 2 - forced detection (will see regardless, only used in obj_can_see_obj scripting function which is called by every critter in the game) int ret1 - overrides the returned result of the function: 0 - not in range (can't see), 1 - in range (will see if not blocked), 2 - forced detection (will see regardless, only used in obj_can_see_obj scripting function which is called by every critter in the game)
------------------------------------------- -------------------------------------------
+4 -4
View File
@@ -19,7 +19,7 @@ The read_xxx functions take a memory address as the parameter and can read arbit
The get/set_pc_base/extra_stat functions are equivalent to calling get/set_critter_base/extra_stat with dude_obj as the critter pointer. None of these stat functions take perks into account, and neither do they do range clamping to make sure the stats are valid. Use the normal get_critter_stat function to get a correctly perk adjusted and range clamped value for a stat. The get/set_pc_base/extra_stat functions are equivalent to calling get/set_critter_base/extra_stat with dude_obj as the critter pointer. None of these stat functions take perks into account, and neither do they do range clamping to make sure the stats are valid. Use the normal get_critter_stat function to get a correctly perk adjusted and range clamped value for a stat.
The set_stat_max/min functions can be used to set the valid ranges on stats. Values returned by get_current_stat will be clamped to this range. The set_pc_ function only effect the player, the set_npc_ functions only effect other critters, and the set_ functions effect both. The set_stat_max/min functions can be used to set the valid ranges on stats. Values returned by get_current_stat will be clamped to this range. The set_pc_ function only affects the player, the set_npc_ functions only affects other critters, and the set_ functions affects both.
The input functions are only available if the user has the input hook turned on in ddraw.ini. Use input_funcs_available to check. The input functions are only available if the user has the input hook turned on in ddraw.ini. Use input_funcs_available to check.
@@ -43,7 +43,7 @@ The 'type' value in the weapon knockback functions can be 0 or 1. If 0, the valu
The get/set_sfall_global functions require an 8 characters long case sensitive string for the variable name. The variables behave the same as normal Fallout globals, except that they don't have to be declared beforehand in vault13.gam. Trying to get a variable which hasn't been set will always return 0. These functions are intended for use when a patch to a mod requires the addition of a new global variable, a case which would otherwise require the player to start a new game. The get/set_sfall_global functions require an 8 characters long case sensitive string for the variable name. The variables behave the same as normal Fallout globals, except that they don't have to be declared beforehand in vault13.gam. Trying to get a variable which hasn't been set will always return 0. These functions are intended for use when a patch to a mod requires the addition of a new global variable, a case which would otherwise require the player to start a new game.
set_pickpocket_max and set_hit_chance_max effect all critters rather than just the player. set_skill_max can't be used to increase the skill cap above 300. set_perk_level_mod sets a modifier between +25 and -25 that is added/subtracted from the player's level for the purposes of deciding which perks can be chosen. set_pickpocket_max and set_hit_chance_max affect all critters rather than just the player and can set the maximum in range from 0 to 999. set_skill_max can't be used to increase the skill cap above 300. set_perk_level_mod sets a modifier between +25 and -25 that is added/subtracted from the player's level for the purposes of deciding which perks can be chosen.
set_fake_trait and set_fake_perk can be used to add additional traits and perks to the character screen. They will be saved correctly when the player saves and reloads games, but by themselves they will have no further effect on the character. For perks, the allowed range for levels is between 0 and 100; setting the level to 0 removes that perk. For traits, the level must be 0 or 1. The image is a numeric id that corresponds to an entry in skilldex.lst. The name is limited to 63 characters and the description to 255 characters by sfall, but internal Fallout limits may be lower. set_fake_trait and set_fake_perk can be used to add additional traits and perks to the character screen. They will be saved correctly when the player saves and reloads games, but by themselves they will have no further effect on the character. For perks, the allowed range for levels is between 0 and 100; setting the level to 0 removes that perk. For traits, the level must be 0 or 1. The image is a numeric id that corresponds to an entry in skilldex.lst. The name is limited to 63 characters and the description to 255 characters by sfall, but internal Fallout limits may be lower.
@@ -57,7 +57,7 @@ get/set_bodypart_hit_modifier alter the hit percentage modifiers for aiming at s
(re)set/get_critical_table are used for modifying the critical table. For details see 'http://falloutmods.wikia.com/wiki/Critical_hit_tables'. Changes are not saved, and will reset to the defaults, (or to the contents of CriticalOverrides.ini, if it exists,) at each game reload. These function also require OverrideCriticalTable to be set to 1 in ddraw.ini. (Disabled by default, because it noticably increases loading times.) (re)set/get_critical_table are used for modifying the critical table. For details see 'http://falloutmods.wikia.com/wiki/Critical_hit_tables'. Changes are not saved, and will reset to the defaults, (or to the contents of CriticalOverrides.ini, if it exists,) at each game reload. These function also require OverrideCriticalTable to be set to 1 in ddraw.ini. (Disabled by default, because it noticably increases loading times.)
get/set_unspent_ap_bonus alter the AC bonus you receive per unused action point at the end of your turn in combat. To allow for fractional values, the value given if divided by 4. (Hence the default value is 4 and not 1.) get/set_unspent_ap_perk_bonus are similar, but effect the extra AC granted by the h2h evade perk. (The default value of this is also 4, equivalent to doubling the original bonus.) get/set_unspent_ap_bonus alter the AC bonus you receive per unused action point at the end of your turn in combat. To allow for fractional values, the value given if divided by 4. (Hence the default value is 4 and not 1.) get/set_unspent_ap_perk_bonus are similar, but affect the extra AC granted by the HtH Evade perk. (The default value of this is also 4, equivalent to doubling the original bonus.)
nb_* functions are reserved for the brotherhood tactical training mod, and should be avoided. nb_* functions are reserved for the brotherhood tactical training mod, and should be avoided.
@@ -537,7 +537,7 @@ Some utility/math functions are available:
> void sfall_func5("draw_image", string/int pathFile/artId, int frame, int x, int y, bool noTransparent) > void sfall_func5("draw_image", string/int pathFile/artId, int frame, int x, int y, bool noTransparent)
> void sfall_func6("draw_image_scaled", string/int pathFile/artId, int frame, int x, int y, int width, int height) > void sfall_func6("draw_image_scaled", string/int pathFile/artId, int frame, int x, int y, int width, int height)
- displays the specified PCX or FRM image in the active window created by vanilla CreateWin or sfall's create_win script function - displays the specified PCX or FRM image in the active window created by vanilla CreateWin or sfall's create_win script function
- pathFile/artId: path to the PCX/FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format) - pathFile/artId: path to the PCX/FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 0x7000026, see specification of the FID format)
optional arguments: optional arguments:
- frame: frame number, the first frame starts from zero - frame: frame number, the first frame starts from zero
- x/y: offset relative to the top-left corner of the window - x/y: offset relative to the top-left corner of the window
+7 -1
View File
@@ -3525,7 +3525,7 @@ void BugFixesInit()
// Fix the code in combat_is_shot_blocked_ to correctly get the next tile from a multihex object instead of the previous // Fix the code in combat_is_shot_blocked_ to correctly get the next tile from a multihex object instead of the previous
// object or source tile // object or source tile
// Note: this bug does not cause an error in the function work // Note: this bug does not cause any noticeable error in the function
BYTE codeData[] = { BYTE codeData[] = {
0x8B, 0x70, 0x04, // mov esi, [eax + 4] 0x8B, 0x70, 0x04, // mov esi, [eax + 4]
0xF6, 0x40, 0x25, 0x08, // test [eax + flags2], MultiHex_ 0xF6, 0x40, 0x25, 0x08, // test [eax + flags2], MultiHex_
@@ -3558,4 +3558,10 @@ void BugFixesInit()
// Fix to limit the maximum distance for the knockback animation // Fix to limit the maximum distance for the knockback animation
MakeCall(0x4104D5, action_knockback_hack); MakeCall(0x4104D5, action_knockback_hack);
// Fix for combat_is_shot_blocked_ engine function not checking critters and their flags correctly
// when calculating the hit chance penalty based on the number of critters in the line of fire
const DWORD isShotBlockedAddr[] = {0x426D46, 0x426D4E};
SafeWriteBatch<BYTE>(0x41, isShotBlockedAddr); // edi > ecx (replace target with object critter)
SafeWrite8(0x426D48, DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT);
} }
+3
View File
@@ -64,6 +64,8 @@ struct KnockbackModifier {
double value; double value;
}; };
long Combat_rawHitChance; // the value of hit chance w/o any cap
static std::vector<long> noBursts; // object id static std::vector<long> noBursts; // object id
static std::vector<KnockbackModifier> mTargets; static std::vector<KnockbackModifier> mTargets;
@@ -265,6 +267,7 @@ static void __declspec(naked) compute_dmg_damage_hack() {
} }
static int __fastcall HitChanceMod(int base, TGameObj* critter) { static int __fastcall HitChanceMod(int base, TGameObj* critter) {
Combat_rawHitChance = base;
for (size_t i = 0; i < hitChanceMods.size(); i++) { for (size_t i = 0; i < hitChanceMods.size(); i++) {
if (critter->id == hitChanceMods[i].id) { if (critter->id == hitChanceMods[i].id) {
return min(base + hitChanceMods[i].mod, hitChanceMods[i].maximum); return min(base + hitChanceMods[i].mod, hitChanceMods[i].maximum);
+2
View File
@@ -50,3 +50,5 @@ void __stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode);
void __stdcall SetNoBurstMode(TGameObj* critter, bool on); void __stdcall SetNoBurstMode(TGameObj* critter, bool on);
void __stdcall DisableAimedShots(DWORD pid); void __stdcall DisableAimedShots(DWORD pid);
void __stdcall ForceAimedShots(DWORD pid); void __stdcall ForceAimedShots(DWORD pid);
extern long Combat_rawHitChance;
+41 -41
View File
@@ -21,12 +21,14 @@
#include "main.h" #include "main.h"
#include "FalloutEngine.h" #include "FalloutEngine.h"
#include "HookScripts.h" #include "Combat.h"
#include "LoadGameHook.h" #include "LoadGameHook.h"
#include "Logging.h" #include "Logging.h"
#include "PartyControl.h" #include "PartyControl.h"
#include "ScriptExtender.h" #include "ScriptExtender.h"
#include "HookScripts.h"
// Number of types of hooks // Number of types of hooks
static const int numHooks = HOOK_COUNT; static const int numHooks = HOOK_COUNT;
@@ -180,13 +182,14 @@ static void __declspec(naked) ToHitHook() {
pushadc; pushadc;
} }
argCount = 7; argCount = 8;
args[7] = Combat_rawHitChance;
RunHookScript(HOOK_TOHIT); RunHookScript(HOOK_TOHIT);
__asm { __asm {
popadc; popadc;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn 8; retn 8;
} }
@@ -270,7 +273,7 @@ static void __declspec(naked) CalcApCostHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -294,7 +297,7 @@ static void __declspec(naked) CalcApCostHook2() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -375,7 +378,7 @@ static void __declspec(naked) CalcDeathAnim2Hook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -516,7 +519,7 @@ static void __declspec(naked) UseObjOnHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
jb defaultHandler; jl defaultHandler;
cmp rets[0], -1; cmp rets[0], -1;
je defaultHandler; je defaultHandler;
mov eax, rets[0]; mov eax, rets[0];
@@ -543,7 +546,7 @@ static void __declspec(naked) Drug_UseObjOnHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
jb defaultHandler; jl defaultHandler;
cmp rets[0], -1; cmp rets[0], -1;
je defaultHandler; je defaultHandler;
mov eax, rets[0]; mov eax, rets[0];
@@ -569,7 +572,7 @@ static void __declspec(naked) UseObjHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
jb defaultHandler; jl defaultHandler;
cmp rets[0], -1; cmp rets[0], -1;
je defaultHandler; je defaultHandler;
mov eax, rets[0]; mov eax, rets[0];
@@ -713,7 +716,7 @@ static void __declspec(naked) MoveCostHook() {
__asm { __asm {
popadc; popadc;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -746,7 +749,7 @@ return:
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -769,7 +772,7 @@ static void __declspec(naked) HexABlockingHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -792,7 +795,7 @@ static void __declspec(naked) HexShootBlockingHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -815,7 +818,7 @@ static void __declspec(naked) HexSightBlockingHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
cmovnb eax, rets[0]; cmovge eax, rets[0];
HookEnd; HookEnd;
retn; retn;
} }
@@ -940,7 +943,7 @@ static void __declspec(naked) UseSkillHook() {
__asm { __asm {
popad; popad;
cmp cRet, 1; cmp cRet, 1;
jb defaultHandler; jl defaultHandler;
cmp rets[0], -1; cmp rets[0], -1;
je defaultHandler; je defaultHandler;
mov eax, rets[0]; mov eax, rets[0];
@@ -968,7 +971,7 @@ static void __declspec(naked) StealCheckHook() {
__asm { __asm {
popadc; popadc;
cmp cRet, 1; cmp cRet, 1;
jb defaultHandler; jl defaultHandler;
cmp rets[0], -1; cmp rets[0], -1;
je defaultHandler; je defaultHandler;
mov eax, rets[0]; mov eax, rets[0];
@@ -980,8 +983,8 @@ defaultHandler:
} }
} }
// Implementation of is_within_perception_ engine function with the hook // 4.x backport
long __fastcall sf_is_within_perception(TGameObj* watcher, TGameObj* target) { static long __fastcall PerceptionRangeHook_Script(TGameObj* watcher, TGameObj* target, int type) {
long result = IsWithinPerception(watcher, target); long result = IsWithinPerception(watcher, target);
BeginHook(); BeginHook();
@@ -989,28 +992,6 @@ long __fastcall sf_is_within_perception(TGameObj* watcher, TGameObj* target) {
args[0] = (DWORD)watcher; args[0] = (DWORD)watcher;
args[1] = (DWORD)target; args[1] = (DWORD)target;
args[2] = result; args[2] = result;
args[3] = 0; // type
argCount = 4;
RunHookScript(HOOK_WITHINPERCEPTION);
if (cRet > 0) result = rets[0];
EndHook();
return result;
}
// 4.x backport
static long __stdcall PerceptionRangeHook_Script(int type) {
long result;
__asm {
HookBegin;
mov args[0], eax; // watcher
mov args[4], edx; // target
call is_within_perception_;
mov result, eax; // check result
}
args[2] = result;
args[3] = type; args[3] = type;
argCount = 4; argCount = 4;
@@ -1022,10 +1003,16 @@ static long __stdcall PerceptionRangeHook_Script(int type) {
return result; return result;
} }
// Implementation of is_within_perception_ engine function with the hook
long __fastcall sf_is_within_perception(TGameObj* watcher, TGameObj* target) { // TODO: add type arg
return PerceptionRangeHook_Script(watcher, target, 0);
}
static void __declspec(naked) PerceptionRangeHook() { static void __declspec(naked) PerceptionRangeHook() {
__asm { __asm {
push ecx; push ecx;
push 0; push 0;
mov ecx, eax;
call PerceptionRangeHook_Script; call PerceptionRangeHook_Script;
pop ecx; pop ecx;
retn; retn;
@@ -1036,6 +1023,7 @@ static void __declspec(naked) PerceptionRangeSeeHook() {
__asm { __asm {
push ecx; push ecx;
push 1; push 1;
mov ecx, eax;
call PerceptionRangeHook_Script; call PerceptionRangeHook_Script;
pop ecx; pop ecx;
cmp eax, 2; cmp eax, 2;
@@ -1052,6 +1040,18 @@ static void __declspec(naked) PerceptionRangeHearHook() {
__asm { __asm {
push ecx; push ecx;
push 2; push 2;
mov ecx, eax;
call PerceptionRangeHook_Script;
pop ecx;
retn;
}
}
static void __declspec(naked) PerceptionSearchTargetHook() {
__asm {
push ecx;
push 3;
mov ecx, eax;
call PerceptionRangeHook_Script; call PerceptionRangeHook_Script;
pop ecx; pop ecx;
retn; retn;
@@ -1782,13 +1782,13 @@ static void HookScriptInit() {
LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION); LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION);
const DWORD perceptionRngHkAddr[] = { const DWORD perceptionRngHkAddr[] = {
0x429157,
0x42B4ED, 0x42B4ED,
0x42BC87, 0x42BC87,
0x42BC9F, 0x42BC9F,
0x42BD04 0x42BD04
}; };
HookCalls(PerceptionRangeHook, perceptionRngHkAddr); HookCalls(PerceptionRangeHook, perceptionRngHkAddr);
HookCall(0x429157, PerceptionSearchTargetHook);
HookCall(0x456BA2, PerceptionRangeSeeHook); HookCall(0x456BA2, PerceptionRangeSeeHook);
HookCall(0x458403, PerceptionRangeHearHook); HookCall(0x458403, PerceptionRangeHearHook);
+16 -10
View File
@@ -405,12 +405,15 @@ static void __declspec(naked) op_set_pickpocket_max() {
__asm { __asm {
mov esi, ecx; mov esi, ecx;
_GET_ARG_INT(end); _GET_ARG_INT(end);
mov ecx, 100; xor edx, edx;
test eax, eax;
cmovs eax, edx; // max < 0
mov ecx, 999;
cmp eax, ecx; cmp eax, ecx;
cmova eax, ecx; // 0 - 100 cmova eax, ecx; // 0 - 999 (maximum)
push 0; push edx; // mod (0)
push eax; push eax; // maximum
push 0xFFFFFFFF; push 0xFFFFFFFF; // critter
call SetPickpocketMax; call SetPickpocketMax;
end: end:
mov ecx, esi; mov ecx, esi;
@@ -422,12 +425,15 @@ static void __declspec(naked) op_set_hit_chance_max() {
__asm { __asm {
mov esi, ecx; mov esi, ecx;
_GET_ARG_INT(end); _GET_ARG_INT(end);
mov ecx, 100; xor edx, edx;
test eax, eax;
cmovs eax, edx; // max < 0
mov ecx, 999;
cmp eax, ecx; cmp eax, ecx;
cmova eax, ecx; // 0 - 100 cmova eax, ecx; // 0 - 999 (maximum)
push 0; push edx; // mod (0)
push eax; push eax; // maximum
push 0xFFFFFFFF; push 0xFFFFFFFF; // critter
call SetHitChanceMax; call SetHitChanceMax;
end: end:
mov ecx, esi; mov ecx, esi;