diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 3225af83..fcf50ef6 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -494,12 +494,12 @@ UseScrollingQuestsList=1 ExtraSaveSlots=0 ;To use more than one save slot for quick saving (F6 key) without picking a slot beforehand, set the next two lines -;AutoQuickSave sets how many save slots on a page you want to use for quick saving (valid range: 1..10) -;AutoQuickSavePage is the page number to use (valid range: 1..1000) if ExtraSaveSlots is enabled -;The quick saves will be rotated from the first slot on the page to the n-th slot +;AutoQuickSave sets how many pages you want to use for quick saving (valid range: 1..10) +;AutoQuickSavePage is the page number to start at (valid range: 1..1000, if ExtraSaveSlots is enabled) +;The quick saves will be rotated from the first slot on the page to the last slot on the n-th page ;Set to 0 to disable. AutoQuickSave will use the current selected page if AutoQuickSavePage is disabled AutoQuickSave=0 -AutoQuickSavePage=0 +AutoQuickSavePage=1 ;Set to 1 to speed up the HP/AC counter animations ;Set to 2 to update the HP/AC counters instantly @@ -649,7 +649,7 @@ ExplosionsEmitLight=0 ;Set to 1 to enable the new arrays behavior (default behavior) ;Set to 0 for backward compatibility with pre-3.4 scripts -arraysBehavior=1 +ArraysBehavior=1 ;Set to 1 to add proper checks to see if there is enough ammo to attack with weapons that use multiple ammo per shot CheckWeaponAmmoCost=0 diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index f243b7d4..639dede8 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -257,16 +257,10 @@ if (get_flags(obj1) bwand FLAG_MULTIHEX) then distance--; \ if (get_flags(obj2) bwand FLAG_MULTIHEX) then distance-- - -/* sfall metarule3 function macros */ -// sets the number of days (range 1...127) for the Frank Horrigan encounter, or disable the encounter if days is set to 0 -#define set_horrigan_days(day) metarule3(200, day, 0, 0) -// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling functions that are waiting for keyboard input -#define clear_keyboard_buffer metarule3(201, 0, 0, 0) - // checks if the specified PID number exists in the list of registered protos #define check_pid(pid) (get_proto_data(pid, 0) != -1) + /* sfall_funcX macros */ #define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name) #define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam) @@ -365,4 +359,19 @@ #define unwield_slot(critter, slot) sfall_func2("unwield_slot", critter, slot) #define win_fill_color(x, y, width, height, color) sfall_func5("win_fill_color", x, y, width, height, color) + +/* sfall metarule3 function macros */ +// sets the number of days (range 1...127) for the Frank Horrigan encounter, or disable the encounter if days is set to 0 +#define set_horrigan_days(day) metarule3(200, day, 0, 0) +// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling functions that are waiting for keyboard input +#define clear_keyboard_buffer metarule3(201, 0, 0, 0) + +// functions to control the save slot +// Note: page and slot here are 0-indexed instead of 1-indexed displayed in game +#define get_current_save_slot metarule3(1000, 0, 0, 0) // returns the amount: page + slot +#define set_current_save_slot(page, slot) metarule3(1001, page, slot, 0) +#define get_current_quick_save_page metarule3(1002, 0, 0, 0) +#define get_current_quick_save_slot metarule3(1003, 0, 0, 0) +#define set_current_quick_save_slot(page, slot, check) metarule3(1004, page, slot, check) // check: 1 - don't check slot when saving + #endif diff --git a/sfall/ExtraSaveSlots.cpp b/sfall/ExtraSaveSlots.cpp index c2ee5f31..ca2ec901 100644 --- a/sfall/ExtraSaveSlots.cpp +++ b/sfall/ExtraSaveSlots.cpp @@ -23,14 +23,23 @@ #include "ExtraSaveSlots.h" -DWORD LSPageOffset = 0; - +static long LSPageOffset = 0; static long LSButtDN = 0; static BYTE* SaveLoadSurface = nullptr; static const char* filename = "%s\\savegame\\slotdat.ini"; -static void SavePageOffsets() { +long ExtraSaveSlots_GetSaveSlot() { + return LSPageOffset + *ptr_slot_cursor; +} + +void ExtraSaveSlots_SetSaveSlot(long page, long slot) { + if (ExtraSaveSlots_GetQuickSavePage() >= 0 && page >= 0 && page <= 9990) LSPageOffset = page - (page % 10);; + if (slot >= 0 && slot < 10) *ptr_slot_cursor = slot; +} + +// save last slot position values to file +static long save_page_offsets() { char SavePath[MAX_PATH], buffer[6]; sprintf_s(SavePath, MAX_PATH, filename, *ptr_patches); @@ -40,16 +49,8 @@ static void SavePageOffsets() { _itoa_s(LSPageOffset, buffer, 10); WritePrivateProfileStringA("POSITION", "PageOffset", buffer, SavePath); -} -static void __declspec(naked) save_page_offsets(void) { - __asm { - // save last slot position values to file - call SavePageOffsets; - // restore original code - mov eax, dword ptr ds:[FO_VAR_lsgwin]; - retn; - } + return *ptr_lsgwin; // restore original code } static void LoadPageOffsets() { @@ -71,16 +72,15 @@ static void __declspec(naked) load_page_offsets(void) { __asm { // load last slot position values from file call LoadPageOffsets; - // restore original code - mov edx, 0x50A480; // ASCII "SAV" + mov edx, 0x50A480; // ASCII "SAV" (restore original code) retn; } } -static void CreateButtons() { +static long create_page_buttons() { DWORD winRef = *ptr_lsgwin; - // left button -10 | X | Y | W | H |HOn |HOff |BDown |BUp |PicUp |PicDown |? |ButType + // left button -10 | X | Y | W | H |HOn |HOff |BDown |BUp |PicUp |PicDown |? |ButType fo_win_register_button(winRef, 100, 60, 24, 20, -1, 0x500, 0x54B, 0x14B, 0, 0, 0, 32); // left button -100 fo_win_register_button(winRef, 68, 60, 24, 20, -1, 0x500, 0x549, 0x149, 0, 0, 0, 32); @@ -90,14 +90,8 @@ static void CreateButtons() { fo_win_register_button(winRef, 248, 60, 24, 20, -1, 0x500, 0x551, 0x151, 0, 0, 0, 32); // Set Number button fo_win_register_button(winRef, 140, 60, 60, 20, -1, -1, 'p', -1, 0, 0, 0, 32); -} -static void __declspec(naked) create_page_buttons(void) { - __asm { - call CreateButtons; - mov eax, 0x65; // restore original code - retn; - } + return 101; // restore original value } static void SetPageNum() { @@ -212,12 +206,12 @@ static long __fastcall CheckPage(long button) { break; case 0x14D: // right button LSPageOffset += 10; - if (LSPageOffset > 9990) LSPageOffset -= 10000; // to the first page + if (LSPageOffset >= 10000) LSPageOffset -= 10000; // to the first page __asm call gsound_red_butt_press_; break; case 0x151: // fast right PGDN button LSPageOffset += 100; - if (LSPageOffset > 9990) LSPageOffset -= 10000; + if (LSPageOffset >= 10000) LSPageOffset -= 10000; __asm call gsound_red_butt_press_; break; case 'p': // p/P button pressed - start SetPageNum func @@ -234,15 +228,17 @@ static long __fastcall CheckPage(long button) { static void __declspec(naked) check_page_buttons(void) { __asm { - pushad; + push eax; + push ecx; mov ecx, eax; call CheckPage; test eax, eax; - popad; - jnz CheckUp; + pop ecx; + pop eax; + jnz checkUp; add dword ptr ds:[esp], 26; // set return to button pressed code jmp GetSlotList_; // reset page save list func -CheckUp: +checkUp: // restore original code cmp eax, 0x148; // up button retn; @@ -323,25 +319,25 @@ static void DrawPageText() { static void __declspec(naked) draw_page_text(void) { __asm { - pushad; + push eax; call DrawPageText; - popad; - mov ebp, 0x57; // restore original code + pop eax; + mov ebp, 87; // restore original code retn; } } // add page num offset when reading and writing various save data files -static void __declspec(naked) AddPageOffset01(void) { +static void __declspec(naked) add_page_offset_hack1(void) { __asm { mov eax, dword ptr ds:[FO_VAR_slot_cursor]; // list position 0-9 - add eax, LSPageOffset; // add page num offset + add eax, LSPageOffset; // add page num offset retn; } } // getting info for the 10 currently displayed save slots from save.dats -static void __declspec(naked) AddPageOffset02(void) { +static void __declspec(naked) add_page_offset_hack2(void) { __asm { push 0x50A514; // ASCII "SAVE.DAT" lea eax, [ebx + 1]; @@ -352,7 +348,7 @@ static void __declspec(naked) AddPageOffset02(void) { } // printing current 10 slot numbers -static void __declspec(naked) AddPageOffset03(void) { +static void __declspec(naked) add_page_offset_hack3(void) { __asm { inc eax; add eax, LSPageOffset; // add page num offset @@ -363,34 +359,38 @@ static void __declspec(naked) AddPageOffset03(void) { static void EnableSuperSaving() { // save/load button setup func - MakeCall(0x47D80D, create_page_buttons); + MakeCall(0x47D80D, create_page_buttons); // LSGameStart_ // Draw button text - MakeCall(0x47E6E8, draw_page_text); + MakeCall(0x47E6E8, draw_page_text); // ShowSlotList_ // check save/load buttons - const DWORD checkPageBtnsAddr[] = {0x47BD49, 0x47CB1C}; + const DWORD checkPageBtnsAddr[] = {0x47BD49, 0x47CB1C}; // SaveGame_, LoadGame_ MakeCalls(check_page_buttons, checkPageBtnsAddr); // save current page and list positions to file on load/save scrn exit - MakeCall(0x47D828, save_page_offsets); + MakeCall(0x47D828, save_page_offsets); // LSGameEnd_ // load saved page and list positions from file - MakeCall(0x47B82B, load_page_offsets); + MakeCall(0x47B82B, load_page_offsets); // InitLoadSave_ // Add Load/Save page offset to Load/Save folder number - const DWORD AddPageOffset01Addr[] = { - 0x47B929, 0x47D8DB, 0x47D9B0, 0x47DA34, 0x47DABF, 0x47DB58, 0x47DBE9, - 0x47DC9C, 0x47EC77, 0x47F5AB, 0x47F694, 0x47F6EB, 0x47F7FB, 0x47F892, - 0x47FB86, 0x47FC3A, 0x47FCF2, 0x480117, 0x4801CF, 0x480234, 0x480310, - 0x4803F3, 0x48049F, 0x480512, 0x4805F2, 0x480767, 0x4807E6, 0x480839, - 0x4808D3 + const DWORD AddPageOffset1Addr[] = { + 0x47B929, // SaveGame_ + 0x47D8DB, 0x47D9B0, 0x47DA34, 0x47DABF, 0x47DB58, 0x47DBE9, // SaveSlot_ + 0x47DC9C, // LoadSlot_ + 0x47EC77, // LoadTumbSlot_ + 0x47F5AB, 0x47F694, 0x47F6EB, 0x47F7FB, 0x47F892, // GameMap2Slot_ + 0x47FB86, 0x47FC3A, 0x47FCF2, // SlotMap2Game_ + 0x480117, 0x4801CF, 0x480234, 0x480310, // SaveBackup_ + 0x4803F3, 0x48049F, 0x480512, 0x4805F2, // RestoreSave_ + 0x480767, 0x4807E6, 0x480839, 0x4808D3 // EraseSave_ }; - MakeCalls(AddPageOffset01, AddPageOffset01Addr); + MakeCalls(add_page_offset_hack1, AddPageOffset1Addr); - MakeJump(0x47E5E1, AddPageOffset02); + MakeJump(0x47E5E1, add_page_offset_hack2); // GetSlotList_ - MakeCall(0x47E756, AddPageOffset03); + MakeCall(0x47E756, add_page_offset_hack3); // ShowSlotList_ } static void GetSaveFileTime(char* filename, FILETIME* ftSlot) { @@ -407,43 +407,69 @@ static void GetSaveFileTime(char* filename, FILETIME* ftSlot) { }; } -static const char* commentFmt = "%02d/%02d/%d - %02d:%02d:%02d"; +static const char* autoFmt = "AUTO: %02d/%02d/%d - %02d:%02d:%02d"; +static const char* quickFmt = "QUICK: %02d/%02d/%d - %02d:%02d:%02d"; -static void CreateSaveComment(char* bufstr) { +static void CreateSaveComment(char* bufstr, bool isAuto) { SYSTEMTIME stUTC, stLocal; GetSystemTime(&stUTC); SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal); - char buf[30]; - sprintf_s(buf, commentFmt, stLocal.wDay, stLocal.wMonth, stLocal.wYear, stLocal.wHour, stLocal.wMinute, stLocal.wSecond); - strcpy(bufstr, buf); + const char* fmt = (isAuto) ? autoFmt : quickFmt; + sprintf_s(bufstr, 30, fmt, stLocal.wDay, stLocal.wMonth, stLocal.wYear, stLocal.wHour, stLocal.wMinute, stLocal.wSecond); } -static long autoQuickSave = 0; -static long quickSavePage = 0; +static long quickSavePageInit = -1; +static long quickSavePageCount; +static long currentPageCount; + +static long quickSavePage = -1; +static long quickSaveSlot = 0; + +static long dontCheckSlot = 0; +static bool qFirst = true; static FILETIME ftPrevSlot; static DWORD __stdcall QuickSaveGame(DbFile* file, char* filename) { long currSlot = *ptr_slot_cursor; - if (file) { // This slot is not empty - fo_db_fclose(file); + if (dontCheckSlot) { + if (file) fo_db_fclose(file); + } else { + // for quick feature + if (file) { // This slot is not empty + fo_db_fclose(file); - FILETIME ftCurrSlot; - GetSaveFileTime(filename, &ftCurrSlot); + FILETIME ftCurrSlot; + GetSaveFileTime(filename, &ftCurrSlot); - if (currSlot == 0 || ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime || - (ftCurrSlot.dwHighDateTime == ftPrevSlot.dwHighDateTime && ftCurrSlot.dwLowDateTime > ftPrevSlot.dwLowDateTime)) - { - ftPrevSlot.dwHighDateTime = ftCurrSlot.dwHighDateTime; - ftPrevSlot.dwLowDateTime = ftCurrSlot.dwLowDateTime; + if (currSlot == 0 || + ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime || + (ftCurrSlot.dwHighDateTime == ftPrevSlot.dwHighDateTime && ftCurrSlot.dwLowDateTime > ftPrevSlot.dwLowDateTime)) + { + ftPrevSlot.dwHighDateTime = ftCurrSlot.dwHighDateTime; + ftPrevSlot.dwLowDateTime = ftCurrSlot.dwLowDateTime; - if (++currSlot > autoQuickSave) { - currSlot = 0; - } else { - *ptr_slot_cursor = currSlot; - return 0x47B929; // check next slot + if (!qFirst && currSlot < 9) { + *ptr_slot_cursor = ++quickSaveSlot; + return 0x47B929; // check next slot + } + currSlot = 0; // set if currSlot >= 9 + qFirst = false; + } + } + // next save slot + if (++quickSaveSlot >= 10) { + quickSaveSlot = 0; + // next page + if (quickSavePageCount > 1 && quickSavePageInit != -1) { + if (++currentPageCount >= quickSavePageCount) { + currentPageCount = 0; + quickSavePage = quickSavePageInit; + } else if (quickSavePage <= 9980) { + quickSavePage += 10; + } } } } @@ -451,7 +477,7 @@ static DWORD __stdcall QuickSaveGame(DbFile* file, char* filename) { // Save to slot *ptr_slot_cursor = currSlot; LSData* saveData = (LSData*)FO_VAR_LSData; - CreateSaveComment(saveData[currSlot].comment); + CreateSaveComment(saveData[currSlot].comment, dontCheckSlot != 0); *ptr_quick_done = 1; return 0x47B9A4; // normal return @@ -471,13 +497,28 @@ static void __declspec(naked) SaveGame_hack0() { static void __declspec(naked) SaveGame_hack1() { __asm { - mov ds:[FO_VAR_slot_cursor], 0; + mov eax, quickSaveSlot; + mov ds:[FO_VAR_slot_cursor], eax; mov eax, quickSavePage; mov LSPageOffset, eax; retn; } } +long ExtraSaveSlots_GetQuickSavePage() { + return quickSavePage; +} + +long ExtraSaveSlots_GetQuickSaveSlot() { + return quickSaveSlot; +} + +void ExtraSaveSlots_SetQuickSaveSlot(long page, long slot, long check) { + if (quickSavePage >= 0 && page >= 0 && page <= 9990) quickSavePage = page - (page % 10); + if (slot >= 0 && slot < 10) quickSaveSlot = slot; + dontCheckSlot = check; +} + void ExtraSaveSlots_Init() { bool extraSaveSlots = (GetConfigInt("Misc", "ExtraSaveSlots", 0) != 0); if (extraSaveSlots) { @@ -486,19 +527,19 @@ void ExtraSaveSlots_Init() { dlogr(" Done", DL_INIT); } - autoQuickSave = GetConfigInt("Misc", "AutoQuickSave", 0); - if (autoQuickSave > 0) { + quickSavePageCount = GetConfigInt("Misc", "AutoQuickSave", 0); + if (quickSavePageCount > 0) { dlog("Applying auto quick save patch.", DL_INIT); - if (autoQuickSave > 10) autoQuickSave = 10; - autoQuickSave--; // reserved slot count + if (quickSavePageCount > 10) quickSavePageCount = 10; - quickSavePage = GetConfigInt("Misc", "AutoQuickSavePage", 0); + quickSavePage = GetConfigInt("Misc", "AutoQuickSavePage", 1); if (quickSavePage > 1000) quickSavePage = 1000; if (extraSaveSlots && quickSavePage > 0) { quickSavePage = (quickSavePage - 1) * 10; + quickSavePageInit = quickSavePage; MakeCall(0x47B923, SaveGame_hack1, 1); - } else { + } else { // for quickSavePage == 0 SafeWrite8(0x47B923, 0x89); SafeWrite32(0x47B924, 0x5193B83D); // mov [slot_cursor], edi = 0 } diff --git a/sfall/ExtraSaveSlots.h b/sfall/ExtraSaveSlots.h index ff1c07df..fd7b0208 100644 --- a/sfall/ExtraSaveSlots.h +++ b/sfall/ExtraSaveSlots.h @@ -18,7 +18,12 @@ #pragma once -extern DWORD LSPageOffset; - void ExtraSaveSlots_Init(); void ExtraSaveSlots_Exit(); + +long ExtraSaveSlots_GetSaveSlot(); +void ExtraSaveSlots_SetSaveSlot(long page, long slot); + +long ExtraSaveSlots_GetQuickSavePage(); +long ExtraSaveSlots_GetQuickSaveSlot(); +void ExtraSaveSlots_SetQuickSaveSlot(long page, long slot, long check); diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index 812de8b5..8cc38f02 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -1238,10 +1238,10 @@ capsMultiDrop: static void __declspec(naked) InvenActionExplosiveDropHack() { __asm { pushadc; - xor ecx, ecx; // no itemReplace - push 6; // event: item drop ground - call InventoryMoveHook_Script; // edx - item - cmp eax, -1; // ret value + xor ecx, ecx; // no itemReplace + push 6; // event: item drop ground + call InventoryMoveHook_Script; // edx - item + cmp eax, -1; // ret value popadc; jnz noDrop; mov dword ptr ds:[FO_VAR_dropped_explosive], ebp; // overwritten engine code (ebp = 1) @@ -1249,7 +1249,7 @@ static void __declspec(naked) InvenActionExplosiveDropHack() { retn; noDrop: add esp, 4; - jmp InvenActionObjDropRet; // no drop + jmp InvenActionObjDropRet; // no drop } } @@ -1942,8 +1942,8 @@ void HookScripts_Init() { HookCalls(SwitchHandHook, switchHandHkAddr); MakeJump(0x4713A9, UseArmorHack); // old 0x4713A3 MakeJump(0x476491, DropIntoContainerHack); - MakeJump(0x471338, DropIntoContainerHandSlotHack); - MakeJump(0x4712AB, DropIntoContainerHandSlotHack); + const DWORD dropIntoContHandHkAddr[] = {0x471338, 0x4712AB}; + MakeJumps(DropIntoContainerHandSlotHack, dropIntoContHandHkAddr); HookCall(0x471200, MoveInventoryHook); HookCall(0x476549, DropAmmoIntoWeaponHook); // old 0x476588 const DWORD actionCurObjDropHkAddr[] = { diff --git a/sfall/LoadGameHook.cpp b/sfall/LoadGameHook.cpp index a8847bce..8590a03b 100644 --- a/sfall/LoadGameHook.cpp +++ b/sfall/LoadGameHook.cpp @@ -139,7 +139,7 @@ static void __stdcall RunOnAfterGameStarted() { } void GetSavePath(char* buf, char* ftype) { - sprintf(buf, "%s\\savegame\\slot%.2d\\sfall%s.sav", *ptr_patches, *ptr_slot_cursor + 1 + LSPageOffset, ftype); //add SuperSave Page offset + sprintf(buf, "%s\\savegame\\slot%.2d\\sfall%s.sav", *ptr_patches, ExtraSaveSlots_GetSaveSlot() + 1, ftype); //add SuperSave Page offset } static void __stdcall SaveGame2() { diff --git a/sfall/MetaruleExtender.cpp b/sfall/MetaruleExtender.cpp index 13870f7e..c39a2e76 100644 --- a/sfall/MetaruleExtender.cpp +++ b/sfall/MetaruleExtender.cpp @@ -18,6 +18,7 @@ #include "main.h" #include "FalloutEngine.h" +#include "ExtraSaveSlots.h" #define HorriganEncounterDays (0x4C06EA) #define HorriganEncounterCheck (0x4C06D8) @@ -29,6 +30,13 @@ static bool HorriganEncounterDisabled = false; enum MetaruleFunction : long { SET_HORRIGAN_ENCOUNTER = 200, // sets the number of days for the Frank Horrigan encounter or disable encounter CLEAR_KEYBOARD_BUFFER = 201, // clears the keyboard input buffer, should be used in the HOOK_KEYPRESS hook to clear keyboard events in some cases + + // for save slots + GET_CURRENT_SAVE_SLOT = 1000, + SET_CURRENT_SAVE_SLOT = 1001, + GET_CURRENT_QSAVE_PAGE = 1002, + GET_CURRENT_QSAVE_SLOT = 1003, + SET_CURRENT_QSAVE_SLOT = 1004, }; /* @@ -55,6 +63,21 @@ static long __fastcall op_metarule3_ext(long metafunc, long* args) { case CLEAR_KEYBOARD_BUFFER: __asm call kb_clear_; break; + case GET_CURRENT_SAVE_SLOT: + result = ExtraSaveSlots_GetSaveSlot(); + break; + case SET_CURRENT_SAVE_SLOT: + ExtraSaveSlots_SetSaveSlot(args[0], args[1]); + break; + case GET_CURRENT_QSAVE_PAGE: + result = ExtraSaveSlots_GetQuickSavePage(); + break; + case GET_CURRENT_QSAVE_SLOT: + result = ExtraSaveSlots_GetQuickSaveSlot(); + break; + case SET_CURRENT_QSAVE_SLOT: + ExtraSaveSlots_SetQuickSaveSlot(args[0], args[1], args[2]); + break; default: fo_debug_printf("\nOPCODE ERROR: metarule3(%d, ...) - metarule function number does not exist.\n > Script: %s, procedure %s.", metafunc, (*ptr_currentProgram)->fileName, fo_findCurrentProc(*ptr_currentProgram));