Implement LoadGameHook (LoopFlag)

This commit is contained in:
Martin Janiczek 2022-10-18 15:44:03 +02:00
parent a330eb9d6a
commit 220c30a788
19 changed files with 286 additions and 25 deletions

View File

@ -144,6 +144,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/lips.h"
"src/loadsave.cc"
"src/loadsave.h"
"src/loop.cc"
"src/loop.h"
"src/main.cc"
"src/main.h"
"src/map_defs.h"

View File

@ -18,6 +18,7 @@
#include "input.h"
#include "item.h"
#include "kb.h"
#include "loop.h"
#include "map.h"
#include "memory.h"
#include "object.h"
@ -395,6 +396,8 @@ void automapShow(bool isInGame, bool isUsingScanner)
bool isoWasEnabled = isoDisable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
loopSetFlag(LoopFlag::AUTOMAP);
bool done = false;
while (!done) {
sharedFpsLimiter.mark();
@ -488,6 +491,8 @@ void automapShow(bool isInGame, bool isUsingScanner)
isoEnable();
}
loopClearFlag(LoopFlag::AUTOMAP);
windowDestroy(window);
fontSetCurrent(oldFont);
}

View File

@ -25,6 +25,7 @@
#include "interface.h"
#include "item.h"
#include "kb.h"
#include "loop.h"
#include "map.h"
#include "memory.h"
#include "message.h"
@ -787,8 +788,19 @@ struct CustomKarmaFolderDescription {
static std::vector<CustomKarmaFolderDescription> gCustomKarmaFolderDescriptions;
static std::vector<TownReputationEntry> gCustomTownReputationEntries;
int characterEditorShowInner(bool isCreationMode);
// Wrapper for editor_design_, setting LoopFlag::CHARSCREEN
// (see sfall: CharacterHook)
int characterEditorShow(bool isCreationMode) {
loopSetFlag(LoopFlag::CHARSCREEN);
int result = characterEditorShowInner(isCreationMode);
loopClearFlag(LoopFlag::CHARSCREEN);
return result;
}
// 0x431DF8
int characterEditorShow(bool isCreationMode)
int characterEditorShowInner(bool isCreationMode)
{
char* messageListItemText;
char line1[128];

View File

@ -23,6 +23,7 @@
#include "item.h"
#include "kb.h"
#include "loadsave.h"
#include "loop.h"
#include "map.h"
#include "memory.h"
#include "message.h"
@ -102,7 +103,8 @@ static int _compare_faster(const void* a1, const void* a2);
static void _combat_sequence_init(Object* a1, Object* a2);
static void _combat_sequence();
static void combatAttemptEnd();
static int _combat_input();
static int combatInput();
static int combatInputInner();
static void _combat_set_move_all();
static int _combat_turn(Object* a1, bool a2);
static bool _combat_should_end();
@ -3125,8 +3127,17 @@ void _combat_turn_run()
}
}
// Wrapper for combatInput, setting LoopFlag::COMBAT_PLAYER_TURN
// (see sfall: PlayerCombatHook)
static int combatInput() {
loopSetFlag(LoopFlag::COMBAT_PLAYER_TURN);
int result = combatInputInner();
loopClearFlag(LoopFlag::COMBAT_PLAYER_TURN);
return result;
}
// 0x4227F4
static int _combat_input()
static int combatInputInner()
{
while ((gCombatState & COMBAT_STATE_0x02) != 0) {
sharedFpsLimiter.mark();
@ -3271,7 +3282,7 @@ static int _combat_turn(Object* a1, bool a2)
_combat_outline_on();
}
if (_combat_input() == -1) {
if (combatInput() == -1) {
gameUiDisable(1);
gameMouseSetCursor(MOUSE_CURSOR_WAIT_WATCH);
a1->data.critter.combat.damageLastTurn = 0;
@ -3366,6 +3377,8 @@ static bool _combat_should_end()
// 0x422D2C
void _combat(STRUCT_664980* attack)
{
loopSetFlag(LoopFlag::COMBAT);
if (attack == NULL
|| (attack->attacker == NULL || attack->attacker->elevation == gElevation)
|| (attack->defender == NULL || attack->defender->elevation == gElevation)) {
@ -3449,6 +3462,8 @@ void _combat(STRUCT_664980* attack)
_game_user_wants_to_quit = 0;
}
}
loopClearFlag(LoopFlag::COMBAT);
}
// 0x422EC4

View File

@ -39,6 +39,8 @@
#include "item.h"
#include "kb.h"
#include "loadsave.h"
#include "loop.h"
#include "main.h"
#include "map.h"
#include "memory.h"
#include "mouse.h"
@ -352,6 +354,7 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
// 0x442B84
void gameReset()
{
mainSetIsGameLoaded(false);
tileDisable();
paletteReset();
randomReset();
@ -819,7 +822,9 @@ int gameHandleKey(int eventCode, bool isInCombatMode)
break;
case KEY_F1:
soundPlayFile("ib1p1xx1");
loopSetFlag(LoopFlag::HELP);
showHelp();
loopClearFlag(LoopFlag::HELP);
break;
case KEY_F2:
gameSoundSetMasterVolume(gameSoundGetMasterVolume() - 2047);

View File

@ -24,6 +24,7 @@
#include "item.h"
#include "kb.h"
#include "lips.h"
#include "loop.h"
#include "memory.h"
#include "mouse.h"
#include "object.h"
@ -920,6 +921,8 @@ int _gdialogInitFromScript(int headFid, int reaction)
backgroundSoundDelete();
}
loopSetFlag(LoopFlag::DIALOG);
_gdDialogWentOff = true;
return 0;
@ -947,6 +950,8 @@ int _gdialogExitFromScript()
_tile_scroll_to(gGameDialogOldCenterTile, 2);
}
loopClearFlag(LoopFlag::DIALOG);
_gdDestroyHeadWindow();
// CE: Fix Barter button.
@ -1437,6 +1442,8 @@ int gameDialogShowReview()
return -1;
}
loopSetFlag(LoopFlag::DIALOG_REVIEW);
// probably current top line or something like this, which is used to scroll
int v1 = 0;
gameDialogReviewWindowUpdate(win, v1);
@ -1474,6 +1481,8 @@ int gameDialogShowReview()
sharedFpsLimiter.throttle();
}
loopClearFlag(LoopFlag::DIALOG_REVIEW);
if (gameDialogReviewWindowFree(&win) == -1) {
return -1;
}

View File

@ -8,6 +8,8 @@
#include "dinput.h"
#include "draw.h"
#include "kb.h"
#include "loop.h"
#include "main.h"
#include "memory.h"
#include "mouse.h"
#include "sfall_config.h"
@ -634,7 +636,7 @@ unsigned int getTicks()
return SDL_GetTicks();
}
// Inspired by sfall SpeedPatch.cpp.
// sfall SpeedPatch.cpp
// Returns the potentially sped up (multiplied) tick count.
unsigned int getMultipliedTicks()
{
@ -651,10 +653,15 @@ unsigned int getMultipliedTicks()
gLastTickCount = newTickCount;
// Multiply the tick count difference by the multiplier
/* TODO janiczek: Original condition was:
if (IsGameLoaded() && enabled && (!(mode = GetLoopFlags()) || mode == LoopFlag::COMBAT || mode == (LoopFlag::COMBAT | LoopFlag::PCOMBAT) || (mode & LoopFlag::WORLDMAP)) && !slideShow)
*/
if (gSpeedPatchEnabled) {
if (mainIsGameLoaded()
&& gSpeedPatchEnabled
&& !mainIsInEndgameSlideshow()
&& (loopIsInWorldMap()
|| loopIsInCombatEnemyTurn()
|| loopIsInCombatWaitingForPlayerAction()
|| loopIsOutsideCombatWaitingForPlayerAction()
)
) {
elapsed *= gSpeedMulti;
elapsed += gTickCountFraction;
gTickCountFraction = modff(gTickCountFraction, &gTickCountFraction);
@ -706,10 +713,7 @@ void inputBlockForTocks(unsigned int ms)
// 0x4C93E0
unsigned int getTicksSince(unsigned int start)
{
// TODO janiczek: this one was supposed to be patched, but the game seems to work better without that.
// We can retry after implementing the big condition, it will likely fix
// all the issues, eg. with inventory animation spinning too fast etc.
unsigned int end = getTicks();
unsigned int end = getMultipliedTicks();
// NOTE: Uninline.
return getTicksBetween(end, start);

View File

@ -27,6 +27,7 @@
#include "item.h"
#include "kb.h"
#include "light.h"
#include "loop.h"
#include "map.h"
#include "message.h"
#include "mouse.h"
@ -585,6 +586,9 @@ void inventoryOpen()
reg_anim_clear(_inven_dude);
inventoryRenderSummary();
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_NORMAL);
loopSetFlag(LoopFlag::INVENTORY);
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
for (;;) {
@ -706,6 +710,8 @@ void inventoryOpen()
}
}
loopClearFlag(LoopFlag::INVENTORY);
_exit_inventory(isoWasEnabled);
// NOTE: Uninline.
@ -2624,6 +2630,9 @@ void inventoryOpenUseItemOn(Object* a1)
bool isoWasEnabled = _setup_inventory(INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
loopSetFlag(LoopFlag::USE_INTERFACE);
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
for (;;) {
sharedFpsLimiter.mark();
@ -2743,6 +2752,8 @@ void inventoryOpenUseItemOn(Object* a1)
sharedFpsLimiter.throttle();
}
loopClearFlag(LoopFlag::USE_INTERFACE);
_exit_inventory(isoWasEnabled);
// NOTE: Uninline.
@ -4218,6 +4229,9 @@ int inventoryOpenLooting(Object* a1, Object* a2)
_display_target_inventory(_target_stack_offset[_target_curr_stack], -1, _target_pud, INVENTORY_WINDOW_TYPE_LOOT);
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_LOOT);
_display_body(a2->fid, INVENTORY_WINDOW_TYPE_LOOT);
loopSetFlag(LoopFlag::LOOT_INTERFACE);
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
bool isCaughtStealing = false;
@ -4466,6 +4480,8 @@ int inventoryOpenLooting(Object* a1, Object* a2)
}
}
loopClearFlag(LoopFlag::LOOT_INTERFACE);
_exit_inventory(isoWasEnabled);
// NOTE: Uninline.
@ -5024,6 +5040,8 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5)
return;
}
loopSetFlag(LoopFlag::BARTER);
Object* armor = critterGetArmor(a2);
if (armor != NULL) {
itemRemove(a2, armor, 1);
@ -5338,6 +5356,8 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5)
itemAdd(a2, item1, 1);
}
loopClearFlag(LoopFlag::BARTER);
_exit_inventory(isoWasEnabled);
// NOTE: Uninline.
@ -5725,6 +5745,8 @@ static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int ma
// 0x476AB8
static int inventoryQuantityWindowInit(int inventoryWindowType, Object* item)
{
loopSetFlag(LoopFlag::COUNTER_WINDOW);
const int oldFont = fontGetCurrent();
fontSetCurrent(103);
@ -5945,6 +5967,8 @@ static int inventoryQuantityWindowFree(int inventoryWindowType)
_moveFrmImages[index].unlock();
}
loopClearFlag(LoopFlag::COUNTER_WINDOW);
windowDestroy(_mt_wid);
return 0;

View File

@ -30,6 +30,8 @@
#include "interface.h"
#include "item.h"
#include "kb.h"
#include "loop.h"
#include "main.h"
#include "map.h"
#include "memory.h"
#include "message.h"
@ -149,6 +151,8 @@ static int _QuickSnapShot();
static int lsgWindowInit(int windowType);
static int lsgWindowFree(int windowType);
static int lsgPerformSaveGame();
int lsgSaveGameInner(int mode);
static int lsgLoadGameInner(int slot);
static int lsgLoadGameInSlot(int slot);
static int lsgSaveHeaderInSlot(int slot);
static int lsgLoadHeaderInSlot(int slot);
@ -326,6 +330,7 @@ static int gLoadSaveWindowOldFont;
static FrmImage _loadsaveFrmImages[LOAD_SAVE_FRM_COUNT];
// 0x47B7E4
void _InitLoadSave()
{
@ -346,9 +351,19 @@ void _ResetLoadSave()
_MapDirErase(PROTO_DIR_NAME "\\" ITEMS_DIR_NAME "\\", PROTO_FILE_EXT);
}
// Wrapper for SaveGame, setting LoopFlag::SAVEGAME
// (see sfall: SaveGame_hook)
int lsgSaveGame(int mode)
{
loopSetFlag(LoopFlag::SAVEGAME);
int result = lsgSaveGameInner(mode);
loopClearFlag(LoopFlag::SAVEGAME);
return result;
}
// SaveGame
// 0x47B88C
int lsgSaveGame(int mode)
int lsgSaveGameInner(int mode)
{
MessageListItem messageListItem;
@ -850,9 +865,23 @@ static int _QuickSnapShot()
return 1;
}
// Wrapper for LoadGame, setting the main::isGameLoaded flag if successful
// (see sfall: LoadGame_hook and LoadGame_After)
int lsgLoadGame(int mode) {
loopSetFlag(LoopFlag::LOADGAME);
int result = lsgLoadGameInner(mode);
loopClearFlag(LoopFlag::LOADGAME);
if (result == 1) {
mainSetIsGameLoaded(true);
}
return result;
}
// LoadGame
// 0x47C640
int lsgLoadGame(int mode)
int lsgLoadGameInner(int mode)
{
MessageListItem messageListItem;

45
src/loop.cc Normal file
View File

@ -0,0 +1,45 @@
#include "loop.h"
namespace fallout {
static uint32_t loopFlags = 0;
// low-level API
uint32_t loopCurrentFlags() {
return loopFlags;
}
bool loopGetFlag(LoopFlag flag) {
return loopFlags & flag;
}
void loopSetFlag(LoopFlag flag) {
loopFlags |= flag;
}
void loopClearFlag(LoopFlag flag) {
loopFlags &= ~flag;
}
// high-level helpers
bool loopIsInWorldMap() {
return loopFlags & LoopFlag::WORLDMAP;
}
bool loopIsInCombatEnemyTurn() {
return loopFlags == LoopFlag::COMBAT;
}
// with no menus open
bool loopIsInCombatWaitingForPlayerAction() {
return loopFlags == (LoopFlag::COMBAT | LoopFlag::COMBAT_PLAYER_TURN);
}
// with no menus open
bool loopIsOutsideCombatWaitingForPlayerAction() {
return !loopFlags;
}
} // namespace fallout

48
src/loop.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef FALLOUT_LOOP_H_
#define FALLOUT_LOOP_H_
#include <cstdint>
namespace fallout {
enum LoopFlag : unsigned long {
WORLDMAP = 1 << 0, // 0x1
// RESERVED = 1 << 1, // 0x2 (unused)
DIALOG = 1 << 2, // 0x4
ESCMENU = 1 << 3, // 0x8
SAVEGAME = 1 << 4, // 0x10
LOADGAME = 1 << 5, // 0x20
COMBAT = 1 << 6, // 0x40
OPTIONS = 1 << 7, // 0x80
HELP = 1 << 8, // 0x100
CHARSCREEN = 1 << 9, // 0x200
PIPBOY = 1 << 10, // 0x400
COMBAT_PLAYER_TURN = 1 << 11, // 0x800
INVENTORY = 1 << 12, // 0x1000
AUTOMAP = 1 << 13, // 0x2000
SKILLDEX = 1 << 14, // 0x4000
USE_INTERFACE = 1 << 15, // 0x8000
LOOT_INTERFACE = 1 << 16, // 0x10000
BARTER = 1 << 17, // 0x20000
// HERO_WINDOW = 1 << 18, // 0x40000 Hero Appearance mod
DIALOG_REVIEW = 1 << 19, // 0x80000
COUNTER_WINDOW = 1 << 20, // 0x100000 Counter window for moving multiple items or setting a timer
// SPECIAL = 1UL << 31 // 0x80000000 Additional special flag for all modes
};
// low-level API
uint32_t loopCurrentFlags();
bool loopGetFlag(LoopFlag flag);
void loopSetFlag(LoopFlag flag);
void loopClearFlag(LoopFlag flag);
// high-level predicates
bool loopIsInWorldMap();
bool loopIsInCombatEnemyTurn();
bool loopIsInCombatWaitingForPlayerAction(); // with no menus open
bool loopIsOutsideCombatWaitingForPlayerAction(); // with no menus open
} // namespace fallout
#endif /* FALLOUT_LOOP_H_ */

View File

@ -172,6 +172,9 @@ static FrmImage _mainMenuBackgroundFrmImage;
static FrmImage _mainMenuButtonNormalFrmImage;
static FrmImage _mainMenuButtonPressedFrmImage;
static bool gIsGameLoaded = false;
static bool gIsInEndgameSlideshow = false;
// 0x48099C
int falloutMain(int argc, char** argv)
{
@ -228,6 +231,8 @@ int falloutMain(int argc, char** argv)
_main_load_new(mapNameCopy);
free(mapNameCopy);
gIsGameLoaded = true;
mainLoop();
paletteFadeTo(gPaletteWhite);
@ -1021,7 +1026,7 @@ static int mainMenuWindowHandleEvents()
mouseShowCursor();
}
unsigned int tick = getTicks();
unsigned int tick = getMultipliedTicks();
int rc = -1;
while (rc == -1) {
@ -1109,4 +1114,21 @@ static void main_menu_play_sound(const char* fileName)
soundPlayFile(fileName);
}
// True if game was started, false when on the main menu
bool mainIsGameLoaded() {
return gIsGameLoaded;
}
void mainSetIsGameLoaded(bool isGameLoaded) {
gIsGameLoaded = isGameLoaded;
}
bool mainIsInEndgameSlideshow() {
return gIsInEndgameSlideshow;
}
void mainSetIsInEndgameSlideshow(bool isInEndgameSlideshow) {
gIsInEndgameSlideshow = isInEndgameSlideshow;
}
} // namespace fallout

View File

@ -5,6 +5,13 @@ namespace fallout {
int falloutMain(int argc, char** argv);
// True if game was started, false when on the main menu
bool mainIsGameLoaded();
void mainSetIsGameLoaded(bool isGameLoaded);
bool mainIsInEndgameSlideshow();
void mainSetIsInEndgameSlideshow(bool isInEndgameSlideshow);
} // namespace fallout
#endif /* MAIN_H */

View File

@ -21,6 +21,7 @@
#include "input.h"
#include "kb.h"
#include "loadsave.h"
#include "loop.h"
#include "memory.h"
#include "message.h"
#include "mouse.h"
@ -455,14 +456,26 @@ int gPreferencesCombatLooks1;
static FrmImage _optionsFrmImages[OPTIONS_WINDOW_FRM_COUNT];
static FrmImage _preferencesFrmImages[PREFERENCES_WINDOW_FRM_COUNT];
int showOptionsWithInitialKeyCodeInner(int initialKeyCode);
// _do_options
// 0x48FC48
int showOptions()
{
return showOptionsWithInitialKeyCode(-1);
}
// Wrapper for do_optionsFunc_, setting LoopFlag::ESCMENU
// (see sfall: EscMenuHook)
int showOptionsWithInitialKeyCode(int initialKeyCode) {
loopSetFlag(LoopFlag::ESCMENU);
int result = showOptionsWithInitialKeyCodeInner(initialKeyCode);
loopClearFlag(LoopFlag::ESCMENU);
return result;
}
// 0x48FC50
int showOptionsWithInitialKeyCode(int initialKeyCode)
int showOptionsWithInitialKeyCodeInner(int initialKeyCode)
{
if (optionsWindowInit() == -1) {
debugPrint("\nOPTION MENU: Error loading option dialog data!\n");
@ -527,7 +540,9 @@ int showOptionsWithInitialKeyCode(int initialKeyCode)
}
if (showPreferences) {
loopSetFlag(LoopFlag::OPTIONS);
_do_prefscreen();
loopClearFlag(LoopFlag::OPTIONS);
} else {
switch (keyCode) {
case KEY_F12:
@ -2063,10 +2078,4 @@ static void _DoThing(int eventCode)
_changed = true;
}
// 0x48FC48
int _do_options()
{
return showOptionsWithInitialKeyCode(-1);
}
} // namespace fallout

View File

@ -22,6 +22,7 @@
#include "input.h"
#include "interface.h"
#include "kb.h"
#include "loop.h"
#include "map.h"
#include "memory.h"
#include "message.h"
@ -541,6 +542,8 @@ static int pipboyWindowInit(int intent)
return -1;
}
loopSetFlag(LoopFlag::PIPBOY);
int pipboyWindowX = (screenGetWidth() - PIPBOY_WINDOW_WIDTH) / 2;
int pipboyWindowY = (screenGetHeight() - PIPBOY_WINDOW_HEIGHT) / 2;
gPipboyWindow = windowCreate(pipboyWindowX, pipboyWindowY, PIPBOY_WINDOW_WIDTH, PIPBOY_WINDOW_HEIGHT, _colorTable[0], WINDOW_FLAG_0x10);
@ -706,6 +709,8 @@ static void pipboyWindowFree()
scriptsExecMapUpdateProc();
loopClearFlag(LoopFlag::PIPBOY);
windowDestroy(gPipboyWindow);
messageListFree(&gPipboyMessageList);

View File

@ -21,6 +21,7 @@
#include "game_mouse.h"
#include "game_movie.h"
#include "input.h"
#include "main.h"
#include "memory.h"
#include "message.h"
#include "object.h"
@ -1006,7 +1007,9 @@ int scriptsHandleRequests()
if ((gScriptsRequests & SCRIPT_REQUEST_ENDGAME) != 0) {
gScriptsRequests &= ~SCRIPT_REQUEST_ENDGAME;
mainSetIsInEndgameSlideshow(true);
endgamePlaySlideshow();
mainSetIsInEndgameSlideshow(false);
endgamePlayMovie();
}

View File

@ -15,6 +15,7 @@
#include "input.h"
#include "interface.h"
#include "kb.h"
#include "loop.h"
#include "map.h"
#include "memory.h"
#include "message.h"
@ -105,9 +106,20 @@ static int gSkilldexWindowOldFont;
static FrmImage _skilldexFrmImages[SKILLDEX_FRM_COUNT];
static int skilldexOpenInner();
// Wrapper for skilldex_select, setting LoopFlag::SKILLDEX
// (see sfall: SkilldexHook)
int skilldexOpen() {
loopSetFlag(LoopFlag::SKILLDEX);
int rc = skilldexOpenInner();
loopClearFlag(LoopFlag::SKILLDEX);
return rc;
}
// skilldex_select
// 0x4ABFD0
int skilldexOpen()
int skilldexOpenInner()
{
if (skilldexWindowInit() == -1) {
debugPrint("\n ** Error loading skilldex dialog data! **\n");

View File

@ -413,7 +413,7 @@ int windowCreate(int x, int y, int width, int height, int a4, int flags)
return index;
}
// win_remove
// win_remove, win_delete
// 0x4D6468
void windowDestroy(int win)
{

View File

@ -27,6 +27,7 @@
#include "interface.h"
#include "item.h"
#include "kb.h"
#include "loop.h"
#include "memory.h"
#include "mouse.h"
#include "object.h"
@ -2941,6 +2942,8 @@ static int wmWorldMapFunc(int a1)
return -1;
}
loopSetFlag(LoopFlag::WORLDMAP);
wmMatchWorldPosToArea(wmGenData.worldPosX, wmGenData.worldPosY, &(wmGenData.currentAreaId));
unsigned int v24 = 0;
@ -4695,6 +4698,8 @@ static int wmInterfaceExit()
int i;
TileInfo* tile;
loopClearFlag(LoopFlag::WORLDMAP);
tickersRemove(wmMouseBkProc);
_backgroundFrmImage.unlock();