diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index b46bc7d0..0acf25ae 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -270,6 +270,7 @@ #define item_weight(obj) sfall_func1("item_weight", obj) #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define loot_obj sfall_func0("loot_obj") +#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") diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 9495bb11..a98f8b51 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -569,6 +569,10 @@ Some utility/math functions are available: > void sfall_func0("art_cache_clear") - clears the cache of FRM image files loaded into memory +> void sfall_func1("npc_engine_level_up", bool toggle) +- enables/disables the engine function that increases the level of party members in the player leveling process +- if the engine function is disabled, the process of leveling up party members should be performed by script functions + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 1e32d1d3..6dd2fdd0 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -19,6 +19,7 @@ #include #include "Functions.h" +#include "FunctionOffsets.h" #include "Structs.h" #include "Variables.h" #include "VariableOffsets.h" @@ -107,20 +108,29 @@ bool HeroIsFemale() { return (fo::func::stat_level(fo::var::obj_dude, fo::Stat::STAT_gender) == fo::Gender::GENDER_FEMALE); } +long CheckAddictByPid(fo::GameObject* critter, long pid) { + __asm { + mov eax, pid; + mov esi, critter; + call fo::funcoffs::item_d_check_addict_; + } + /* keyword 'return' is not needed, the compiler will do everything correctly */ +} + //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { DWORD posOffset = Ypos * ToWidth + Xpos; __asm { - xor eax, eax - mov al, ColourIndex - push eax - mov edx, DisplayText - mov ebx, TxtWidth - mov ecx, ToWidth - mov eax, ToSurface - add eax, posOffset - call dword ptr ds:[FO_VAR_text_to_buf] + xor eax, eax; + mov al, ColourIndex; + push eax; + mov edx, DisplayText; + mov ebx, TxtWidth; + mov ecx, ToWidth; + mov eax, ToSurface; + add eax, posOffset; + call dword ptr ds:[FO_VAR_text_to_buf]; } } @@ -129,8 +139,8 @@ void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWOR DWORD GetTextHeight() { DWORD TxtHeight; __asm { - call dword ptr ds:[FO_VAR_text_height] //get text height - mov TxtHeight, eax + call dword ptr ds:[FO_VAR_text_height]; //get text height + mov TxtHeight, eax; } return TxtHeight; } @@ -140,9 +150,9 @@ DWORD GetTextHeight() { DWORD GetTextWidth(char *TextMsg) { DWORD TxtWidth; __asm { - mov eax, TextMsg - call dword ptr ds:[FO_VAR_text_width] //get text width - mov TxtWidth, eax + mov eax, TextMsg; + call dword ptr ds:[FO_VAR_text_width]; //get text width + mov TxtWidth, eax; } return TxtWidth; } @@ -152,9 +162,9 @@ DWORD GetTextWidth(char *TextMsg) { DWORD GetCharWidth(char CharVal) { DWORD charWidth; __asm { - mov al, CharVal - call dword ptr ds:[FO_VAR_text_char_width] - mov charWidth, eax + mov al, CharVal; + call dword ptr ds:[FO_VAR_text_char_width]; + mov charWidth, eax; } return charWidth; } @@ -164,9 +174,9 @@ DWORD GetCharWidth(char CharVal) { DWORD GetMaxTextWidth(char *TextMsg) { DWORD msgWidth; __asm { - mov eax, TextMsg - call dword ptr ds:[FO_VAR_text_mono_width] - mov msgWidth, eax + mov eax, TextMsg; + call dword ptr ds:[FO_VAR_text_mono_width]; + mov msgWidth, eax; } return msgWidth; } @@ -176,8 +186,8 @@ DWORD GetMaxTextWidth(char *TextMsg) { DWORD GetCharGapWidth() { DWORD gapWidth; __asm { - call dword ptr ds:[FO_VAR_text_spacing] - mov gapWidth, eax + call dword ptr ds:[FO_VAR_text_spacing]; + mov gapWidth, eax; } return gapWidth; } @@ -187,8 +197,8 @@ DWORD GetCharGapWidth() { DWORD GetMaxCharWidth() { DWORD charWidth = 0; __asm { - call dword ptr ds:[FO_VAR_text_max] - mov charWidth, eax + call dword ptr ds:[FO_VAR_text_max]; + mov charWidth, eax; } return charWidth; } diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index d07f6285..75e38449 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -60,6 +60,8 @@ GameObject* GetActiveItem(); bool HeroIsFemale(); +long CheckAddictByPid(fo::GameObject* critter, long pid); + // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); // gets the height of the currently selected font diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index 89bb67db..48e4b469 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -709,4 +709,11 @@ struct Queue { Queue* next; }; +#pragma pack(1) +struct DrugInfoList { + DWORD itemPid; + long addictGvar; + long numEffects; +}; + } diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index d79cdd2d..31d779c4 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -39,7 +39,7 @@ VAR_(dialogue_switch_mode, DWORD) VAR_(dialog_target, DWORD) VAR_(dialog_target_is_party, DWORD) VAR_(dropped_explosive, DWORD) -VAR_(drugInfoList, DWORD) +VARA(drugInfoList, DrugInfoList, 9) VAR_(edit_win, DWORD) VAR_(Educated, DWORD) VAR_(elevation, DWORD) diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index d0f9bc8a..e2981cb9 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -39,8 +39,6 @@ static char versionString[65]; static int* scriptDialog = nullptr; -bool npcAutoLevelEnabled; - static const DWORD PutAwayWeapon[] = { 0x411EA2, // action_climb_ladder_ 0x412046, // action_use_an_item_on_object_ @@ -467,15 +465,6 @@ static const DWORD EncounterTableSize[] = { 0x4C0815, 0x4C0D4A, 0x4C0FD4, }; -void NpcAutoLevelPatch() { - npcAutoLevelEnabled = GetConfigInt("Misc", "NPCAutoLevel", 0) != 0; - if (npcAutoLevelEnabled) { - dlog("Applying NPC autolevel patch.", DL_INIT); - SafeWrite8(0x495CFB, 0xEB); // jmps 0x495D28 (skip random check) - dlogr(" Done", DL_INIT); - } -} - void AdditionalWeaponAnimsPatch() { if (GetConfigInt("Misc", "AdditionalWeaponAnims", 0)) { dlog("Applying additional weapon animations patch.", DL_INIT); @@ -924,7 +913,6 @@ void MiscPatches::init() { LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load CombatProcFix(); - NpcAutoLevelPatch(); DialogueFix(); AdditionalWeaponAnimsPatch(); MultiPatchesPatch(); diff --git a/sfall/Modules/MiscPatches.h b/sfall/Modules/MiscPatches.h index 274844e0..43298074 100644 --- a/sfall/Modules/MiscPatches.h +++ b/sfall/Modules/MiscPatches.h @@ -35,9 +35,5 @@ struct CodeData { BYTE db = 0x90; }; -extern bool npcAutoLevelEnabled; - -void _stdcall SetMapMulti(float d); - } diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index 17562e8c..87e15116 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -30,6 +30,9 @@ namespace sfall { +bool npcAutoLevelEnabled; +bool npcEngineLevelUp = true; + bool isControllingNPC = false; bool skipCounterAnim = false; @@ -54,8 +57,7 @@ static struct DudeState { DWORD itemCurrentItem; fo::ItemButtonItem itemButtonItems[2]; long perkLevelDataList[fo::PERK_count]; - //DWORD drug_gvar[6]; - //DWORD jet_gvar; + long addictGvar[8]; long tag_skill[4]; //DWORD bbox_sneak; } realDude; @@ -78,6 +80,10 @@ static void SaveRealDudeState() { realDude.sneak_working = fo::var::sneak_working; fo::SkillGetTags(realDude.tag_skill, 4); + for (int i = 0; i < 6; i++) realDude.addictGvar[i] = fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar]; + realDude.addictGvar[6] = fo::var::game_global_vars[fo::var::drugInfoList[7].addictGvar]; + realDude.addictGvar[7] = fo::var::game_global_vars[fo::var::drugInfoList[8].addictGvar]; + if (skipCounterAnim) SafeWriteBatch(0, {0x422BDE, 0x4229EC}); // no animate } @@ -100,8 +106,8 @@ static void SetCurrentDude(fo::GameObject* npc) { fo::func::critter_pc_set_name(fo::func::critter_name(npc)); // change level - int level = fo::func::isPartyMember(npc) - ? fo::func::partyMemberGetCurLevel(npc) + int level = fo::func::isPartyMember(npc) + ? fo::func::partyMemberGetCurLevel(npc) : 0; fo::var::Level_ = level; @@ -121,6 +127,17 @@ static void SetCurrentDude(fo::GameObject* npc) { fo::var::itemCurrentItem = 1; } + long alcoholAddict = 0; + for (int i = 0; i < 9; i++) { + long result = fo::CheckAddictByPid(npc, fo::var::drugInfoList[i].itemPid); + if (i == 6) { + alcoholAddict = result; + } else if (i == 7) { + result |= alcoholAddict; + } + fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = result; + } + // switch main dude_obj pointers - this should be done last! fo::var::obj_dude = npc; fo::var::inven_dude = npc; @@ -160,6 +177,10 @@ static void RestoreRealDudeState() { fo::func::stat_pc_add_experience(delayedExperience); } + for (int i = 0; i < 6; i++) fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = realDude.addictGvar[i]; + fo::var::game_global_vars[fo::var::drugInfoList[7].addictGvar] = realDude.addictGvar[6]; + fo::var::game_global_vars[fo::var::drugInfoList[8].addictGvar] = realDude.addictGvar[7]; + if (skipCounterAnim) SafeWriteBatch(1, {0x422BDE, 0x4229EC}); // restore fo::func::intface_redraw(); @@ -279,12 +300,28 @@ static void __declspec(naked) gdControlUpdateInfo_hook() { } } -void PartyControl::init() { - LoadGameHook::OnGameReset() += PartyControlReset; +static void NpcAutoLevelPatch() { + npcAutoLevelEnabled = GetConfigInt("Misc", "NPCAutoLevel", 0) != 0; + if (npcAutoLevelEnabled) { + dlog("Applying NPC autolevel patch.", DL_INIT); + SafeWrite8(0x495CFB, 0xEB); // jmps 0x495D28 (skip random check) + dlogr(" Done", DL_INIT); + } +} +void PartyControl::init() { + LoadGameHook::OnGameReset() += []() { + PartyControlReset(); + if (!npcEngineLevelUp) { + npcEngineLevelUp = true; + SafeWrite16(0x4AFC1C, 0x840F); + } + }; HookCall(0x454218, stat_pc_add_experience_hook); // call inside op_give_exp_points_hook HookCalls(pc_flag_toggle_hook, { 0x4124F1, 0x41279A }); + NpcAutoLevelPatch(); + skipCounterAnim = (GetConfigInt("Misc", "SpeedInterfaceCounterAnims", 0) == 3); // display party member's current level & AC & addict flag diff --git a/sfall/Modules/PartyControl.h b/sfall/Modules/PartyControl.h index 5a9861e6..55a7b34a 100644 --- a/sfall/Modules/PartyControl.h +++ b/sfall/Modules/PartyControl.h @@ -39,5 +39,7 @@ public: }; extern bool isControllingNPC; +extern bool npcAutoLevelEnabled; +extern bool npcEngineLevelUp; } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 11fc7f8b..588b7c7c 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -110,6 +110,7 @@ static const SfallMetarule metarules[] = { {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, {"loot_obj", sf_get_loot_object, 0, 0}, + {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, {ARG_ANY}}, {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, @@ -129,38 +130,11 @@ static const SfallMetarule metarules[] = { {"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}}, + #ifndef NDEBUG {"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}}, + #endif }; - -static std::string sf_test_stringBuf; -void sf_test(OpcodeContext& ctx) { - std::ostringstream sstream; - sstream << "sfall_funcX(\"test\""; - for (int i = 0; i < ctx.numArgs(); i++) { - const ScriptValue &arg = ctx.arg(i); - sstream << ", "; - switch (arg.type()) { - case DataType::INT: - sstream << arg.asInt(); - break; - case DataType::FLOAT: - sstream << arg.asFloat(); - break; - case DataType::STR: - sstream << '"' << arg.asString() << '"'; - break; - default: - sstream << "???"; - break; - } - } - sstream << ")"; - - sf_test_stringBuf = sstream.str(); - ctx.setReturn(sf_test_stringBuf.c_str()); -} - // returns current contents of metarule table static void sf_get_metarule_table(OpcodeContext& ctx) { DWORD arrId = TempArray(metaruleTable.size(), 0); @@ -219,5 +193,35 @@ void HandleMetarule(OpcodeContext& ctx) { } } +#ifndef NDEBUG +static std::string sf_test_stringBuf; +void sf_test(OpcodeContext& ctx) { + std::ostringstream sstream; + sstream << "sfall_funcX(\"test\""; + for (int i = 0; i < ctx.numArgs(); i++) { + const ScriptValue &arg = ctx.arg(i); + sstream << ", "; + switch (arg.type()) { + case DataType::INT: + sstream << arg.asInt(); + break; + case DataType::FLOAT: + sstream << arg.asFloat(); + break; + case DataType::STR: + sstream << '"' << arg.asString() << '"'; + break; + default: + sstream << "???"; + break; + } + } + sstream << ")"; + + sf_test_stringBuf = sstream.str(); + ctx.setReturn(sf_test_stringBuf.c_str()); +} +#endif + } } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.h b/sfall/Modules/Scripting/Handlers/Metarule.h index 925a27ff..d864844e 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.h +++ b/sfall/Modules/Scripting/Handlers/Metarule.h @@ -31,7 +31,9 @@ namespace script { // Example handler. Feel free to add handlers in other files. +#ifndef NDEBUG void sf_test(OpcodeContext&); +#endif // returns current contents of metarule table void sf_get_metarule_table(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index d5c61e7f..62967ac2 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -25,9 +25,10 @@ #include "..\..\HeroAppearance.h" #include "..\..\Inventory.h" #include "..\..\KillCounter.h" -#include "..\..\MiscPatches.h" +//#include "..\..\MiscPatches.h" #include "..\..\Movies.h" #include "..\..\Objects.h" +#include "..\..\PartyControl.h" #include "..\..\PlayerModel.h" #include "..\..\ScriptExtender.h" #include "..\..\Stats.h" @@ -201,36 +202,6 @@ void __declspec(naked) op_game_loaded() { } } -void __declspec(naked) op_set_map_time_multi() { - __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_FLOAT; - jz paramWasFloat; - cmp dx, VAR_TYPE_INT; - jnz fail; - push eax; - fild dword ptr [esp]; - fstp dword ptr [esp]; - jmp end; -paramWasFloat: - push eax; -end: - call SetMapMulti; -fail: - pop edx; - pop ecx; - pop ebx; - retn; - } -} - void __declspec(naked) op_set_pipboy_available() { __asm { push ebx; @@ -1646,5 +1617,15 @@ void sf_get_ini_section(OpcodeContext& ctx) { ctx.setReturn(arrayId); } +void sf_npc_engine_level_up(OpcodeContext& ctx) { + if (ctx.arg(0).asBool()) { + if (!npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0x840F); // enable + npcEngineLevelUp = true; + } else { + if (npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0xE990); + npcEngineLevelUp = false; + } +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Misc.h b/sfall/Modules/Scripting/Handlers/Misc.h index 9ae70366..27425288 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.h +++ b/sfall/Modules/Scripting/Handlers/Misc.h @@ -41,8 +41,6 @@ void sf_get_year(OpcodeContext&); void __declspec() op_game_loaded(); -void __declspec() op_set_map_time_multi(); - void __declspec() op_set_pipboy_available(); // Kill counters @@ -165,5 +163,7 @@ void sf_get_ini_sections(OpcodeContext&); void sf_get_ini_section(OpcodeContext&); +void sf_npc_engine_level_up(OpcodeContext&); + } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 5aa7fadb..c873710c 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -227,6 +227,36 @@ end: } } +void __declspec(naked) op_set_map_time_multi() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call fo::funcoffs::interpretPopShort_; + mov edx, eax; + mov eax, ecx; + call fo::funcoffs::interpretPopLong_; + cmp dx, VAR_TYPE_FLOAT; + jz paramWasFloat; + cmp dx, VAR_TYPE_INT; + jnz fail; + push eax; + fild dword ptr [esp]; + fstp dword ptr [esp]; + jmp end; +paramWasFloat: + push eax; +end: + call SetMapMulti; +fail: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + void sf_set_car_intface_art(OpcodeContext& ctx) { Worldmap::SetCarInterfaceArt(ctx.arg(0).asInt()); } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 2a1cce7f..1b6d4788 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -40,6 +40,8 @@ void __declspec() op_get_world_map_y_pos(); void __declspec() op_set_world_map_pos(); +void __declspec() op_set_map_time_multi(); + void sf_set_car_intface_art(OpcodeContext&); void sf_set_map_enter_position(OpcodeContext&); diff --git a/sfall/Modules/Worldmap.h b/sfall/Modules/Worldmap.h index 5a28b3bd..d1dce2f8 100644 --- a/sfall/Modules/Worldmap.h +++ b/sfall/Modules/Worldmap.h @@ -44,4 +44,6 @@ public: static void SetAddedYears(DWORD years); }; +void _stdcall SetMapMulti(float d); + }