Fixed AI not checking min HP properly for using stims.

Edited the code of InventoryApCost in Inventory.cpp.
Minor edits to some other code.
This commit is contained in:
NovaRain
2019-05-28 13:51:40 +08:00
parent aa221adea3
commit aa5bd95dfe
8 changed files with 85 additions and 69 deletions
+1 -1
View File
@@ -604,7 +604,7 @@ optional arguments:
> void sfall_func5("set_selectable_perk_npc", object npc, string namePerk, int active, int image, string desc) > void sfall_func5("set_selectable_perk_npc", object npc, string namePerk, int active, int image, string desc)
> int sfall_func2("has_fake_perk_npc", object npc, string namePerk) > int sfall_func2("has_fake_perk_npc", object npc, string namePerk)
> int sfall_func2("has_fake_trait_npc", object npc, string nameTrait) > int sfall_func2("has_fake_trait_npc", object npc, string nameTrait)
- functions are similar to has_fake_*/set_fake_*/set_selectable_perk opcodes, but work on the specified party member NPC (including dude_obj) - these functions are similar to has_fake_*/set_fake_*/set_selectable_perk functions, but apply to the specified party member NPC (including dude_obj)
------------------------ ------------------------
------ MORE INFO ------- ------ MORE INFO -------
+1 -1
View File
@@ -150,7 +150,7 @@ long IsPartyMemberByPid(long pid) {
} }
bool IsPartyMember(fo::GameObject* critter) { bool IsPartyMember(fo::GameObject* critter) {
if (critter->id < 18000) return false; if (critter->id < PLAYER_ID) return false;
return (IsPartyMemberByPid(critter->protoId) > 0); return (IsPartyMemberByPid(critter->protoId) > 0);
} }
+14
View File
@@ -76,6 +76,17 @@ tryHeal:
} }
} }
static void __declspec(naked) ai_check_drugs_hook() {
__asm {
call fo::funcoffs::stat_level_; // current hp
mov edx, dword ptr [esp + 0x34 - 0x1C + 4]; // ai cap
mov edx, [edx + 0x10]; // min_hp
cmp eax, edx; // curr_hp < cap.min_hp
cmovl edi, edx;
retn;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static DWORD RetryCombatLastAP; static DWORD RetryCombatLastAP;
@@ -194,6 +205,9 @@ void AI::init() {
// Fix to allow fleeing NPC to use drugs // Fix to allow fleeing NPC to use drugs
MakeCall(0x42B1DC, combat_ai_hack); MakeCall(0x42B1DC, combat_ai_hack);
// Fix for AI not checking minimum hp properly for using stimpaks (prevents premature fleeing)
HookCall(0x428579, ai_check_drugs_hook);
// Fix for NPC stuck in fleeing mode when the hit chance of a target was too low // Fix for NPC stuck in fleeing mode when the hit chance of a target was too low
HookCall(0x42B1E3, combat_ai_hook_FleeFix); HookCall(0x42B1E3, combat_ai_hook_FleeFix);
HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5}); HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5});
+7 -7
View File
@@ -801,19 +801,19 @@ static void __declspec(naked) PipStatus_AddHotLines_hook() {
static void __declspec(naked) perform_withdrawal_start_display_print_hook() { static void __declspec(naked) perform_withdrawal_start_display_print_hook() {
__asm { __asm {
test eax, eax test eax, eax;
jz end jz end;
jmp fo::funcoffs::display_print_ jmp fo::funcoffs::display_print_;
end: end:
retn retn;
} }
} }
static void __declspec(naked) op_wield_obj_critter_adjust_ac_hook() { static void __declspec(naked) op_wield_obj_critter_adjust_ac_hook() {
__asm { __asm {
call fo::funcoffs::adjust_ac_ call fo::funcoffs::adjust_ac_;
xor eax, eax // not animated xor eax, eax; // not animated
jmp fo::funcoffs::intface_update_ac_ jmp fo::funcoffs::intface_update_ac_;
} }
} }
+2 -2
View File
@@ -36,10 +36,10 @@ static void _stdcall ConsoleFilePrint(const char* msg) {
static const DWORD ConsoleHookRet = 0x431871; static const DWORD ConsoleHookRet = 0x431871;
static void __declspec(naked) ConsoleHook() { static void __declspec(naked) ConsoleHook() {
__asm { __asm {
pushad; pushadc;
push eax; push eax;
call ConsoleFilePrint; call ConsoleFilePrint;
popad; popadc;
push ebx; push ebx;
push ecx; push ecx;
push edx; push edx;
+54 -55
View File
@@ -63,6 +63,7 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed) {
} }
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
DWORD __stdcall sf_item_total_size(fo::GameObject* critter) { DWORD __stdcall sf_item_total_size(fo::GameObject* critter) {
int totalSize = fo::func::item_c_curr_size(critter); int totalSize = fo::func::item_c_curr_size(critter);
@@ -85,19 +86,6 @@ DWORD __stdcall sf_item_total_size(fo::GameObject* critter) {
return totalSize; return totalSize;
} }
/*static const DWORD ObjPickupFail=0x49B70D;
static const DWORD ObjPickupEnd=0x49B6F8;
static __declspec(naked) void ObjPickupHook() {
__asm {
cmp edi, ds:[FO_VAR_obj_dude];
jnz end;
end:
lea edx, [esp+0x10];
mov eax, ecx;
jmp ObjPickupEnd;
}
}*/
static int __stdcall CritterGetMaxSize(fo::GameObject* critter) { static int __stdcall CritterGetMaxSize(fo::GameObject* critter) {
if (critter == fo::var::obj_dude) return invSizeMaxLimit; if (critter == fo::var::obj_dude) return invSizeMaxLimit;
@@ -198,8 +186,8 @@ static __declspec(naked) void barter_attempt_transaction_hack_pc() {
/* cmp eax, edx */ /* cmp eax, edx */
jg fail; // if there's no available weight jg fail; // if there's no available weight
//------ //------
mov ecx, edi; // source (pc) mov ecx, edi; // source (pc)
mov edx, ebp; // npc table mov edx, ebp; // npc table
call BarterAttemptTransaction; call BarterAttemptTransaction;
test eax, eax; test eax, eax;
jz fail; jz fail;
@@ -232,19 +220,19 @@ fail:
static __declspec(naked) void loot_container_hook_btn() { static __declspec(naked) void loot_container_hook_btn() {
__asm { __asm {
push ecx; push ecx;
push edx; // source current weight push edx; // source current weight
mov edx, eax; // target mov edx, eax; // target
mov ecx, [esp + 0x150 - 0x1C + 12]; // source mov ecx, [esp + 0x150 - 0x1C + 12]; // source
call BarterAttemptTransaction; call BarterAttemptTransaction;
pop edx; pop edx;
pop ecx; pop ecx;
test eax, eax; test eax, eax;
jz fail; jz fail;
mov eax, ebp; // target mov eax, ebp; // target
jmp fo::funcoffs::item_total_weight_; jmp fo::funcoffs::item_total_weight_;
fail: fail:
mov eax, edx; mov eax, edx;
inc eax; // weight + 1 inc eax; // weight + 1
retn; retn;
} }
} }
@@ -340,51 +328,32 @@ static void __declspec(naked) gdControlUpdateInfo_hack() {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
static std::string superStimMsg; static std::string superStimMsg;
static int __fastcall SuperStimFix2(fo::GameObject* item, fo::GameObject* target) { static int __fastcall SuperStimFix(fo::GameObject* item, fo::GameObject* target) {
if (item->protoId != fo::PID_SUPER_STIMPAK || !target || target->Type() != fo::OBJ_TYPE_CRITTER) { if (item->protoId != fo::PID_SUPER_STIMPAK || !target || target->Type() != fo::OBJ_TYPE_CRITTER) {
return 0; return 0;
} }
long curr_hp, max_hp; long max_hp = fo::func::stat_level(target, fo::STAT_max_hit_points);
curr_hp = fo::func::stat_level(target, fo::STAT_current_hp); if (target->critter.health < max_hp) return 0;
max_hp = fo::func::stat_level(target, fo::STAT_max_hit_points);
if (curr_hp < max_hp) return 0;
fo::func::display_print(superStimMsg.c_str()); fo::func::display_print(superStimMsg.c_str());
return -1; return -1;
} }
static const DWORD UseItemHookRet = 0x49C5F4; static const DWORD protinst_use_item_on_Ret = 0x49C5F4;
static void __declspec(naked) SuperStimFix() { static void __declspec(naked) protinst_use_item_on_hack() {
__asm { __asm {
push ecx; push ecx;
mov ecx, ebx; // ecx - item mov ecx, ebx; // ecx - item
call SuperStimFix2; // edx - target call SuperStimFix; // edx - target
pop ecx; pop ecx;
test eax, eax; test eax, eax;
jnz end; jnz end;
mov ebp, -1; // overwritten engine code mov ebp, -1; // overwritten engine code
retn; retn;
end: end:
add esp, 4; // destroy ret add esp, 4; // destroy ret
jmp UseItemHookRet; // exit jmp protinst_use_item_on_Ret; // exit
}
}
static int invenApCost, invenApCostDef;
static char invenApQPReduction;
void _stdcall SetInvenApCost(int cost) {
invenApCost = cost;
}
static const DWORD inven_ap_cost_hack_ret = 0x46E816;
static void __declspec(naked) inven_ap_cost_hack() {
_asm {
movzx ebx, byte ptr invenApQPReduction;
mul bl;
mov edx, invenApCost;
sub edx, eax;
mov eax, edx;
jmp inven_ap_cost_hack_ret;
} }
} }
@@ -570,7 +539,7 @@ DWORD __stdcall Inventory::adjust_fid_replacement() {
using namespace fo; using namespace fo;
DWORD fid; DWORD fid;
if (var::inven_dude->TypeFid() == fo::OBJ_TYPE_CRITTER) { if (var::inven_dude->TypeFid() == ObjType::OBJ_TYPE_CRITTER) {
DWORD frameNum; DWORD frameNum;
DWORD weaponAnimCode = 0; DWORD weaponAnimCode = 0;
if (PartyControl::IsNpcControlled()) { if (PartyControl::IsNpcControlled()) {
@@ -652,6 +621,34 @@ end:
} }
} }
static int invenApCost, invenApCostDef;
static char invenApQPReduction;
static const DWORD inven_ap_cost_Ret = 0x46E812;
static void __declspec(naked) inven_ap_cost_hack() {
_asm {
mul byte ptr invenApQPReduction;
mov edx, invenApCost;
jmp inven_ap_cost_Ret;
}
}
static bool onlyOnceAP = false;
inline static void ApplyInvenApCostPatch() {
MakeJump(0x46E80B, inven_ap_cost_hack);
onlyOnceAP = true;
}
void _stdcall SetInvenApCost(int cost) {
invenApCost = cost;
if (!onlyOnceAP) ApplyInvenApCostPatch();
}
// TODO: Make GetInvenApCost() function
/*long GetInvenApCost() {
long plevel = fo::func::perk_level(fo::var::obj_dude, fo::PERK_quick_pockets);
return invenApCost - (invenApQPReduction * plevel);
}*/
void InventoryReset() { void InventoryReset() {
invenApCost = invenApCostDef; invenApCost = invenApCostDef;
} }
@@ -712,17 +709,19 @@ void Inventory::init() {
} }
} }
invenApCost = invenApCostDef = GetConfigInt("Misc", "InventoryApCost", 4);
invenApQPReduction = GetConfigInt("Misc", "QuickPocketsApCostReduction", 2);
MakeJump(0x46E80B, inven_ap_cost_hack);
if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) { if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) {
superStimMsg = Translate("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!"); superStimMsg = Translate("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!");
MakeCall(0x49C3D9, SuperStimFix); MakeCall(0x49C3D9, protinst_use_item_on_hack);
} }
reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0); reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0);
invenApCost = invenApCostDef = GetConfigInt("Misc", "InventoryApCost", 4);
invenApQPReduction = GetConfigInt("Misc", "QuickPocketsApCostReduction", 2);
if (invenApCostDef != 4 || invenApQPReduction != 2) {
ApplyInvenApCostPatch();
}
if (GetConfigInt("Misc", "StackEmptyWeapons", 0)) { if (GetConfigInt("Misc", "StackEmptyWeapons", 0)) {
MakeCall(0x4736C6, inven_action_cursor_hack); MakeCall(0x4736C6, inven_action_cursor_hack);
HookCall(0x4772AA, item_add_mult_hook); HookCall(0x4772AA, item_add_mult_hook);
+1 -1
View File
@@ -35,7 +35,7 @@ long Objects::uniqueID = UniqueID::Start; // current counter id, saving to sfall
// player ID = 18000, all party members have ID = 18000 + its pid (file number of prototype) // player ID = 18000, all party members have ID = 18000 + its pid (file number of prototype)
long Objects::SetObjectUniqueID(fo::GameObject* obj) { long Objects::SetObjectUniqueID(fo::GameObject* obj) {
long id = obj->id; long id = obj->id;
if (id > UniqueID::Start || obj == fo::var::obj_dude || (id >= 18000 && id < 83536)) return id; // 65535 maximum possible number of prototypes if (id > UniqueID::Start || obj == fo::var::obj_dude || (id >= PLAYER_ID && id < 83536)) return id; // 65535 maximum possible number of prototypes
if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start; if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start;
obj->id = ++uniqueID; obj->id = ++uniqueID;
+5 -2
View File
@@ -365,10 +365,13 @@ void sf_item_weight(OpcodeContext& ctx) {
void sf_set_dude_obj(OpcodeContext& ctx) { void sf_set_dude_obj(OpcodeContext& ctx) {
auto obj = ctx.arg(0).asObject(); auto obj = ctx.arg(0).asObject();
if (obj->Type() == fo::OBJ_TYPE_CRITTER) { if (obj->Type() == fo::OBJ_TYPE_CRITTER) {
PartyControl::SwitchToCritter(obj); //if (!InCombat && obj != PartyControl::RealDudeObject()) {
// ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName());
//} else {
PartyControl::SwitchToCritter(obj);
//}
} else { } else {
ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName()); ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName());
ctx.setReturn(-1);
} }
} }