Added new HOOK_USESKILLON and PartyMemberSkillFix option (#191)

This commit is contained in:
NovaRain
2018-10-24 12:19:30 +08:00
parent 3203152ca4
commit 5ab389a51a
9 changed files with 156 additions and 2 deletions
+9 -1
View File
@@ -75,7 +75,8 @@ Use32BitHeadGraphics=0
;Requires graphics mode 4 or 5
AllowDShowMovies=0
;Fade effect time length percentage modifier
;Fade effect time percentage modifier
;Default is 100. Decrease/increase this value to speed up/slow down fade effects
FadeMultiplier=100
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -631,6 +632,13 @@ DisplaySwiftLearnerExp=1
;Set to 1 to display party member's current level/AC/addict flag on the combat control panel
PartyMemberExtraInfo=0
;Set to 1 to fix the bug of using First Aid/Doctor skills when using them on the player
;This will cause the party member to apply his/her skills when you use First Aid/Doctor skills on the player, but only if
;the player is standing next to the party member
;Note that because the related engine function is not fully implemented, enabling this option without a global script
;that overrides First Aid/Doctor functions has very limited usefulness
PartyMemberSkillFix=0
;Set to 1 to skip loading game settings from a saved game, except for the game/combat difficulty settings
;Set to 2 to also skip loading the game/combat difficulty settings
SkipLoadingGameSettings=0
+2
View File
@@ -55,6 +55,8 @@
#define HOOK_GAMEMODECHANGE (31)
#define HOOK_USEANIMOBJ (32)
#define HOOK_EXPLOSIVETIMER (33)
#define HOOK_DESCRIPTIONOBJ (34)
#define HOOK_USESKILLON (35)
//Valid arguments to list_begin
#define LIST_CRITTERS (0)
+14
View File
@@ -544,3 +544,17 @@ An example usage would be to add an additional description to the item based on
Obj arg1 - the item
int ret1 - a pointer to the new text received by using "get_string_pointer" function
-------------------------------------------
HOOK_USESKILLON (hs_useskillon.int)
Runs before using any skill on any object. Lets you override the critter that uses the skill.
NOTE: the user critter can't be overridden when using Steal skill.
Critter arg1 - The user critter (usually dude_obj)
Obj arg2 - the target object/critter
int arg3 - skill being used
int ret1 - a new critter to override the user critter. Pass -1 to cancel the skill use, pass 0 to skip this return value
int ret2 - pass 1 to allow the skill being used in combat (only for dude_obj or critter being controlled by the player)
+2
View File
@@ -79,6 +79,7 @@ static HooksInjectInfo injectHooks[] = {
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, false},
{HOOK_USESKILLON, Inject_UseSkillOnHook, false},
};
bool HookScripts::injectAllHooks;
@@ -227,6 +228,7 @@ void HookScripts::init() {
OnKeyPressed() += KeyPressHook;
OnMouseClick() += MouseClickHook;
LoadGameHook::OnGameModeChange() += GameModeChangeHook;
LoadGameHook::OnAfterGameStarted() += SourceUseSkillOnInit;
HookScripts::injectAllHooks = (isDebug && (GetConfigInt("Debugging", "InjectAllGameHooks", 0) != 0));
}
+1
View File
@@ -61,6 +61,7 @@ enum HookType
HOOK_USEANIMOBJ = 32,
HOOK_EXPLOSIVETIMER = 33,
HOOK_DESCRIPTIONOBJ = 34,
HOOK_USESKILLON = 35,
HOOK_COUNT
};
+90
View File
@@ -392,6 +392,85 @@ end:
}
}
static fo::GameObject* sourceSkillOn = nullptr;
void SourceUseSkillOnInit() { sourceSkillOn = fo::var::obj_dude; }
static char resultSkillOn;
static long bakupCombatState;
static void __cdecl UseSkillOnHook_Script(DWORD source, DWORD target, register DWORD skillId) {
BeginHook();
argCount = 3;
args[0] = source; // user
args[1] = target; // target
args[2] = skillId; // skill id
sourceSkillOn = fo::var::obj_dude;
resultSkillOn = 0;
bakupCombatState = -1;
RunHookScript(HOOK_USESKILLON);
EndHook();
if (skillId != fo::Skill::SKILL_STEAL && cRet > 0) { // not work for steal skill
if (rets[0] != 0) {
resultSkillOn = (rets[0] == -1) ? -1 : 1;
if (resultSkillOn == 1) {
sourceSkillOn = (fo::GameObject*)rets[0];
}
}
if (resultSkillOn != -1 && cRet > 1 && rets[1] == 1) {
bakupCombatState = fo::var::combat_state;
fo::var::combat_state = 0;
}
}
}
static void __declspec(naked) UseSkillOnHook() {
__asm {
push ecx;
push ebx; // skill id
push edx; // target
push eax; // user
call UseSkillOnHook_Script;
pop eax;
pop edx;
add esp, 4; // ebx
pop ecx;
cmp resultSkillOn, -1; // skip handler
jnz handler;
retn;
handler:
jmp fo::funcoffs::action_use_skill_on_;
}
}
static void __declspec(naked) UseSkillOnHack() {
__asm {
cmp bakupCombatState, -1;
jz skip;
mov ebp, bakupCombatState;
mov dword ptr ds:[FO_VAR_combat_state], ebp;
skip:
cmp resultSkillOn, 0;
jz default;
mov edi, sourceSkillOn;
retn; // flag ZF = 0
default:
// engine code
cmp eax, dword ptr ds:[FO_VAR_obj_dude];
retn;
}
}
static void __declspec(naked) skill_use_hack() {
__asm {
cmp ebp, dword ptr ds:[FO_VAR_obj_dude];
setnz al;
retn;
}
}
void Inject_BarterPriceHook() {
HookCalls(BarterPriceHook, {
0x474D4C,
@@ -440,6 +519,16 @@ void Inject_ExplosiveTimerHook() {
HookCall(0x49BDC4, ExplosiveTimerHook);
}
void Inject_UseSkillOnHook() {
HookCalls(UseSkillOnHook, { 0x44C3CA, 0x44C81C });
MakeCall(0x4127BA, UseSkillOnHack);
SafeWrite8(0x4127BF, 0x90);
MakeCalls(skill_use_hack, {0x4AB05D, 0x4AB558, 0x4ABA60}); // fix checking obj_dude's target
// replace source skill user
SafeWriteBatch<DWORD>((DWORD)&sourceSkillOn, {0x4AAF47, 0x4AB051, 0x4AB3FB, 0x4AB550, 0x4AB8FA, 0x4ABA54});
SafeWriteBatch<DWORD>((DWORD)&sourceSkillOn, {0x4AB0EF, 0x4AB5C0, 0x4ABAF2}); // fix for time
}
void InitMiscHookScripts() {
LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
@@ -450,6 +539,7 @@ void InitMiscHookScripts() {
LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
LoadHookScript("hs_useskillon", HOOK_USESKILLON);
}
}
+2
View File
@@ -3,6 +3,7 @@
namespace sfall
{
void InitMiscHookScripts();
void SourceUseSkillOnInit();
void Inject_BarterPriceHook();
void Inject_UseSkillHook();
@@ -12,4 +13,5 @@ namespace sfall
void Inject_SetGlobalVarHook();
void Inject_RestTimerHook();
void Inject_ExplosiveTimerHook();
void Inject_UseSkillOnHook();
}
+35
View File
@@ -448,6 +448,24 @@ end:
}
}
static void __declspec(naked) action_use_skill_on_hook() {
__asm { // eax = dude_obj, edx = target, ebp = party_member
cmp eax, edx;
jnz end; // jump if target != dude_obj
mov edx, ebp;
call fo::funcoffs::obj_dist_; // check distance between dude_obj and party_member
cmp eax, 1; // if the distance is greater than 1, then reset the register
jg skip;
inc eax;
retn;
skip:
xor eax, eax;
retn;
end:
jmp fo::funcoffs::obj_dist_;
}
}
static const DWORD EncounterTableSize[] = {
0x4BD1A3, 0x4BD1D9, 0x4BD270, 0x4BD604, 0x4BDA14, 0x4BDA44, 0x4BE707,
0x4C0815, 0x4C0D4A, 0x4C0FD4,
@@ -855,6 +873,21 @@ void KeepWeaponSelectModePatch() {
}
}
void PartyMemberSkillPatch() {
// Fixed getting distance from source to target when using skills
// Note: this will cause the party member to apply his/her skill when you use First Aid/Doctor skill on the player, but only if
// the player is standing next to the party member. Because the related engine function is not fully implemented, enabling
// this option without a global script that overrides First Aid/Doctor functions has very limited usefulness
if (GetConfigInt("Misc", "PartyMemberSkillFix", 0) != 0) {
dlog("Applying party member using First Aid/Doctor skill patch.", DL_INIT);
HookCall(0x412836, action_use_skill_on_hook);
dlogr(" Done", DL_INIT);
}
// Small code patch for HOOK_USESKILLON (change obj_dude to source)
SafeWrite32(0x4128F3, 0x90909090);
SafeWrite16(0x4128F7, 0xFE39); // cmp esi, _obj_dude -> cmp esi, edi
}
void SkipLoadingGameSettingsPatch() {
int skipLoading = GetConfigInt("Misc", "SkipLoadingGameSettings", 0);
if (skipLoading) {
@@ -969,6 +1002,8 @@ void MiscPatches::init() {
DisplaySecondWeaponRangePatch();
KeepWeaponSelectModePatch();
PartyMemberSkillPatch();
SkipLoadingGameSettingsPatch();
InterfaceDontMoveOnTopPatch();
}
+1 -1
View File
@@ -279,7 +279,7 @@ static bool LoadOriginalDll(DWORD dwReason) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
char path[MAX_PATH];
CopyMemory(path + GetSystemDirectory(path , MAX_PATH - 10), "\\ddraw.dll", 11); // path to original dll
CopyMemory(path + GetSystemDirectoryA(path , MAX_PATH - 10), "\\ddraw.dll", 11); // path to original dll
ddraw.dll = LoadLibrary(path);
if (!ddraw.dll || ddraw.dll == INVALID_HANDLE_VALUE) {
MessageBox(0, "Cannot load the original ddraw.dll library.", "sfall", MB_ICONERROR);