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
|
||||
|
||||
|
||||
+60
-5
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user