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:07:34 +08:00
parent af90444f9b
commit f8447ba3b1
8 changed files with 92 additions and 20 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
+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, fo::GameObject*, critter, fo::GameObject*, item, fo::GameObject**, outAmmo)
WRAP_WATCOM_FFUNC3(long, ai_pick_hit_mode, fo::GameObject*, source, fo::GameObject*, item, fo::GameObject*, target)
WRAP_WATCOM_FFUNC3(fo::GameObject*, ai_search_inven_weap, fo::GameObject*, source, long, apCheck, fo::GameObject*, target)
WRAP_WATCOM_FFUNC3(void, check_for_death, fo::GameObject*, critter, long, amountDamage, long*, flags)
@@ -33,6 +34,7 @@ WRAP_WATCOM_FFUNC3(void, intface_update_items, long, animate, long, modeLeft, lo
WRAP_WATCOM_FFUNC3(fo::GameObject*, inven_find_type, fo::GameObject*, critter, long, itemType, DWORD*, slot)
WRAP_WATCOM_FFUNC3(long, inven_wield, fo::GameObject*, critter, fo::GameObject*, item, long, slot)
WRAP_WATCOM_FFUNC3(long, item_add_force, fo::GameObject*, critter, fo::GameObject*, item, long, count)
WRAP_WATCOM_FFUNC3(long, item_mp_cost, fo::GameObject*, source, long, hitMode, long, isCalled)
WRAP_WATCOM_FFUNC3(long, item_w_mp_cost, fo::GameObject*, source, long, hitMode, long, isCalled)
WRAP_WATCOM_FFUNC7(void, make_straight_path_func, fo::GameObject*, 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)
+22 -9
View File
@@ -4,7 +4,7 @@
*
*/
//#include "..\main.h"
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\HookScripts\CombatHs.h"
@@ -19,6 +19,8 @@ namespace game
namespace sf = sfall;
static constexpr int reloadCostAP = 2; // engine default reload AP cost
long Items::item_weapon_range(fo::GameObject* source, fo::GameObject* weapon, long hitMode) {
fo::Proto* wProto;
if (!GetProto(weapon->protoId, &wProto)) return 0;
@@ -43,10 +45,8 @@ long Items::item_weapon_range(fo::GameObject* source, fo::GameObject* weapon, lo
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 Items::item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon, long hitMode, long isCalled) {
long __fastcall Items::item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon, long hitMode, long isCalled) {
long cost = 0;
switch (hitMode) {
@@ -60,16 +60,16 @@ long Items::item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon,
break;
case fo::AttackType::ATKTYPE_LWEAPON_RELOAD:
case fo::AttackType::ATKTYPE_RWEAPON_RELOAD:
if (source->protoId != fo::ProtoID::PID_SOLAR_SCORCHER && weapon) {
cost = 2; // default reload AP cost
if (weapon && weapon->protoId != fo::ProtoID::PID_SOLAR_SCORCHER) { // Solar Scorcher has no reload AP cost
cost = reloadCostAP;
if (fo::GetProto(weapon->protoId)->item.weapon.perk == fo::Perk::PERK_weapon_fast_reload) {
cost--;
}
}
}
if (hitMode < fo::AttackType::ATKTYPE_LWEAPON_RELOAD) {
if (cost == -1) cost = 0;
if (isCalled) cost++;
if (cost < 0) cost = 0;
long type = fo::func::item_w_subtype(weapon, hitMode);
@@ -92,13 +92,26 @@ long Items::item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon,
}
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
long __fastcall Items::item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled) {
long Items::item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled) {
long cost = fo::func::item_w_mp_cost(source, hitMode, isCalled);
return sf::CalcApCostHook_Invoke(source, hitMode, isCalled, cost, nullptr);
}
static void __declspec(naked) ai_search_inven_weap_hook() {
using namespace fo;
__asm {
push 0; // no called
push ATKTYPE_RWEAPON_PRIMARY;
mov edx, esi; // found weapon
mov ecx, edi; // source
call Items::item_weapon_mp_cost;
retn;
}
}
void Items::init() {
// Replace the item_w_primary_mp_cost_ function with the sfall implementation
sf::HookCall(0x429A08, ai_search_inven_weap_hook);
}
}
+4 -2
View File
@@ -16,10 +16,12 @@ public:
static long item_weapon_range(fo::GameObject* source, fo::GameObject* 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
static long item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon, long hitMode, long isCalled);
// Note: Use only for weapons
static long __fastcall item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon, long hitMode, long isCalled);
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
static long __fastcall item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled);
// Note: Use the generic item_mp_cost function which has a hook call
static long item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled);
};
}
+60 -6
View File
@@ -22,7 +22,6 @@
#include "..\FalloutEngine\Fallout2.h"
#include "LoadGameHook.h"
//#include "HookScripts\CombatHS.h"
#include "..\Game\items.h"
#include "AI.h"
@@ -287,6 +286,8 @@ end:
}
}
////////////////////////////////////////////////////////////////////////////////
static long __fastcall ai_weapon_reload_fix(fo::GameObject* weapon, fo::GameObject* ammo, fo::GameObject* critter) {
fo::Proto* proto = nullptr;
long result = -1;
@@ -339,11 +340,59 @@ skip:
}
}
////////////////////////////////////////////////////////////////////////////////
static long tempReloadCost;
static long __fastcall item_weapon_reload_cost_fix(fo::GameObject* source, fo::GameObject* weapon, fo::GameObject** outAmmo) {
long reloadCost = game::Items::item_weapon_mp_cost(source, weapon, fo::AttackType::ATKTYPE_RWEAPON_RELOAD, 0);
if (reloadCost > source->critter.movePoints) return -1; // no action points
tempReloadCost = reloadCost;
return fo::func::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(fo::GameObject* source, fo::GameObject* target) {
long weaponRange = fo::func::item_w_range(source, fo::ATKTYPE_RWEAPON_SECONDARY);
long targetDist = fo::func::obj_dist(source, target);
if (targetDist > weaponRange) return 0; // don't use secondary mode
//return (source->critter.movePoints >= fo::func::item_mp_cost(source, fo::ATKTYPE_RWEAPON_SECONDARY, 0));
return (source->critter.movePoints >= game::Items::item_w_mp_cost(source, fo::ATKTYPE_RWEAPON_SECONDARY, 0)); // 1 - allow secondary mode
}
@@ -376,15 +425,15 @@ fix: // check result
static void __declspec(naked) cai_perform_distance_prefs_hack() {
using namespace fo;
__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 game::Items::item_w_mp_cost;
mov eax, esi;
call fo::funcoffs::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
@@ -504,6 +553,11 @@ void AI::init() {
0x42A970, 0x42AA56, // ai_try_attack_
});
// 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
@@ -158,7 +158,6 @@ static void CriticalTableOverride() {
if (mode == 2 || mode == 3) { // bug fixes
using namespace fo;
// Children
SetEntry(2, LegRight, 1, FlagsFail, 0);
SetEntry(2, LegRight, 1, Message, 5216);
+1
View File
@@ -7,6 +7,7 @@
#pragma once
#include "Game\inventory.h"
#include "Game\items.h"
#include "Game\render.h"
#include "Game\skills.h"
#include "Game\stats.h"
+1
View File
@@ -172,6 +172,7 @@ int SetConfigInt(const char* section, const char* setting, int value) {
void InitReplacementHacks() {
game::Inventory::init();
game::Items::init();
game::Render::init();
game::Skills::init();
game::Stats::init();