Rewrote the fix to allow fleeing NPC to use drugs.

Code edits to Perks.cpp.
This commit is contained in:
NovaRain
2019-05-24 10:35:50 +08:00
parent 04d7f4aefa
commit fca3307bd9
5 changed files with 82 additions and 66 deletions
+30 -15
View File
@@ -44,22 +44,9 @@ static void __declspec(naked) ai_try_attack_hook_FleeFix() {
static const DWORD combat_ai_hook_flee_Ret = 0x42B22F;
static void __declspec(naked) combat_ai_hook_FleeFix() {
__asm {
test byte ptr [ebp], 8; // 'ReTarget' flag
test byte ptr [ebp], 8; // 'ReTarget' flag (critter.combat_state)
jnz reTarget;
test byte ptr [ebp], 4; // flee flag? (critter.combat_state)
jz tryHeal;
flee:
jmp fo::funcoffs::critter_name_;
tryHeal:
call fo::funcoffs::ai_check_drugs_; // try to heal
mov eax, esi;
mov edx, STAT_current_hp;
call fo::funcoffs::stat_level_;
cmp eax, [ebx + 0x10]; // minimum hp, below which NPC will run away
mov eax, esi;
jl flee;
add esp, 4;
jmp combat_ai_hook_flee_Ret;
reTarget:
and byte ptr [ebp], ~(4 | 8); // unset Flee/ReTarget flags
xor edi, edi;
@@ -69,6 +56,31 @@ reTarget:
}
}
static const DWORD combat_ai_hack_Ret = 0x42B204;
static void __declspec(naked) combat_ai_hack() {
__asm {
mov edx, [ebx + 0x10]; // cap.min_hp
cmp eax, edx;
jl tryHeal; // curr_hp < min_hp
end:
add esp, 4;
jmp combat_ai_hack_Ret;
tryHeal:
mov eax, esi;
call fo::funcoffs::ai_check_drugs_;
push edx;
mov eax, esi;
mov edx, STAT_current_hp;
call fo::funcoffs::stat_level_;
pop edx;
cmp eax, edx; // edx - minimum hp, below which NPC will run away
jge end;
retn; // flee
}
}
////////////////////////////////////////////////////////////////////////////////
static DWORD RetryCombatLastAP;
static DWORD RetryCombatMinAP;
static void __declspec(naked) RetryCombatHook() {
@@ -96,6 +108,8 @@ end:
}
}
////////////////////////////////////////////////////////////////////////////////
static void __fastcall CombatAttackHook(DWORD source, DWORD target) {
sources[target] = source;
targets[source] = target;
@@ -182,8 +196,9 @@ void AI::init() {
/////////////////////// Combat AI behavior fixes ///////////////////////
// Fix to allow fleeing NPC to use drugs
HookCall(0x42B1E3, combat_ai_hook_FleeFix);
MakeCall(0x42B1DC, combat_ai_hack);
// Fix for NPC stuck in fleeing mode when the hit chance of a target was too low
HookCall(0x42B1E3, combat_ai_hook_FleeFix);
HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5});
// Disable fleeing when NPC cannot move closer to target
BlockCall(0x42ADF6); // ai_try_attack_
+3 -3
View File
@@ -26,7 +26,7 @@ namespace sfall
{
static int unjamTimeState;
static int maxCountProto = 512;
static int maxCountLoadProto = 512;
long Objects::uniqueID = UniqueID::Start; // current counter id, saving to sfallgv.sav
@@ -112,13 +112,13 @@ void RestoreObjUnjamAllLocks() {
static void __declspec(naked) proto_ptr_hack() {
__asm {
mov ecx, maxCountProto;
mov ecx, maxCountLoadProto;
cmp ecx, 4096;
jae skip;
cmp eax, ecx;
jb end;
add ecx, 256;
mov maxCountProto, ecx;
mov maxCountLoadProto, ecx;
skip:
cmp eax, ecx;
end:
+5 -5
View File
@@ -141,9 +141,6 @@ static void SetCurrentDude(fo::GameObject* npc) {
}
}
// change character name
fo::func::critter_pc_set_name(fo::func::critter_name(npc));
// change level
int level = (isPartyMember) // fo::func::isPartyMember(npc)
? fo::func::partyMemberGetCurLevel(npc)
@@ -152,6 +149,9 @@ static void SetCurrentDude(fo::GameObject* npc) {
fo::var::Level_ = level;
fo::var::last_level = level;
// change character name
fo::func::critter_pc_set_name(fo::func::critter_name(npc));
// reset other stats
fo::var::Experience_ = 0;
fo::var::free_perk = 0;
@@ -320,8 +320,8 @@ void PartyControl::SwitchToCritter(fo::GameObject* critter) {
if (!HookScripts::IsInjectHook(HOOK_INVENTORYMOVE)) Inject_SwitchHandHook();
// Gets dude perks and traits from script while controlling another NPC
// WARNING: Handling dude perks/traits in the engine code while controlling another NPC remains impossible, this requires serious hacking of the engine code
HookCall(0x458242, GetRealDudePerk); //op_has_trait_hook_
HookCall(0x458326, GetRealDudeTrait); //op_has_trait_hook_
HookCall(0x458242, GetRealDudePerk); // op_has_trait_
HookCall(0x458326, GetRealDudeTrait); // op_has_trait_
}
}
+27 -22
View File
@@ -76,7 +76,7 @@ struct FakePerk {
char Name[maxNameLen];
char Desc[maxDescLen];
char reserve[510]; // empty block
short id; // use last bytes of the description under the ID value for compatibility
short id; // perk id (use last bytes of the description under the ID value for compatibility)
FakePerk() {}
@@ -217,7 +217,11 @@ void _stdcall SetSelectablePerk(char* name, int active, int image, char* desc) {
}
void _stdcall SetFakePerk(char* name, int level, int image, char* desc) {
if (level < 0 || level > 100) return;
if (level > 100) {
level = 100;
} else if (level < 0 ) {
return;
}
size_t size = fakePerks.size();
if (level == 0) { // remove perk from fakePerks
for (size_t i = 0; i < size; i++) {
@@ -242,7 +246,11 @@ void _stdcall SetFakePerk(char* name, int level, int image, char* desc) {
}
void _stdcall SetFakeTrait(char* name, int active, int image, char* desc) {
if (active < 0 || active > 1) return;
if (active > 1) {
active = 1;
} else if (active < 0) {
return;
}
size_t size = fakeTraits.size();
if (active == 0) {
for (size_t i = 0; i < size; i++) {
@@ -326,8 +334,8 @@ static DWORD HandleFakeTraits(int isSelect) {
return isSelect;
}
static long __fastcall PlayerHasPerk(int* isSelectPtr) {
*isSelectPtr = HandleFakeTraits(*isSelectPtr);
static long __fastcall PlayerHasPerk(int &isSelectPtr) {
isSelectPtr = HandleFakeTraits(isSelectPtr);
for (int i = 0; i < PERK_count; i++) {
if (fo::func::perk_level(fo::var::obj_dude, i)) return 0x43438A; // print perks
@@ -335,7 +343,7 @@ static long __fastcall PlayerHasPerk(int* isSelectPtr) {
return (!fakePerks.empty()) ? 0x43438A : 0x434446; // skip print perks
}
static DWORD __fastcall HaveFakeTraits(int* isSelectPtr) {
static DWORD __fastcall HaveFakeTraits(int &isSelectPtr) {
return (fakeTraits.empty()) ? PlayerHasPerk(isSelectPtr) : 0x43425B;
}
@@ -344,7 +352,7 @@ static void __declspec(naked) PlayerHasPerkHack() {
push ecx; // isSelect
mov ecx, esp; // ptr to isSelect
call PlayerHasPerk;
pop ecx; // value from HandleFakeTraits
pop ecx; // isSelect value from HandleFakeTraits
jmp eax;
}
}
@@ -435,8 +443,7 @@ cLoop:
}
}
// Build a table of perks ID numbers available for selection
// data buffer has limited size for 119 perks
// Build a table of perks ID numbers available for selection, data buffer has limited size for 119 perks
static DWORD _stdcall HandleExtraSelectablePerks(DWORD available, DWORD* data) {
size_t count = extPerks.size();
for (size_t i = 0; i < count; i++) {
@@ -447,7 +454,7 @@ static DWORD _stdcall HandleExtraSelectablePerks(DWORD available, DWORD* data) {
for (size_t i = 0; i < count; i++) {
if (available >= 119) break;
// for fake perks, their ID should start from 256
data[available++] = startFakeID + i; //*(WORD*)(_name_sort_list + (offset+i)*8)=(WORD)(PERK_count+i);
data[available++] = startFakeID + i;
}
return available; // total number of perks available for selection
}
@@ -456,8 +463,7 @@ static void __declspec(naked) GetAvailablePerksHook() {
__asm {
push ecx;
push edx; // arg data
mov ecx, IgnoringDefaultPerks;
test ecx, ecx;
cmp IgnoringDefaultPerks, 0;
jnz skipDefaults;
call fo::funcoffs::perk_make_list_; // return available count
jmp next;
@@ -587,7 +593,7 @@ static void _stdcall AddFakePerk(DWORD perkID) {
}
}
if (!matched) { // add to fakePerks
RemovePerkID.push_back(count); // index of the added perk
RemovePerkID.push_back(count); // index of the added perk
int index = PerkSearchID(perkID);
fakePerks.emplace_back(extPerks[index].Name, 1, extPerks[index].data.image, extPerks[index].Desc, extPerks[index].id); // id same as perkID
}
@@ -626,8 +632,7 @@ static void _stdcall AddFakePerk(DWORD perkID) {
}
}
if (addPerkMode & 4) { // delete from selectable perks
RemoveSelectableID.push_back(perkID);
//fakeSelectablePerks.remove_at(perkID);
RemoveSelectableID.push_back(perkID); //fakeSelectablePerks.remove_at(perkID);
}
}
@@ -1282,15 +1287,15 @@ void _stdcall AddPerkMode(DWORD mode) {
addPerkMode = mode;
}
DWORD HasFakePerk(const char* name, long id) {
if (id < PERK_count && name[0] == 0) return 0;
DWORD HasFakePerk(const char* name, long perkId) {
if (perkId < PERK_count && name[0] == 0) return 0;
for (DWORD i = 0; i < fakePerks.size(); i++) {
if (id) {
if (fakePerks[i].id == id) return fakePerks[i].Level;
} else {
if (!strcmp(name, fakePerks[i].Name)) {
return fakePerks[i].Level; // current perk level
if (perkId) {
if (fakePerks[i].id == perkId) {
return fakePerks[i].Level;
}
} else if (!strcmp(name, fakePerks[i].Name)) {
return fakePerks[i].Level; // current perk level
}
}
return 0;
+17 -21
View File
@@ -511,28 +511,24 @@ end:
void __declspec(naked) op_remove_trait() {
__asm {
pushad;
mov ebp, eax;
call fo::funcoffs::interpretPopShort_;
mov edi, eax;
mov eax, ebp;
call fo::funcoffs::interpretPopLong_;
cmp di, VAR_TYPE_INT;
jnz end;
xor ebx, ebx;
dec ebx;
mov ecx, ds:[FO_VAR_pc_trait + 4];
cmp eax, ds:[FO_VAR_pc_trait];
jne next;
mov ds:[FO_VAR_pc_trait], ecx;
mov ds:[FO_VAR_pc_trait + 4], ebx;
jmp end;
next:
cmp eax, ds:[FO_VAR_pc_trait + 4];
jne end;
mov ds:[FO_VAR_pc_trait + 4], ebx;
push ecx;
_GET_ARG_INT(end);
test eax, eax;
jl end;
mov edx, -1;
cmp eax, ds:[FO_VAR_pc_trait];
jne next;
mov ecx, ds:[FO_VAR_pc_trait2];
mov ds:[FO_VAR_pc_trait], ecx;
mov ds:[FO_VAR_pc_trait2], edx;
end:
popad;
pop ecx;
retn;
next:
cmp eax, ds:[FO_VAR_pc_trait2];
jne end;
mov ds:[FO_VAR_pc_trait2], edx;
pop ecx;
retn;
}
}