diff --git a/SFALL_COMPATIBILITY.md b/SFALL_COMPATIBILITY.md
index 0036202a..cdc6a17c 100644
--- a/SFALL_COMPATIBILITY.md
+++ b/SFALL_COMPATIBILITY.md
@@ -75,10 +75,10 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Global scripts / Global script functions | set_global_script_repeat
set_global_script_type
available_global_script_types | ✅ except available_global_script_types | - |
| Combat | attack_is_aimed
block_combat
force_aimed_shots
disable_aimed_shots
get_attack_type
get/set_bodypart_hit_modifier
combat_data
get/set/reset_critical_table
get_last_target
get_last_attacker
set_critter_burst_disable
get/set_critter_current_ap
set_spray_settings
get/set_combat_free_move | ✅ except block_combat, force_aimed_shots, disable_aimed_shots, get_last_target, get_last_attacker, set_spray_settings | - |
| Car | set_car_current_town
car_gas_amount
set_car_intface_art | implemented: all except set_car_intface_art | - |
-| Interface / Windows and images | art_frame_data
interface_art_draw
interface_print
draw_image
draw_image_scaled
get_window_under_mouse
create_win
get_window_attribute
message_box
set_window_flag
win_fill_color
interface_overlay
dialog_message
get_text_width
hide_window
show_window
create_message_window | implemented: only message_box, get_text_width, show_window, create_message_window | - |
+| Interface / Windows and images | art_frame_data
interface_art_draw
interface_print
draw_image
draw_image_scaled
get_window_under_mouse
create_win
get_window_attribute
message_box
set_window_flag
win_fill_color
interface_overlay
dialog_message
get_text_width
hide_window
show_window
create_message_window | implemented: get_window_under_mouse, create_win, get_window_attribute, message_box, set_window_flag, dialog_message, get_text_width, hide_window, show_window, create_message_window | - |
| Interface / Outline | outlined_object
get_outline
set_outline | ✅ | - |
| Interface / Main interface | intface_is_hidden
intface_redraw
intface_hide
intface_show
set_quest_failure_value | implemented: only intface_redraw | `intface_redraw` only supports the zero-argument form; the optional-argument path is explicitly unimplemented. |
-| Interface / Inventory | display_stats
inventory_redraw
critter_inven_obj2
get_current_inven_size
item_weight | implemented: critter_inven_obj2, item_weight | - |
+| Interface / Inventory | display_stats
inventory_redraw
critter_inven_obj2
get_current_inven_size
item_weight | ✅ except get_current_inven_size | - |
| Interface / Cursor | get/set_cursor_mode | ✅ | - |
| Locks | lock_is_jammed
unjam_lock
set_unjam_locks_time | not implemented | - |
| INI settings | get_ini_setting
get_ini_string
get_ini_section
get_ini_sections
get_ini_config
get_ini_config_db
set_ini_setting | ✅ | `modified_ini` is intentionally omitted as deprecated. |
diff --git a/src/automap.cc b/src/automap.cc
index eb6ccbfc..715ccec9 100644
--- a/src/automap.cc
+++ b/src/automap.cc
@@ -46,6 +46,8 @@ static void _decode_map_data(int elevation);
static int automapCreate();
static int _copy_file_data(File* stream1, File* stream2, int length);
+static int gAutomapWindow = -1;
+
typedef enum AutomapFrm {
AUTOMAP_FRM_BACKGROUND,
AUTOMAP_FRM_BUTTON_UP,
@@ -326,6 +328,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
// adding WINDOW_TRANSPARENT and WINDOW_DRAGGABLE_BY_BACKGROUND for testing temporarily
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP | WINDOW_TRANSPARENT | WINDOW_DRAGGABLE_BY_BACKGROUND);
+ gAutomapWindow = window;
int scannerBtn = buttonCreate(window,
111,
@@ -493,10 +496,16 @@ void automapShow(bool isInGame, bool isUsingScanner)
}
windowDestroy(window);
+ gAutomapWindow = -1;
fontSetCurrent(oldFont);
touch_set_touchscreen_mode(false);
}
+int automapGetWindow()
+{
+ return windowGetWindow(gAutomapWindow) != nullptr ? gAutomapWindow : -1;
+}
+
// Renders automap in Map window.
//
// 0x41BD1C draw_top_down_map
diff --git a/src/automap.h b/src/automap.h
index 6fe55064..8616ede3 100644
--- a/src/automap.h
+++ b/src/automap.h
@@ -55,6 +55,7 @@ void automapShow(bool isInGame, bool isUsingScanner);
int automapRenderInPipboyWindow(int win, int map, int elevation);
int automapSaveCurrent();
int automapGetHeader(AutomapHeader** automapHeaderPtr);
+int automapGetWindow();
void automapSetDisplayMap(int map, bool available);
diff --git a/src/character_editor.cc b/src/character_editor.cc
index a58e8b6a..063564a5 100644
--- a/src/character_editor.cc
+++ b/src/character_editor.cc
@@ -663,7 +663,7 @@ static int gCharacterEditorPrimaryStatMinusBtns[7];
static unsigned char* gCharacterEditorWindowBuffer;
// 0x57060C edit_win
-static int gCharacterEditorWindow;
+static int gCharacterEditorWindow = -1;
// + stats buttons
//
@@ -5680,6 +5680,29 @@ void characterEditorReset()
gCharacterEditorLastLevel = 1;
}
+int characterEditorGetWindow()
+{
+ return windowGetWindow(gCharacterEditorWindow) != nullptr ? gCharacterEditorWindow : -1;
+}
+
+void characterEditorDisplayStats()
+{
+ if (windowGetWindow(gCharacterEditorWindow) == nullptr) {
+ return;
+ }
+
+ critterUpdateDerivedStats(gDude);
+ critterAdjustHitPoints(gDude, 0);
+
+ characterEditorDrawSkills(0);
+ characterEditorDrawPrimaryStat(RENDER_ALL_STATS, 0, 0);
+ characterEditorDrawPcStats();
+ characterEditorDrawDerivedStats();
+ characterEditorDrawFolders();
+ characterEditorDrawCard();
+ windowRefresh(gCharacterEditorWindow);
+}
+
// level up if needed
//
// 0x43C228 UpdateLevel
diff --git a/src/character_editor.h b/src/character_editor.h
index af69e848..a1e28097 100644
--- a/src/character_editor.h
+++ b/src/character_editor.h
@@ -14,6 +14,8 @@ char* _strmfe(char* dest, const char* name, const char* ext);
int characterEditorSave(File* stream);
int characterEditorLoad(File* stream);
void characterEditorReset();
+int characterEditorGetWindow();
+void characterEditorDisplayStats();
} // namespace fallout
diff --git a/src/game_dialog.cc b/src/game_dialog.cc
index c6864a5a..715f6056 100644
--- a/src/game_dialog.cc
+++ b/src/game_dialog.cc
@@ -3495,6 +3495,11 @@ bool gameDialogIsBarterWindowExpanded()
return gBarterWindowExpanded;
}
+int gameDialogGetWindow()
+{
+ return windowGetWindow(gGameDialogWindow) != nullptr ? gGameDialogWindow : -1;
+}
+
// 0x448660 gdialog_barter_cleanup_tables
void gameDialogBarterCleanupTables()
{
diff --git a/src/game_dialog.h b/src/game_dialog.h
index 10a616db..e35a289f 100644
--- a/src/game_dialog.h
+++ b/src/game_dialog.h
@@ -40,6 +40,7 @@ void gameDialogSetBarterModifier(int modifier);
int gameDialogBarter(int modifier);
void gameDialogEndBarter();
bool gameDialogIsBarterWindowExpanded();
+int gameDialogGetWindow();
} // namespace fallout
diff --git a/src/inventory.cc b/src/inventory.cc
index 68b2b2ff..648a2e35 100644
--- a/src/inventory.cc
+++ b/src/inventory.cc
@@ -352,6 +352,7 @@ static void inventoryNormalLayoutUpdate();
static bool inventoryBackgroundLoad(FrmImage& image, int col1FrmId, const char* col2Name, int columns);
static int inventoryChooseColumns(FrmImage& image, int expandedWidth, int col1FrmId, const char* col2Name);
static void inventoryCreateSlotButtons(int baseKeyCode, int scrollerX, int scrollerY, int columns);
+static bool inventoryGetCurrentWindowType(InventoryWindowType* inventoryWindowTypePtr);
static int inventoryGetWindowWidth(int inventoryWindowType);
static int inventoryGetWindowHeight(int inventoryWindowType);
static void inventoryNormalClampStackOffset();
@@ -594,7 +595,7 @@ static int gInventoryWindowDudeFid;
static Inventory* _pud;
// 0x59E964 i_wid
-static int gInventoryWindow;
+static int gInventoryWindow = -1;
// item2
// 0x59E968 i_rhand
@@ -695,6 +696,31 @@ static void inventoryCreateSlotButtons(int baseKeyCode, int scrollerX, int scrol
}
}
+static bool inventoryGetCurrentWindowType(InventoryWindowType* inventoryWindowTypePtr)
+{
+ if (GameMode::isInGameMode(GameMode::kBarter)) {
+ *inventoryWindowTypePtr = INVENTORY_WINDOW_TYPE_TRADE;
+ return true;
+ }
+
+ if (GameMode::isInGameMode(GameMode::kLoot)) {
+ *inventoryWindowTypePtr = INVENTORY_WINDOW_TYPE_LOOT;
+ return true;
+ }
+
+ if (GameMode::isInGameMode(GameMode::kUseOn)) {
+ *inventoryWindowTypePtr = INVENTORY_WINDOW_TYPE_USE_ITEM_ON;
+ return true;
+ }
+
+ if (GameMode::isInGameMode(GameMode::kInventory)) {
+ *inventoryWindowTypePtr = INVENTORY_WINDOW_TYPE_NORMAL;
+ return true;
+ }
+
+ return false;
+}
+
static int inventoryGetWindowWidth(int inventoryWindowType)
{
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) return inventoryLayout.windowWidth;
@@ -957,8 +983,6 @@ void inventoryOpen()
}
}
- ScopedGameMode gm(GameMode::kInventory);
-
if (inventoryCommonInit() == -1) {
return;
}
@@ -997,6 +1021,7 @@ void inventoryOpen()
inventoryRenderSummary();
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_NORMAL);
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
+ ScopedGameMode gm(GameMode::kInventory);
for (;;) {
sharedFpsLimiter.mark();
@@ -2743,8 +2768,6 @@ static void _adjust_fid()
// 0x4717E4
void inventoryOpenUseItemOn(Object* targetObj)
{
- ScopedGameMode gm(GameMode::kUseOn);
-
if (inventoryCommonInit() == -1) {
return;
}
@@ -2752,6 +2775,7 @@ void inventoryOpenUseItemOn(Object* targetObj)
bool isoWasEnabled = _setup_inventory(INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
+ ScopedGameMode gm(GameMode::kUseOn);
for (;;) {
sharedFpsLimiter.mark();
@@ -3088,7 +3112,7 @@ int objectGetCarriedQuantityByPid(Object* object, int pid)
// Renders character's summary of SPECIAL stats, equipped armor bonuses,
// and weapon's damage/range.
//
-// 0x471D5C
+// 0x471D5C display_stats
static void inventoryRenderSummary()
{
int summaryStats[7];
@@ -4350,8 +4374,6 @@ int inventoryOpenLooting(Object* looter, Object* target)
return 0;
}
- ScopedGameMode gm(GameMode::kLoot);
-
if (FID_TYPE(target->fid) == OBJ_TYPE_CRITTER) {
if (critterFlagCheck(target->pid, CRITTER_NO_STEAL)) {
// You can't find anything to take from that.
@@ -4417,6 +4439,7 @@ int inventoryOpenLooting(Object* looter, Object* target)
}
bool isoWasEnabled = _setup_inventory(INVENTORY_WINDOW_TYPE_LOOT);
+ ScopedGameMode gm(GameMode::kLoot);
Object** critters = nullptr;
int critterCount = 0;
@@ -6338,6 +6361,46 @@ int inventorySetTimer(Object* item)
return seconds;
}
+int inventoryGetWindow()
+{
+ return windowGetWindow(gInventoryWindow) != nullptr ? gInventoryWindow : -1;
+}
+
+void inventoryDisplayStats()
+{
+ if (windowGetWindow(gInventoryWindow) == nullptr) {
+ return;
+ }
+
+ inventoryRenderSummary();
+ windowRefresh(gInventoryWindow);
+}
+
+void inventoryRedraw(int redrawSide)
+{
+ if (windowGetWindow(gInventoryWindow) == nullptr) {
+ return;
+ }
+
+ InventoryWindowType inventoryWindowType;
+ if (!inventoryGetCurrentWindowType(&inventoryWindowType)) {
+ return;
+ }
+
+ if (redrawSide <= 0) {
+ _stack_offset[_curr_stack] = 0;
+ _display_inventory(0, -1, inventoryWindowType);
+ }
+
+ if (redrawSide != 0
+ && (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT
+ || inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE)) {
+ _target_stack_offset[_target_curr_stack] = 0;
+ _display_target_inventory(0, -1, _target_pud, inventoryWindowType);
+ windowRefresh(gInventoryWindow);
+ }
+}
+
Object* inventoryGetTargetObject()
{
return _target_stack[_target_curr_stack];
@@ -6350,10 +6413,8 @@ int inventoryUnwieldSlot(Object* critter, InvenSlot slot)
}
bool isDude = critter == gDude;
- bool uiModeActive = GameMode::isInGameMode(GameMode::kInventory)
- || GameMode::isInGameMode(GameMode::kUseOn)
- || GameMode::isInGameMode(GameMode::kLoot)
- || GameMode::isInGameMode(GameMode::kBarter);
+ InventoryWindowType inventoryWindowType;
+ bool uiModeActive = inventoryGetCurrentWindowType(&inventoryWindowType);
bool update = false;
if (slot != InvenSlot::Armor && !uiModeActive) {
@@ -6443,15 +6504,6 @@ int inventoryUnwieldSlot(Object* critter, InvenSlot slot)
return 0;
}
- InventoryWindowType inventoryWindowType = INVENTORY_WINDOW_TYPE_NORMAL;
- if (GameMode::isInGameMode(GameMode::kBarter)) {
- inventoryWindowType = INVENTORY_WINDOW_TYPE_TRADE;
- } else if (GameMode::isInGameMode(GameMode::kLoot)) {
- inventoryWindowType = INVENTORY_WINDOW_TYPE_LOOT;
- } else if (GameMode::isInGameMode(GameMode::kUseOn)) {
- inventoryWindowType = INVENTORY_WINDOW_TYPE_USE_ITEM_ON;
- }
-
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) {
inventoryRenderSummary();
}
diff --git a/src/inventory.h b/src/inventory.h
index a67a8826..c68022d4 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -62,6 +62,9 @@ int inventoryOpenLooting(Object* looter, Object* target);
int inventoryOpenStealing(Object* thief, Object* target);
void barterProcessUI(int win, Object* barterer, Object* playerTable, Object* bartererTable, int barterMod);
int inventorySetTimer(Object* item);
+int inventoryGetWindow();
+void inventoryDisplayStats();
+void inventoryRedraw(int redrawSide);
Object* inventoryGetTargetObject();
int inventoryUnwieldSlot(Object* critter, InvenSlot slot);
diff --git a/src/options.cc b/src/options.cc
index f44080d3..4e970a01 100644
--- a/src/options.cc
+++ b/src/options.cc
@@ -72,7 +72,9 @@ static unsigned char* _opbtns[OPTIONS_WINDOW_BUTTONS_COUNT];
static bool gOptionsWindowGameMouseObjectsWasVisible;
// 0x663900 optnwin
-static int gOptionsWindow;
+static int gOptionsWindow = -1;
+
+static int gPauseWindow = -1;
// 0x663908 winbuf
static unsigned char* gOptionsWindowBuffer;
@@ -303,6 +305,7 @@ static int optionsWindowInit()
static int optionsWindowFree()
{
windowDestroy(gOptionsWindow);
+ gOptionsWindow = -1;
fontSetCurrent(gOptionsWindowOldFont);
messageListFree(&gPreferencesMessageList);
@@ -389,6 +392,8 @@ int showPause(bool preserveWorldState)
return -1;
}
+ gPauseWindow = window;
+
unsigned char* windowBuffer = windowGetBuffer(window);
memcpy(windowBuffer,
frmImages[PAUSE_WINDOW_FRM_BACKGROUND].getData(),
@@ -477,6 +482,7 @@ int showPause(bool preserveWorldState)
}
windowDestroy(window);
+ gPauseWindow = -1;
messageListFree(&gPreferencesMessageList);
if (!preserveWorldState) {
@@ -529,4 +535,17 @@ int _init_options_menu()
return 0;
}
+int optionsGetWindow()
+{
+ if (windowGetWindow(gOptionsWindow) != nullptr) {
+ return gOptionsWindow;
+ }
+
+ if (windowGetWindow(gPauseWindow) != nullptr) {
+ return gPauseWindow;
+ }
+
+ return -1;
+}
+
} // namespace fallout
diff --git a/src/options.h b/src/options.h
index 5acb5828..e68548db 100644
--- a/src/options.h
+++ b/src/options.h
@@ -6,6 +6,7 @@ namespace fallout {
int showOptions();
int showPause(bool preserveWorldState);
int _init_options_menu();
+int optionsGetWindow();
} // namespace fallout
diff --git a/src/pipboy.cc b/src/pipboy.cc
index 6e258cf4..8ce195d5 100644
--- a/src/pipboy.cc
+++ b/src/pipboy.cc
@@ -354,7 +354,7 @@ int gPipboyPreviousMouseX;
int gPipboyPreviousMouseY;
// 0x6644C4 pip_win
-int gPipboyWindow;
+int gPipboyWindow = -1;
// 0x6644F4 holodisk
int _holodisk;
@@ -1014,6 +1014,11 @@ int pipboyLoad(File* stream)
return _save_pipboy(stream);
}
+int pipboyGetWindow()
+{
+ return windowGetWindow(gPipboyWindow) != nullptr ? gPipboyWindow : -1;
+}
+
// 0x497BD8
static void pipboyWindowHandleStatus(int userInput)
{
diff --git a/src/pipboy.h b/src/pipboy.h
index 4c6779d5..8ceeb33f 100644
--- a/src/pipboy.h
+++ b/src/pipboy.h
@@ -16,6 +16,7 @@ void pipboyInit();
void pipboyReset();
int pipboySave(File* stream);
int pipboyLoad(File* stream);
+int pipboyGetWindow();
extern MessageList gPipboyMessageList;
int pipboyMessageListInit();
diff --git a/src/sfall_metarules.cc b/src/sfall_metarules.cc
index 5d124ba0..2be88d6a 100644
--- a/src/sfall_metarules.cc
+++ b/src/sfall_metarules.cc
@@ -7,6 +7,8 @@
#include
#include "art.h"
+#include "automap.h"
+#include "character_editor.h"
#include "color.h"
#include "combat.h"
#include "config.h" // For Config, configInit, configFree
@@ -24,6 +26,8 @@
#include "message.h"
#include "object.h"
#include "opcode_context.h"
+#include "options.h"
+#include "pipboy.h"
#include "platform_compat.h"
#include "proto_instance.h"
#include "scripts.h"
@@ -32,9 +36,11 @@
#include "sfall_ini.h"
#include "sfall_opcodes.h"
#include "sfall_script_hooks.h"
+#include "skilldex.h"
#include "text_font.h"
#include "tile.h"
#include "window.h"
+#include "window_manager.h"
#include "worldmap.h"
#include
@@ -44,8 +50,11 @@ namespace fallout {
static void mf_attack_is_aimed(OpcodeContext& ctx);
static void mf_car_gas_amount(OpcodeContext& ctx);
static void mf_combat_data(OpcodeContext& ctx);
+static void mf_create_win(OpcodeContext& ctx);
static void mf_critter_inven_obj2(OpcodeContext& ctx);
static void mf_dialog_obj(OpcodeContext& ctx);
+static void mf_dialog_message(OpcodeContext& ctx);
+static void mf_display_stats(OpcodeContext& ctx);
static void mf_get_combat_free_move(OpcodeContext& ctx);
static void mf_get_cursor_mode(OpcodeContext& ctx);
static void mf_get_flags(OpcodeContext& ctx);
@@ -54,7 +63,10 @@ static void mf_get_object_data(OpcodeContext& ctx);
static void mf_get_outline(OpcodeContext& ctx);
static void mf_get_sfall_arg_at(OpcodeContext& ctx);
static void mf_get_text_width(OpcodeContext& ctx);
+static void mf_get_window_attribute(OpcodeContext& ctx);
+static void mf_hide_window(OpcodeContext& ctx);
static void mf_intface_redraw(OpcodeContext& ctx);
+static void mf_inventory_redraw(OpcodeContext& ctx);
static void mf_item_weight(OpcodeContext& ctx);
static void mf_loot_obj(OpcodeContext& ctx);
static void mf_message_box(OpcodeContext& ctx);
@@ -72,6 +84,7 @@ static void mf_set_cursor_mode(OpcodeContext& ctx);
static void mf_set_flags(OpcodeContext& ctx);
static void mf_set_iface_tag_text(OpcodeContext& ctx);
static void mf_set_outline(OpcodeContext& ctx);
+static void mf_set_window_flag(OpcodeContext& ctx);
static void mf_set_unique_id(OpcodeContext& ctx);
static void mf_show_window(OpcodeContext& ctx);
static void mf_signal_close_game(OpcodeContext& ctx);
@@ -93,15 +106,14 @@ const MetaruleInfo kMetarules[] = {
// {"add_g_timer_event", mf_add_g_timer_event, 2, 2, -1, {ARG_INT, ARG_INT}},
// {"add_trait", mf_add_trait, 1, 1, -1, {ARG_INT}},
{ "art_cache_clear", mf_art_cache_flush, 0, 0 },
- // {"art_frame_data", mf_art_frame_data, 1, 3, 0, {ARG_INTSTR, ARG_INT, ARG_INT}},
{ "attack_is_aimed", mf_attack_is_aimed, 0, 0 },
{ "car_gas_amount", mf_car_gas_amount, 0, 0 },
{ "combat_data", mf_combat_data, 0, 0 },
- // {"create_win", mf_create_win, 5, 6, -1, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
+ { "create_win", mf_create_win, 5, 6, -1, { ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT } },
{ "critter_inven_obj2", mf_critter_inven_obj2, 2, 2, 0, { ARG_OBJECT, ARG_INT } },
- // {"dialog_message", mf_dialog_message, 1, 1, -1, {ARG_STRING}},
+ { "dialog_message", mf_dialog_message, 1, 1, -1, { ARG_STRING } },
{ "dialog_obj", mf_dialog_obj, 0, 0 },
- // {"display_stats", mf_display_stats, 0, 0}, // refresh
+ { "display_stats", mf_display_stats, 0, 0 }, // refresh
// {"draw_image", mf_draw_image, 1, 5, -1, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
// {"draw_image_scaled", mf_draw_image_scaled, 1, 6, -1, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
// {"exec_map_update_scripts", mf_exec_map_update_scripts, 0, 0},
@@ -126,18 +138,17 @@ const MetaruleInfo kMetarules[] = {
// {"get_string_pointer", mf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, // note: deprecated; do not implement
// {"get_terrain_name", mf_get_terrain_name, 0, 2, -1, {ARG_INT, ARG_INT}},
{ "get_text_width", mf_get_text_width, 1, 1, 0, { ARG_STRING } },
- // {"get_window_attribute", mf_get_window_attribute, 1, 2, -1, {ARG_INT, ARG_INT}},
+ { "get_window_attribute", mf_get_window_attribute, 1, 2, -1, { ARG_INT, ARG_INT } },
// {"has_fake_perk_npc", mf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}},
// {"has_fake_trait_npc", mf_has_fake_trait_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}},
- // {"hide_window", mf_hide_window, 0, 1, -1, {ARG_STRING}},
- // {"interface_art_draw", mf_interface_art_draw, 4, 6, -1, {ARG_INT, ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
+ { "hide_window", mf_hide_window, 0, 1, -1, { ARG_STRING } },
// {"interface_overlay", mf_interface_overlay, 2, 6, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
// {"interface_print", mf_interface_print, 5, 6, -1, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
// {"intface_hide", mf_intface_hide, 0, 0},
// {"intface_is_hidden", mf_intface_is_hidden, 0, 0},
{ "intface_redraw", mf_intface_redraw, 0, 1 },
// {"intface_show", mf_intface_show, 0, 0},
- // {"inventory_redraw", mf_inventory_redraw, 0, 1, -1, {ARG_INT}},
+ { "inventory_redraw", mf_inventory_redraw, 0, 1, -1, { ARG_INT } },
// {"item_make_explosive", mf_item_make_explosive, 3, 4, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{ "item_weight", mf_item_weight, 1, 1, 0, { ARG_OBJECT } },
// {"lock_is_jammed", mf_lock_is_jammed, 1, 1, 0, {ARG_OBJECT}},
@@ -177,7 +188,7 @@ const MetaruleInfo kMetarules[] = {
// {"set_town_title", mf_set_town_title, 2, 2, -1, {ARG_INT, ARG_STRING}},
{ "set_unique_id", mf_set_unique_id, 1, 2, -1, { ARG_OBJECT, ARG_INT } },
// {"set_unjam_locks_time", mf_set_unjam_locks_time, 1, 1, -1, {ARG_INT}},
- // {"set_window_flag", mf_set_window_flag, 3, 3, -1, {ARG_INTSTR, ARG_INT, ARG_INT}},
+ { "set_window_flag", mf_set_window_flag, 3, 3, -1, { ARG_INTSTR, ARG_INT, ARG_INT } },
{ "show_window", mf_show_window, 0, 1, -1, { ARG_STRING } },
{ "signal_close_game", mf_signal_close_game, 0, 0 },
// {"spatial_radius", mf_spatial_radius, 1, 1, 0, {ARG_OBJECT}},
@@ -189,13 +200,102 @@ const MetaruleInfo kMetarules[] = {
{ "tile_refresh_display", mf_tile_refresh_display, 0, 0 },
// {"unjam_lock", mf_unjam_lock, 1, 1, -1, {ARG_OBJECT}},
{ "unwield_slot", mf_unwield_slot, 2, 2, -1, { ARG_OBJECT, ARG_INT } },
- // {"win_fill_color", mf_win_fill_color, 0, 5, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{ "opcode_exists", mf_opcode_exists, 1, 1 },
};
const std::size_t kMetarulesCount = sizeof(kMetarules) / sizeof(kMetarules[0]);
constexpr int kMetarulesMax = sizeof(kMetarules) / sizeof(kMetarules[0]);
+enum class InterfaceWindowLookupResult {
+ Found,
+ Missing,
+ Invalid,
+};
+
+/*
+// Valid window types for get_window_attribute
+#define WINTYPE_INVENTORY (0) // any inventory window (player/loot/use/barter)
+#define WINTYPE_DIALOG (1)
+#define WINTYPE_PIPBOY (2)
+#define WINTYPE_WORLDMAP (3)
+#define WINTYPE_IFACEBAR (4) // the interface bar
+#define WINTYPE_CHARACTER (5)
+#define WINTYPE_SKILLDEX (6)
+#define WINTYPE_ESCMENU (7) // escape menu
+#define WINTYPE_AUTOMAP (8)
+*/
+static InterfaceWindowLookupResult getInterfaceWindowByType(int winType, int& window)
+{
+ switch (winType) {
+ case 0:
+ window = inventoryGetWindow();
+ break;
+ case 1:
+ window = gameDialogGetWindow();
+ break;
+ case 2:
+ window = pipboyGetWindow();
+ break;
+ case 3:
+ window = worldmapGetWindow();
+ break;
+ case 4:
+ window = windowGetWindow(gInterfaceBarWindow) != nullptr ? gInterfaceBarWindow : -1;
+ break;
+ case 5:
+ window = characterEditorGetWindow();
+ break;
+ case 6:
+ window = skilldexGetWindow();
+ break;
+ case 7:
+ window = optionsGetWindow();
+ break;
+ case 8:
+ window = automapGetWindow();
+ break;
+ default:
+ window = -1;
+ return InterfaceWindowLookupResult::Invalid;
+ }
+
+ return window != -1 ? InterfaceWindowLookupResult::Found : InterfaceWindowLookupResult::Missing;
+}
+
+static int getCurrentInterfaceWindow()
+{
+ int window = -1;
+ if (GameMode::isInGameMode(GameMode::kInventory)
+ || GameMode::isInGameMode(GameMode::kUseOn)
+ || GameMode::isInGameMode(GameMode::kLoot)
+ || GameMode::isInGameMode(GameMode::kBarter)) {
+ window = inventoryGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kDialog)) {
+ window = gameDialogGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kPipboy)) {
+ window = pipboyGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kWorldmap)) {
+ window = worldmapGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kEditor)) {
+ window = characterEditorGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kSkilldex)) {
+ window = skilldexGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kOptions)) {
+ window = optionsGetWindow();
+ } else if (GameMode::isInGameMode(GameMode::kAutomap)) {
+ window = automapGetWindow();
+ } else if (windowGetWindow(gInterfaceBarWindow) != nullptr) {
+ window = gInterfaceBarWindow;
+ }
+
+ return window;
+}
+
+static bool applyWindowFlag(int windowId, int bitFlag, bool enabled)
+{
+ return scriptWindowSetFlag(windowId, bitFlag, enabled);
+}
+
void mf_art_cache_flush(OpcodeContext& ctx)
{
artCacheFlush();
@@ -236,6 +336,35 @@ void mf_combat_data(OpcodeContext& ctx)
}
}
+void mf_create_win(OpcodeContext& ctx)
+{
+ int flags = ctx.numArgs() > 5
+ ? ctx.arg(5).asInt()
+ : WINDOW_MOVE_ON_TOP;
+
+ int color = (flags & WINDOW_TRANSPARENT) != 0 ? 0 : 256;
+ if (scriptWindowCreate(ctx.stringArg(0),
+ ctx.arg(1).asInt(),
+ ctx.arg(2).asInt(),
+ ctx.arg(3).asInt(),
+ ctx.arg(4).asInt(),
+ color,
+ flags)
+ == -1) {
+ ctx.printError("%s() - couldn't create window.", ctx.name());
+ ctx.setReturn(-1);
+ }
+}
+
+void mf_display_stats(OpcodeContext& ctx)
+{
+ if (GameMode::isInGameMode(GameMode::kInventory)) {
+ inventoryDisplayStats();
+ } else if (GameMode::isInGameMode(GameMode::kEditor)) {
+ characterEditorDisplayStats();
+ }
+}
+
void mf_critter_inven_obj2(OpcodeContext& ctx)
{
Object* obj = ctx.arg(0).asObject();
@@ -268,6 +397,14 @@ void mf_dialog_obj(OpcodeContext& ctx)
}
}
+void mf_dialog_message(OpcodeContext& ctx)
+{
+ if (GameMode::isInGameMode(GameMode::kDialog)
+ && !GameMode::isInGameMode(GameMode::kDialogReview)) {
+ gameDialogRenderSupplementaryMessage(ctx.stringArg(0));
+ }
+}
+
void mf_get_combat_free_move(OpcodeContext& ctx)
{
ctx.setReturn(_combat_free_move);
@@ -331,6 +468,67 @@ void mf_get_text_width(OpcodeContext& ctx)
ctx.setReturn(fontGetStringWidth(ctx.stringArg(0)));
}
+void mf_get_window_attribute(OpcodeContext& ctx)
+{
+ int attrType = ctx.numArgs() > 1 ? ctx.arg(1).asInt() : 0;
+ int window = -1;
+ InterfaceWindowLookupResult lookup = getInterfaceWindowByType(ctx.arg(0).asInt(), window);
+ if (lookup == InterfaceWindowLookupResult::Missing) {
+ if (attrType != 0) {
+ ctx.printError("%s() - failed to get the interface window.", ctx.name());
+ ctx.setReturn(-1);
+ } else {
+ ctx.setReturn(0);
+ }
+ return;
+ }
+
+ if (lookup == InterfaceWindowLookupResult::Invalid) {
+ ctx.printError("%s() - invalid window type number.", ctx.name());
+ ctx.setReturn(-1);
+ return;
+ }
+
+ Rect rect;
+ if (windowGetRect(window, &rect) == -1) {
+ ctx.setReturn(-1);
+ return;
+ }
+
+ switch (attrType) {
+ case -1: {
+ ArrayId arrayId = CreateTempArray(-1, 0);
+ SetArray(arrayId, programMakeString(ctx.program(), "left"), ProgramValue(rect.left), false, ctx.program());
+ SetArray(arrayId, programMakeString(ctx.program(), "top"), ProgramValue(rect.top), false, ctx.program());
+ SetArray(arrayId, programMakeString(ctx.program(), "right"), ProgramValue(rect.right), false, ctx.program());
+ SetArray(arrayId, programMakeString(ctx.program(), "bottom"), ProgramValue(rect.bottom), false, ctx.program());
+ ctx.setReturn(ProgramValue(arrayId));
+ break;
+ }
+ case 0: // basically an existence check
+ ctx.setReturn(1);
+ break;
+ case 1:
+ ctx.setReturn(rect.left);
+ break;
+ case 2:
+ ctx.setReturn(rect.top);
+ break;
+ case 3:
+ ctx.setReturn(windowGetWidth(window));
+ break;
+ case 4:
+ ctx.setReturn(windowGetHeight(window));
+ break;
+ case 5:
+ ctx.setReturn(window);
+ break;
+ default:
+ ctx.setReturn(0);
+ break;
+ }
+}
+
void mf_intface_redraw(OpcodeContext& ctx)
{
if (ctx.numArgs() == 0) {
@@ -341,6 +539,11 @@ void mf_intface_redraw(OpcodeContext& ctx)
}
}
+void mf_inventory_redraw(OpcodeContext& ctx)
+{
+ inventoryRedraw(ctx.numArgs() > 0 ? ctx.arg(0).asInt() : -1);
+}
+
void mf_item_weight(OpcodeContext& ctx)
{
Object* object = ctx.arg(0).asObject();
@@ -355,7 +558,7 @@ void mf_item_weight(OpcodeContext& ctx)
void mf_loot_obj(OpcodeContext& ctx)
{
- if (GameMode::isInGameMode(GameMode::kInventory)) {
+ if (GameMode::isInGameMode(GameMode::kLoot)) {
ctx.setReturn(inventoryGetTargetObject());
} else {
ctx.setReturn(nullptr);
@@ -524,6 +727,41 @@ void mf_set_outline(OpcodeContext& ctx)
object->outline = outline;
}
+void mf_set_window_flag(OpcodeContext& ctx)
+{
+ int bitFlag = ctx.arg(1).asInt();
+ switch (bitFlag) {
+ case WINDOW_DONT_MOVE_TOP:
+ case WINDOW_MOVE_ON_TOP:
+ case WINDOW_HIDDEN:
+ case WINDOW_MODAL:
+ case WINDOW_TRANSPARENT:
+ break;
+ default:
+ return;
+ }
+
+ bool enabled = ctx.arg(2).asInt() != 0;
+ if (ctx.arg(0).isString()) {
+ const char* windowName = ctx.stringArg(0);
+ if (!scriptWindowSetNamedFlag(windowName, bitFlag, enabled)) {
+ ctx.printError("%s() - window '%s' is not found.", ctx.name(), windowName);
+ }
+ return;
+ }
+
+ int windowId = ctx.arg(0).asInt();
+ if (windowId <= 0) {
+ windowId = getCurrentInterfaceWindow();
+ }
+
+ if (windowId == -1) {
+ return;
+ }
+
+ applyWindowFlag(windowId, bitFlag, enabled);
+}
+
void mf_set_unique_id(OpcodeContext& ctx)
{
Object* object = ctx.arg(0).asObject();
@@ -552,6 +790,18 @@ void mf_show_window(OpcodeContext& ctx)
}
}
+void mf_hide_window(OpcodeContext& ctx)
+{
+ if (ctx.numArgs() == 0) {
+ scriptWindowHide();
+ } else {
+ const char* windowName = ctx.stringArg(0);
+ if (!scriptWindowHideNamed(windowName)) {
+ ctx.printError("%s() - window '%s' is not found.", ctx.name(), windowName);
+ }
+ }
+}
+
void mf_tile_refresh_display(OpcodeContext& ctx)
{
tileWindowRefresh();
diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc
index 4f6eaf99..3fe16f44 100644
--- a/src/sfall_opcodes.cc
+++ b/src/sfall_opcodes.cc
@@ -42,6 +42,7 @@
#include "stat.h"
#include "svga.h"
#include "tile.h"
+#include "window_manager.h"
#include "worldmap.h"
namespace fallout {
@@ -899,6 +900,11 @@ static void op_get_mouse_buttons(Program* program)
programStackPushInteger(program, mouse_get_last_buttons());
}
+static void op_get_window_under_mouse(Program* program)
+{
+ programStackPushInteger(program, _win_last_button_winID());
+}
+
// get_screen_width
static void op_get_screen_width(Program* program)
{
@@ -1873,6 +1879,7 @@ void sfallOpcodesInit()
// 0x821e - int get_mouse_buttons()
interpreterRegisterOpcode(0x821E, op_get_mouse_buttons);
// 0x821f - int get_window_under_mouse()
+ interpreterRegisterOpcode(0x821F, op_get_window_under_mouse);
// 0x8163 - int get_year()
interpreterRegisterOpcode(0x8163, op_get_year);
diff --git a/src/skilldex.cc b/src/skilldex.cc
index c552ce05..78494eeb 100644
--- a/src/skilldex.cc
+++ b/src/skilldex.cc
@@ -95,7 +95,7 @@ static MessageList gSkilldexMessageList;
static MessageListItem gSkilldexMessageListItem;
// 0x668140 skldxwin
-static int gSkilldexWindow;
+static int gSkilldexWindow = -1;
// 0x668144 winbuf_2
static unsigned char* gSkilldexWindowBuffer;
@@ -147,6 +147,11 @@ int skilldexOpen()
return rc;
}
+int skilldexGetWindow()
+{
+ return windowGetWindow(gSkilldexWindow) != nullptr ? gSkilldexWindow : -1;
+}
+
// 0x4AC054 skilldex_start
static int skilldexWindowInit()
{
diff --git a/src/skilldex.h b/src/skilldex.h
index 3c1b65c7..b0952310 100644
--- a/src/skilldex.h
+++ b/src/skilldex.h
@@ -17,6 +17,7 @@ typedef enum SkilldexRC {
} SkilldexRC;
int skilldexOpen();
+int skilldexGetWindow();
} // namespace fallout
diff --git a/src/window.cc b/src/window.cc
index ed5f183a..9f00045a 100644
--- a/src/window.cc
+++ b/src/window.cc
@@ -158,9 +158,6 @@ static ManagedWindow gManagedWindows[MANAGED_WINDOW_COUNT];
// 0x672D70 inputFunc
static WindowInputHandler** gWindowInputHandlers;
-// 0x672D74 createWindowFunc
-static ManagedWindowCreateCallback* off_672D74;
-
// NOTE: This value is never set.
//
// 0x672D78 selectWindowFunc
@@ -704,6 +701,50 @@ bool scriptWindowShow()
return true;
}
+bool scriptWindowHideNamed(const char* windowName)
+{
+ for (int index = 0; index < MANAGED_WINDOW_COUNT; index++) {
+ ManagedWindow* managedWindow = &(gManagedWindows[index]);
+ if (managedWindow->window != -1) {
+ if (compat_stricmp(managedWindow->name, windowName) == 0) {
+ windowHide(managedWindow->window);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool scriptWindowSetFlag(int windowId, int bitFlag, bool enabled)
+{
+ if (!windowIsValidWindowId(windowId) || windowId == 0) {
+ return false;
+ }
+
+ Window* window = windowGetWindow(windowId);
+ if (window == nullptr) {
+ return false;
+ }
+
+ if (bitFlag == WINDOW_HIDDEN) {
+ if (enabled) {
+ windowHide(windowId);
+ } else {
+ windowShow(windowId);
+ }
+ return true;
+ }
+
+ if (enabled) {
+ window->flags |= bitFlag;
+ } else {
+ window->flags &= ~bitFlag;
+ }
+
+ return true;
+}
+
// 0x4B7734
int scriptWindowWidth()
{
@@ -843,9 +884,6 @@ int scriptWindowCreate(const char* windowName, int x, int y, int width, int heig
managedWindow->buttonsLength = 0;
flags |= WINDOW_MANAGED | WINDOW_USE_DEFAULTS;
- if (off_672D74 != nullptr) {
- off_672D74(windowIndex, managedWindow->name, &flags);
- }
managedWindow->window = windowCreate(x, y, width, height, a6, flags);
managedWindow->cursorY = 0;
@@ -950,23 +988,30 @@ unsigned char* scriptWindowGetBuffer()
// 0x4B8330
int scriptWindowPush(const char* windowName)
{
- if (_winTOS >= MANAGED_WINDOW_COUNT) {
- return -1;
+ int oldCurrentWindowIndex = gCurrentManagedWindowIndex;
+
+ int existingIndex = -1;
+ for (int index = 0; index <= _winTOS; index++) {
+ if (_winStack[index] == oldCurrentWindowIndex) {
+ existingIndex = index;
+ break;
+ }
}
- int oldCurrentWindowIndex = gCurrentManagedWindowIndex;
+ // CE: check duplicates before checking capacity; removing one frees a stack slot.
+ int newTopIndex = existingIndex == -1 ? _winTOS + 1 : _winTOS;
+ if (newTopIndex >= MANAGED_WINDOW_COUNT) {
+ return -1;
+ }
int windowIndex = scriptWindowSelect(windowName);
if (windowIndex == -1) {
return -1;
}
- // TODO: Check.
- for (int index = 0; index < _winTOS; index++) {
- if (_winStack[index] == oldCurrentWindowIndex) {
- memcpy(&(_winStack[index]), &(_winStack[index + 1]), sizeof(*_winStack) * (_winTOS - index));
- break;
- }
+ if (existingIndex != -1) {
+ memmove(&(_winStack[existingIndex]), &(_winStack[existingIndex + 1]), sizeof(*_winStack) * (_winTOS - existingIndex));
+ _winTOS--;
}
_winTOS++;
@@ -983,9 +1028,15 @@ int _popWindow()
}
int windowIndex = _winStack[_winTOS];
- ManagedWindow* managedWindow = &(gManagedWindows[windowIndex]);
_winTOS--;
+ if (windowIndex < 0 || windowIndex >= MANAGED_WINDOW_COUNT) {
+ gCurrentManagedWindowIndex = -1;
+ return -1;
+ }
+
+ ManagedWindow* managedWindow = &(gManagedWindows[windowIndex]);
+
return scriptWindowSelect(managedWindow->name);
}
@@ -1745,8 +1796,7 @@ bool scriptWindowAddButtonGfx(const char* buttonName, char* pressedFileName, cha
}
if (hoverFileName != nullptr) {
- // TODO: this almost certainly should be hoverFileName
- unsigned char* hover = datafileRead(normalFileName, &width, &height);
+ unsigned char* hover = datafileRead(hoverFileName, &width, &height);
if (hover != nullptr) {
if (managedButton->hover == nullptr) {
managedButton->hover = (unsigned char*)internal_malloc_safe(managedButton->height * managedButton->width, __FILE__, __LINE__); // "..\\int\\WINDOW.C, 1849
@@ -2657,4 +2707,19 @@ bool scriptWindowShowNamed(const char* windowName)
return false;
}
+bool scriptWindowSetNamedFlag(const char* windowName, int bitFlag, bool enabled)
+{
+ for (int index = 0; index < MANAGED_WINDOW_COUNT; index++) {
+ ManagedWindow* managedWindow = &(gManagedWindows[index]);
+ if (managedWindow->window != -1) {
+ if (compat_stricmp(managedWindow->name, windowName) == 0) {
+ // note: Sfall also updates managedWindow->flags, but that is unused
+ return scriptWindowSetFlag(managedWindow->window, bitFlag, enabled);
+ }
+ }
+ }
+
+ return false;
+}
+
} // namespace fallout
diff --git a/src/window.h b/src/window.h
index 50b69633..6d776e5e 100644
--- a/src/window.h
+++ b/src/window.h
@@ -65,6 +65,7 @@ void _doRightButtonRelease(int btn, int keyCode);
void _setButtonGFX(int width, int height, unsigned char* normal);
bool scriptWindowHide();
bool scriptWindowShow();
+bool scriptWindowHideNamed(const char* name);
int scriptWindowWidth();
int scriptWindowHeight();
bool scriptWindowDraw();
@@ -131,6 +132,8 @@ void _drawScaledBuf(unsigned char* dest, int destWidth, int destHeight, unsigned
void _fillBuf3x3(unsigned char* src, int srcWidth, int srcHeight, unsigned char* dest, int destWidth, int destHeight);
bool scriptWindowShowNamed(const char* name);
+bool scriptWindowSetFlag(int windowId, int bitFlag, bool enabled);
+bool scriptWindowSetNamedFlag(const char* name, int bitFlag, bool enabled);
} // namespace fallout
diff --git a/src/window_manager.cc b/src/window_manager.cc
index 0422a620..30b67db3 100644
--- a/src/window_manager.cc
+++ b/src/window_manager.cc
@@ -1121,9 +1121,14 @@ void _refresh_all(Rect* rect, unsigned char* dest)
}
// 0x4D7888
+bool windowIsValidWindowId(int win)
+{
+ return win >= 0 && win < MAX_WINDOW_COUNT;
+}
+
Window* windowGetWindow(int win)
{
- if (win == -1) {
+ if (!windowIsValidWindowId(win)) {
return nullptr;
}
diff --git a/src/window_manager.h b/src/window_manager.h
index 2abffc80..91b1ded6 100644
--- a/src/window_manager.h
+++ b/src/window_manager.h
@@ -203,6 +203,7 @@ void windowRefreshRect(int win, const Rect* rect);
void _GNW_win_refresh(Window* window, Rect* rect, unsigned char* dest);
void windowRefreshAll(Rect* rect);
void _win_get_mouse_buf(unsigned char* dest);
+bool windowIsValidWindowId(int win);
Window* windowGetWindow(int win);
unsigned char* windowGetBuffer(int win);
Buffer2D windowGetBuffer2D(int win);
diff --git a/src/worldmap.cc b/src/worldmap.cc
index 714d29c9..fc5f54b7 100644
--- a/src/worldmap.cc
+++ b/src/worldmap.cc
@@ -690,6 +690,11 @@ static int wmMaxMapNum = 0;
// 0x51DE14 wmBkWin
static int wmBkWin = -1;
+int worldmapGetWindow()
+{
+ return windowGetWindow(wmBkWin) != nullptr ? wmBkWin : -1;
+}
+
// 0x51DE24 wmBkWinBuf
static unsigned char* wmBkWinBuf = nullptr;
diff --git a/src/worldmap.h b/src/worldmap.h
index de79e2d1..b0877f55 100644
--- a/src/worldmap.h
+++ b/src/worldmap.h
@@ -289,6 +289,7 @@ void wmSetPartyWorldPos(int x, int y);
void wmCarSetCurrentArea(int area);
void wmForceEncounter(int map, unsigned int flags);
void wmSetScriptWorldMapMulti(float value);
+int worldmapGetWindow();
} // namespace fallout