diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 1eaa3c58..2ac822d8 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -4,7 +4,8 @@ As well as the new functions, sfall also adds global scripts. These run independent of any loaded maps, but do not have an attached object. (i.e. using self_obj without using set_self first will crash the script.) To use a global script, the script must have a name which begins with 'gl' and contains a procedure called 'start', 'map_enter_p_proc', 'map_exit_p_proc', or 'map_update_p_proc'. The start procedure will be executed once when the player loads a saved game or starts a new game. The map_*_p_proc procedures will be executed once when a map is being entered/left/updated. If you wish the script to be executed repeatedly, call set_global_script_repeat on the first run of the start procedure using the number of frames between each run as the argument. (0 disables the script, 1 runs it every frame, 2 runs it every other frame etc.) -Global scripts have multiple modes, which can be set using the set_global_script_type function. In the default mode (i.e. mode 0) their execution is linked to the local map game loop, so the script will not run in dialogs or on the world map. In mode 1 their execution is linked to the player input, and so they will run whenever the mouse cursor is visible on screen, including the world map, character dialogs etc. In mode 2, execution is linked to the world map loop, so the script will only be executed on the world map and not on the local map or in any dialog windows. Mode 3 is a combination of modes 0 and 2, so scripts will be executed on both local maps and the world map, but not in dialog windows. Using mode 1 requires the input wrapper to be enabled. Use available_global_script_types to check what is available. +Global scripts have multiple modes, which can be set using the set_global_script_type function. In the default mode (i.e. mode 0) their execution is linked to the local map game loop, so the script will not run in dialogs or on the world map. In mode 1 their execution is linked to the player input, and so they will run whenever the mouse cursor is visible on screen, including the world map, character dialogs etc. In mode 2, execution is linked to the world map loop, so the script will only be executed on the world map and not on the local map or in any dialog windows. Mode 3 is a combination of modes 0 and 2, so scripts will be executed on both local maps and the world map, but not in dialog windows. +[Using mode 1 requires the input wrapper to be enabled. Use available_global_script_types to check what is available.] - Obsolete. diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 9f234a5e..71f59d61 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -1819,6 +1819,10 @@ void SkillSetTags(long* tags, long num) { WRAP_WATCOM_CALL2(skill_set_tags_, tags, num) } +long __fastcall GetItemType(TGameObj* item) { + return ItemGetType(item); +} + __declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, InvenType slot) { TGameObj* itemPtr = nullptr; switch (slot) { diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index a6fa8679..77c245e7 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -1531,6 +1531,8 @@ void SkillGetTags(long* result, long num); // wrapper for skill_set_tags with bounds checking void SkillSetTags(long* tags, long num); +long __fastcall GetItemType(TGameObj* item); + __declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, InvenType slot); long& GetActiveItemMode(); diff --git a/sfall/Graphics.cpp b/sfall/Graphics.cpp index 411c640a..35952d70 100644 --- a/sfall/Graphics.cpp +++ b/sfall/Graphics.cpp @@ -304,7 +304,7 @@ static void WindowInit() { static void GetDisplayMode(D3DDISPLAYMODE &ddm) { ZeroMemory(&ddm, sizeof(ddm)); d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &ddm); - dlog_f("Display mode format id: %d\n", DL_INIT, ddm.Format); + dlog_f("Display mode format ID: %d\n", DL_INIT, ddm.Format); } static void ResetDevice(bool createNew) { @@ -718,14 +718,13 @@ private: bool isPrimary; BYTE* lockTarget; - FakeDirectDrawSurface() : lockTarget(nullptr) {} - public: static bool IsPlayMovie; FakeDirectDrawSurface(bool primary) { Refs = 1; isPrimary = primary; + lockTarget = nullptr; if (primary && GPUBlt) { // use the mainTex texture as a buffer } else { diff --git a/sfall/InputFuncs.cpp b/sfall/InputFuncs.cpp index b2261717..6bad3a43 100644 --- a/sfall/InputFuncs.cpp +++ b/sfall/InputFuncs.cpp @@ -260,7 +260,7 @@ public: if (DeviceType != kDeviceType_KEYBOARD) { return RealDevice->GetDeviceData(a, buf, count, d); } - if (!buf && !d && *count == INFINITE) { // flush + if (*count == INFINITE && !buf && !d) { // flush while (!bufferedPresses.empty()) bufferedPresses.pop(); return RealDevice->GetDeviceData(a, buf, count, d); } diff --git a/sfall/MiscPatches.cpp b/sfall/MiscPatches.cpp index 9e9bca67..6554cbd4 100644 --- a/sfall/MiscPatches.cpp +++ b/sfall/MiscPatches.cpp @@ -209,6 +209,62 @@ static void __declspec(naked) display_stats_hook() { } } +static void __fastcall SwapHandSlots(TGameObj* item, TGameObj* &toSlot) { + if (toSlot && GetItemType(item) != item_type_weapon && GetItemType(toSlot) != item_type_weapon) { + return; + } + ItemButtonItem* leftSlot = &ptr_itemButtonItems[0]; + ItemButtonItem* rightSlot = &ptr_itemButtonItems[1]; + + if (toSlot == nullptr) { // copy to slot + ItemButtonItem* slot; + ItemButtonItem item[1]; + if ((int)&toSlot == _i_lhand) { + std::memcpy(item, rightSlot, 0x14); + item[0].primaryAttack = ATKTYPE_LWEAPON_PRIMARY; + item[0].secondaryAttack = ATKTYPE_LWEAPON_SECONDARY; + slot = leftSlot; // Rslot > Lslot + } else { + std::memcpy(item, leftSlot, 0x14); + item[0].primaryAttack = ATKTYPE_RWEAPON_PRIMARY; + item[0].secondaryAttack = ATKTYPE_RWEAPON_SECONDARY; + slot = rightSlot; // Lslot > Rslot; + } + std::memcpy(slot, item, 0x14); + } else { // swap slots + ItemButtonItem hands[2]; + std::memcpy(hands, ptr_itemButtonItems, sizeof(ItemButtonItem) * 2); + hands[0].primaryAttack = ATKTYPE_RWEAPON_PRIMARY; + hands[0].secondaryAttack = ATKTYPE_RWEAPON_SECONDARY; + hands[1].primaryAttack = ATKTYPE_LWEAPON_PRIMARY; + hands[1].secondaryAttack = ATKTYPE_LWEAPON_SECONDARY; + + std::memcpy(leftSlot, &hands[1], 0x14); // Rslot > Lslot + std::memcpy(rightSlot, &hands[0], 0x14); // Lslot > Rslot + } +} + +static void __declspec(naked) switch_hand_hack() { + __asm { + pushfd; + test ebx, ebx; + jz skip; + cmp ebx, edx; + jz skip; + push ecx; + mov ecx, eax; + call SwapHandSlots; + pop ecx; +skip: + popfd; + jz end; + retn; +end: + mov dword ptr [esp], 0x4715B7; + retn; + } +} + static void __declspec(naked) endgame_movie_hook() { __asm { cmp [esp + 16], 0x45C563; // call from op_endgame_movie_ @@ -541,6 +597,14 @@ static void DisplaySecondWeaponRangePatch() { //} } +static void KeepWeaponSelectModePatch() { + //if (GetConfigInt("Misc", "KeepWeaponSelectMode", 1)) { + dlog("Applying keep weapon select mode patch.", DL_INIT); + MakeCall(0x4714EC, switch_hand_hack, 1); + dlogr(" Done", DL_INIT); + //} +} + #pragma pack(push, 1) struct CodeData { DWORD dd; @@ -747,6 +811,7 @@ void MiscPatchesInit() { NumbersInDialoguePatch(); DisplaySecondWeaponRangePatch(); + KeepWeaponSelectModePatch(); SkipLoadingGameSettingsPatch(); InterfaceDontMoveOnTopPatch();