mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Rewrote the fix to allow fleeing NPC to use drugs.
Code edits to Perks.cpp.
This commit is contained in:
+30
-15
@@ -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_
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user