Fixed getting perks/traits from the real dude_obj while controlling other NPCs.

Added "real_dude_obj" script function and updated documents.
This commit is contained in:
NovaRain
2019-05-22 12:59:27 +08:00
parent 301ccba45f
commit 2c7548f164
10 changed files with 92 additions and 41 deletions
+1
View File
@@ -240,6 +240,7 @@
#define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle)
#define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude)
#define outlined_object sfall_func0("outlined_object")
#define real_dude_obj sfall_func0("real_dude_obj")
#define set_cursor_mode(mode) sfall_func1("set_cursor_mode", mode)
#define set_flags(obj, flags) sfall_func2("set_flags", obj, flags)
#define set_ini_setting(setting, value) sfall_func2("set_ini_setting", setting, value)
@@ -415,6 +415,9 @@ Some utility/math functions are available:
> object sfall_func0("outlined_object")
- returns an object that is currently highlighted by hovering the mouse above it
> void sfall_func0("real_dude_obj")
- returns the initial dude_obj after taking control of other critters
> int sfall_func0("get_cursor_mode")
- returns the current cursor mode (0 - movement cursor, 1 - command cursor, 2 - targeting cursor)
- mode 4 to 10 are Skilldex skills (yellow targeting cursor)
+15
View File
@@ -1004,6 +1004,21 @@ long __stdcall StatLevel(TGameObj* critter, long statId) {
}
}
long __stdcall PerkLevel(TGameObj* critter, long perkId) {
__asm {
mov edx, perkId;
mov eax, critter;
call perk_level_;
}
}
long __stdcall TraitLevel(long traitID) {
__asm {
mov eax, traitID;
call trait_level_;
}
}
long __stdcall QueueFindFirst(TGameObj* object, long qType) {
__asm {
mov edx, qType;
+4
View File
@@ -1028,6 +1028,10 @@ void __fastcall DisplayTargetInventory(long inventoryOffset, long visibleOffset,
long __stdcall StatLevel(TGameObj* critter, long statId);
long __stdcall PerkLevel(TGameObj* critter, long perkId);
long __stdcall TraitLevel(long traitID);
long __stdcall QueueFindFirst(TGameObj* object, long qType);
long __stdcall NewObjId();
+1 -7
View File
@@ -147,13 +147,7 @@ static DWORD _stdcall CombatSaveTest() {
return 0;
}
int ap = StatLevel(*ptr_obj_dude, STAT_max_move_points);
int bonusmove;
__asm {
mov edx, PERK_bonus_move;
mov eax, ds:[_obj_dude];
call perk_level_;
mov bonusmove, eax;
}
int bonusmove = PerkLevel(*ptr_obj_dude, PERK_bonus_move);
if (*(DWORD*)(*(DWORD*)_obj_dude + 0x40) != ap || bonusmove * 2 != *(DWORD*)_combat_free_move) {
DisplayConsoleMessage(SaveFailMsg);
return 0;
+56 -33
View File
@@ -35,7 +35,7 @@ bool npcAutoLevelEnabled;
bool npcEngineLevelUp = true;
static DWORD Mode;
static int IsControllingNPC = 0;
static bool IsControllingNPC = false;
static bool SkipCounterAnim = false;
static std::vector<WORD> Chars;
static int DelayedExperience;
@@ -52,7 +52,7 @@ static DWORD real_unspent_skill_points;
static DWORD real_sneak_working;
//static DWORD real_sneak_queue_time;
static DWORD real_hand;
static DWORD real_itemButtonItems[6*2];
static DWORD real_itemButtonItems[6 * 2];
static DWORD real_perkLevelDataList[PERK_count];
//static DWORD real_drug_gvar[6];
//static DWORD real_jet_gvar;
@@ -74,10 +74,10 @@ static bool _stdcall IsInPidList(TGameObj* obj) {
static void _stdcall SetInventoryCheck(bool skip) {
if (skip) {
SafeWrite16(0x46E7CD, 0x9090); //Inventory check
SafeWrite32(0x46E7Cf, 0x90909090);
SafeWrite32(0x46E7CF, 0x90909090);
} else {
SafeWrite16(0x46E7CD, 0x850F); //Inventory check
SafeWrite32(0x46E7Cf, 0x4B1);
SafeWrite32(0x46E7CF, 0x4B1);
}
}
@@ -156,7 +156,7 @@ static void TakeControlOfNPC(TGameObj* npc) {
*ptr_obj_dude = npc;
*ptr_inven_dude = npc;
IsControllingNPC = 1;
IsControllingNPC = true;
DelayedExperience = 0;
SetInventoryCheck(true);
@@ -165,10 +165,13 @@ static void TakeControlOfNPC(TGameObj* npc) {
// restores the real dude state
static void RestoreRealDudeState() {
assert(real_dude != nullptr);
*ptr_map_elevation = real_dude->elevation;
*ptr_obj_dude = real_dude;
*ptr_inven_dude = real_dude;
*ptr_inven_pid = real_dude->pid;
*ptr_itemCurrentItem = real_hand;
memcpy(ptr_itemButtonItems, real_itemButtonItems, sizeof(DWORD) * 6 * 2);
@@ -183,8 +186,6 @@ static void RestoreRealDudeState() {
*ptr_sneak_working = real_sneak_working;
SkillSetTags(real_tag_skill, 4);
*ptr_inven_pid = real_dude->pid;
if (DelayedExperience > 0) {
StatPcAddExperience(DelayedExperience);
}
@@ -196,7 +197,7 @@ static void RestoreRealDudeState() {
InterfaceRedraw();
SetInventoryCheck(false);
IsControllingNPC = 0;
IsControllingNPC = false;
real_dude = nullptr;
}
@@ -235,8 +236,8 @@ static void __declspec(naked) FidChangeHook() {
je skip;
push eax;
mov eax, [eax+0x20]; // current fid
and eax, 0xffff0fff;
and edx, 0x0000f000;
and eax, 0xFFFF0FFF;
and edx, 0x0000F000;
or edx, eax; // only change one octet with weapon type
pop eax;
skip:
@@ -260,7 +261,7 @@ static void __stdcall DisplayCantDoThat() {
// 1 skip handler, -1 don't skip
int __stdcall PartyControl_SwitchHandHook(TGameObj* item) {
if (IsControllingNPC > 0 && ItemGetType(item) == item_type_weapon) {
if (IsControllingNPC && ItemGetType(item) == item_type_weapon) {
int canUse;
/* check below uses AI packets and skills to check if weapon is usable
__asm {
@@ -273,7 +274,7 @@ int __stdcall PartyControl_SwitchHandHook(TGameObj* item) {
}*/
int fId = (*ptr_obj_dude)->artFID;
char weaponCode = AnimCodeByWeapon(item);
fId = (fId & 0xffff0fff) | (weaponCode << 12);
fId = (fId & 0xFFFF0FFF) | (weaponCode << 12);
// check if art with this weapon exists
__asm {
mov eax, fId;
@@ -288,6 +289,19 @@ int __stdcall PartyControl_SwitchHandHook(TGameObj* item) {
return -1;
}
long __fastcall GetRealDudePerk(TGameObj* source, long perk) {
if (IsControllingNPC && source == real_dude) {
return real_perkLevelDataList[perk];
}
return PerkLevel(source, perk);
}
long __fastcall GetRealDudeTrait(TGameObj* source, long trait) {
if (IsControllingNPC && source == real_dude) {
return (trait == real_traits[0] || trait == real_traits[1]) ? 1 : 0;
}
return TraitLevel(trait);
}
static void __declspec(naked) CombatWrapper_v2() {
__asm {
@@ -329,38 +343,42 @@ gonormal:
static void __declspec(naked) stat_pc_add_experience_hook() {
__asm {
xor eax, eax
cmp IsControllingNPC, eax
je skip
add DelayedExperience, esi
retn
cmp IsControllingNPC, 0;
je skip;
add DelayedExperience, esi;
retn;
skip:
xchg esi, eax
jmp stat_pc_add_experience_
xchg esi, eax;
jmp stat_pc_add_experience_;
}
}
// prevents using sneak when controlling NPCs
static void __declspec(naked) pc_flag_toggle_hook() {
__asm {
cmp IsControllingNPC, 0
je end
call DisplayCantDoThat
retn
cmp IsControllingNPC, 0;
je end;
call DisplayCantDoThat;
retn;
end:
call pc_flag_toggle_
retn
jmp pc_flag_toggle_;
}
}
void __stdcall PartyControlReset() {
if (real_dude != nullptr && IsControllingNPC > 0) {
if (real_dude != nullptr && IsControllingNPC) {
RestoreRealDudeState();
}
}
bool IsNpcControlled() {
return IsControllingNPC != 0;
return IsControllingNPC;
}
TGameObj* RealDudeObject() {
return real_dude != nullptr
? real_dude
: *ptr_obj_dude;
}
static char levelMsg[12], armorClassMsg[12], addictMsg[16];
@@ -442,15 +460,20 @@ void PartyControlInit() {
}
dlog_f(" Mode %d, Chars read: %d.\n", DL_INIT, Mode, Chars.size());
HookCall(0x46EBEE, &FidChangeHook);
HookCall(0x46EBEE, FidChangeHook);
MakeJump(0x422354, CombatHack_add_noncoms_);
HookCall(0x422D87, &CombatWrapper_v2);
HookCall(0x422E20, &CombatWrapper_v2);
HookCall(0x422D87, CombatWrapper_v2);
HookCall(0x422E20, CombatWrapper_v2);
HookCall(0x454218, &stat_pc_add_experience_hook); // call inside op_give_exp_points_hook
HookCall(0x4124F1, &pc_flag_toggle_hook);
HookCall(0x41279A, &pc_flag_toggle_hook);
HookCall(0x454218, stat_pc_add_experience_hook); // call inside op_give_exp_points_hook
HookCall(0x4124F1, pc_flag_toggle_hook);
HookCall(0x41279A, pc_flag_toggle_hook);
// 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_
} else
dlogr(" Disabled.", DL_INIT);
+1
View File
@@ -25,4 +25,5 @@ void PartyControlInit();
void __stdcall PartyControlReset();
int __stdcall PartyControl_SwitchHandHook(TGameObj* item);
bool IsNpcControlled();
TGameObj* RealDudeObject();
void NpcEngineLevelUpReset();
+1
View File
@@ -130,6 +130,7 @@ static const SfallMetarule metaruleArray[] = {
{"npc_engine_level_up", sf_npc_engine_level_up, 1, 1},
{"obj_under_cursor", sf_get_obj_under_cursor, 2, 2},
{"outlined_object", sf_outlined_object, 0, 0},
{"real_dude_obj", sf_real_dude_obj, 0, 0},
{"set_cursor_mode", sf_set_cursor_mode, 1, 1},
{"set_flags", sf_set_flags, 2, 2},
{"set_ini_setting", sf_set_ini_setting, 2, 2},
+5
View File
@@ -21,6 +21,7 @@
#include "main.h"
#include "Inventory.h"
#include "PartyControl.h"
#include "ScriptExtender.h"
//script control functions
@@ -596,6 +597,10 @@ static void sf_item_weight() {
opHandler.setReturn(weight);
}
static void sf_real_dude_obj() {
opHandler.setReturn(RealDudeObject());
}
static void sf_lock_is_jammed() {
TGameObj* obj = opHandler.arg(0).asObject();
int result;
+5 -1
View File
@@ -18,8 +18,12 @@
#pragma once
#include <assert.h>
//#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>
#include <vector>
#include "SafeWrite.h"
#include "Logging.h"
@@ -94,4 +98,4 @@ T SimplePatch(DWORD *addrs, int numAddrs, const char* iniSection, const char* in
dlogr(" Done", DL_INIT);
}
return value;
}
}