Added fixes for the AP cost for AI

* before the reload AP cost for AI was always 2, even if it's changed
by CALCAPCOST hook.
This commit is contained in:
NovaRain
2021-03-05 15:16:35 +08:00
parent b883bb30a3
commit c789dbffb4
6 changed files with 90 additions and 16 deletions
+2 -2
View File
@@ -118,8 +118,8 @@ You can get the weapon object by checking item slot based on attack type (ATKTYP
Critter arg0 - The critter performing the action
int arg1 - Attack Type (see ATKTYPE_* constants)
int arg2 - Is aimed attack (1 or 0)
int arg3 - The normal AP cost
Item arg4 - The weapon
int arg3 - The default AP cost
Item arg4 - The weapon for which the cost is calculated. If it is 0, the pointer to the weapon can still be obtained by the aforementioned method
int ret0 - The new AP cost
+60 -5
View File
@@ -283,6 +283,8 @@ end:
}
}
////////////////////////////////////////////////////////////////////////////////
static long __fastcall ai_weapon_reload_fix(TGameObj* weapon, TGameObj* ammo, TGameObj* critter) {
sProto* proto = nullptr;
long result = -1;
@@ -334,11 +336,59 @@ skip:
}
}
////////////////////////////////////////////////////////////////////////////////
static long tempReloadCost;
static long __fastcall item_weapon_reload_cost_fix(TGameObj* source, TGameObj* weapon, TGameObj** outAmmo) {
long reloadCost = sfgame_item_weapon_mp_cost(source, weapon, ATKTYPE_RWEAPON_RELOAD, 0);
if (reloadCost > source->critter.movePoints) return -1; // no action points
tempReloadCost = reloadCost;
return fo_ai_have_ammo(source, weapon, outAmmo); // 0 - no ammo
}
static void __declspec(naked) ai_try_attack_hook_cost_reload() {
static const DWORD ai_try_attack_hook_unwield_Ret = 0x42AACD;
__asm {
push ebx; // ammoObj ref
mov ecx, eax; // source
call item_weapon_reload_cost_fix; // edx - weapon
cmp eax, -1;
je noAPs;
retn;
noAPs:
add esp, 4; // destroy ret
jmp ai_try_attack_hook_unwield_Ret; // unwield weapon (default)
}
}
static void __declspec(naked) ai_try_attack_hook_cost1() {
__asm {
xor ebx, ebx;
sub edx, tempReloadCost; // curr.mp - reload cost
cmovg ebx, edx; // if curr.mp > 0
retn;
}
}
static void __declspec(naked) ai_try_attack_hook_cost2() {
__asm {
xor ecx, ecx;
sub ebx, tempReloadCost; // curr.mp - reload cost
cmovg ecx, ebx; // if curr.mp > 0
retn;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
static long __fastcall CheckWeaponRangeAndApCost(TGameObj* source, TGameObj* target) {
long weaponRange = fo_item_w_range(source, ATKTYPE_RWEAPON_SECONDARY);
long targetDist = fo_obj_dist(source, target);
if (targetDist > weaponRange) return 0; // don't use secondary mode
//return (source->critter.movePoints >= fo_item_mp_cost(source, fo::ATKTYPE_RWEAPON_SECONDARY, 0));
return (source->critter.movePoints >= sfgame_item_w_mp_cost(source, ATKTYPE_RWEAPON_SECONDARY, 0)); // 1 - allow secondary mode
}
@@ -370,15 +420,15 @@ fix: // check result
static void __declspec(naked) cai_perform_distance_prefs_hack() {
__asm {
mov ebx, eax; // current distance to target
mov ecx, esi;
push 0; // no called shot
mov ecx, eax; // current distance to target
xor ebx, ebx; // no called shot
mov edx, ATKTYPE_RWEAPON_PRIMARY;
call sfgame_item_w_mp_cost;
mov eax, esi;
call item_mp_cost_;
mov edx, [esi + movePoints];
sub edx, eax; // ap - cost = free AP's
jle moveAway; // <= 0
lea edx, [edx + ebx - 1];
lea edx, [edx + ecx - 1];
cmp edx, 5; // minimum threshold distance
jge skipMove; // distance >= 5?
// check combat rating
@@ -498,6 +548,11 @@ void AI_Init() {
};
HookCalls(item_w_reload_hook, itemWReloadAddr);
// Fix incorrect AP check and cost for AI when reloading a weapon
HookCall(0x42A955, ai_try_attack_hook_cost_reload);
MakeCall(0x42A9DE, ai_try_attack_hook_cost1);
MakeCall(0x42AABC, ai_try_attack_hook_cost2, 4);
// Adds a check for the weapon range and the AP cost when AI is choosing weapon attack modes
HookCall(0x429F6D, ai_pick_hit_mode_hook);
+1
View File
@@ -46,6 +46,7 @@ FUNC(adjust_fid_, 0x4716E8)
FUNC(ai_can_use_weapon_, 0x4298EC) // (TGameObj *aCritter<eax>, int aWeapon<edx>, int a2Or3<ebx>) returns 1 or 0
FUNC(ai_cap_, 0x4280B4)
FUNC(ai_check_drugs_, 0x428480)
FUNC(ai_have_ammo_, 0x4292D4)
FUNC(ai_pick_hit_mode_, 0x429DB4)
FUNC(ai_run_away_, 0x428868)
FUNC(ai_search_inven_armor_, 0x429A6C)
+2
View File
@@ -19,6 +19,7 @@
because the compiler builds the better/optimized code when calling the engine functions
*/
WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count)
WRAP_WATCOM_FFUNC3(long, ai_have_ammo, TGameObj*, critter, TGameObj*, item, TGameObj**, outAmmo)
WRAP_WATCOM_FFUNC3(long, ai_pick_hit_mode, TGameObj*, source, TGameObj*, item, TGameObj*, target)
WRAP_WATCOM_FFUNC3(TGameObj*, ai_search_inven_weap, TGameObj*, source, long, apCheck, TGameObj*, target)
WRAP_WATCOM_FFUNC3(void, check_for_death, TGameObj*, critter, long, amountDamage, long*, flags)
@@ -33,6 +34,7 @@ WRAP_WATCOM_FFUNC3(void, intface_update_items, long, animate, long, modeLeft, lo
WRAP_WATCOM_FFUNC3(TGameObj*, inven_find_type, TGameObj*, critter, long, itemType, DWORD*, slot)
WRAP_WATCOM_FFUNC3(long, inven_wield, TGameObj*, critter, TGameObj*, item, long, slot)
WRAP_WATCOM_FFUNC3(long, item_add_force, TGameObj*, critter, TGameObj*, item, long, count)
WRAP_WATCOM_FFUNC3(long, item_mp_cost, TGameObj*, source, long, hitMode, long, isCalled)
WRAP_WATCOM_FFUNC3(long, item_w_mp_cost, TGameObj*, source, long, hitMode, long, isCalled)
WRAP_WATCOM_FFUNC7(void, make_straight_path_func, TGameObj*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func)
WRAP_WATCOM_FFUNC3(long, message_find, DWORD*, msgFile, long, msgNumber, DWORD*, outBuf)
+21 -7
View File
@@ -123,6 +123,8 @@ static void __declspec(naked) adjust_fid_hack() {
//////////////////////////////////// ITEMS /////////////////////////////////////
static const int reloadCostAP = 2; // engine default reload AP cost
long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long hitMode) {
sProto* wProto;
if (!GetProto(weapon->protoId, &wProto)) return 0;
@@ -147,10 +149,8 @@ long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long
return range;
}
// TODO: replace all item_w_primary_mp_cost/item_w_secondary_mp_cost in engine with item_weapon_mp_cost function
// Implementation of item_w_primary_mp_cost_ and item_w_secondary_mp_cost_ engine functions in a single function with the HOOK_CALCAPCOST hook
long __stdcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled) {
long __fastcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled) {
long cost = 0;
switch (hitMode) {
@@ -164,16 +164,16 @@ long __stdcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, lo
break;
case ATKTYPE_LWEAPON_RELOAD:
case ATKTYPE_RWEAPON_RELOAD:
if (source->protoId != PID_SOLAR_SCORCHER && weapon) {
cost = 2; // default reload AP cost
if (weapon && weapon->protoId != PID_SOLAR_SCORCHER) { // Solar Scorcher has no reload AP cost
cost = reloadCostAP;
if (GetProto(weapon->protoId)->item.weapon.perk == PERK_weapon_fast_reload) {
cost--;
}
}
}
if (hitMode < ATKTYPE_LWEAPON_RELOAD) {
if (cost == -1) cost = 0;
if (isCalled) cost++;
if (cost < 0) cost = 0;
long type = fo_item_w_subtype(weapon, hitMode);
@@ -196,11 +196,22 @@ long __stdcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, lo
}
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
long __fastcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled) {
long __stdcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled) {
long cost = fo_item_w_mp_cost(source, hitMode, isCalled);
return CalcApCostHook_Invoke(source, hitMode, isCalled, cost, nullptr);
}
static void __declspec(naked) ai_search_inven_weap_hook() {
__asm {
push 0; // no called
push ATKTYPE_RWEAPON_PRIMARY;
mov edx, esi; // found weapon
mov ecx, edi; // source
call sfgame_item_weapon_mp_cost;
retn;
}
}
/////////////////////////////////// OBJECTS ////////////////////////////////////
// Implementation of is_within_perception_ engine function with the HOOK_WITHINPERCEPTION hook
@@ -507,6 +518,9 @@ void InitReplacementHacks() {
// Replace adjust_fid_ function
MakeJump(adjust_fid_, adjust_fid_hack); // 0x4716E8
// Replace the item_w_primary_mp_cost_ function with the sfall implementation
HookCall(0x429A08, ai_search_inven_weap_hook);
// Replace the srcCopy_ function with a pure MMX implementation
MakeJump(buf_to_buf_, fo_buf_to_buf); // 0x4D36D4
// Replace the transSrcCopy_ function
+4 -2
View File
@@ -37,10 +37,12 @@ DWORD __stdcall sfgame_adjust_fid();
long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long hitMode);
// Implementation of item_w_primary_mp_cost_ and item_w_secondary_mp_cost_ engine functions in a single function with the HOOK_CALCAPCOST hook
long __stdcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled);
// Note: Use only for weapons
long __fastcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled);
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
long __fastcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled);
// Note: Use the generic item_mp_cost function which has a hook call
long __stdcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled);
// Implementation of is_within_perception_ engine function with the HOOK_WITHINPERCEPTION hook
long __stdcall sfgame_is_within_perception(TGameObj* watcher, TGameObj* target, long hookType);