mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user