#include "interface.h" #include #include #include "animation.h" #include "art.h" #include "color.h" #include "combat.h" #include "config.h" #include "critter.h" #include "cycle.h" #include "debug.h" #include "display_monitor.h" #include "draw.h" #include "endgame.h" #include "game.h" #include "game_mouse.h" #include "game_sound.h" #include "geometry.h" #include "input.h" #include "item.h" #include "kb.h" #include "memory.h" #include "mouse.h" #include "object.h" #include "platform_compat.h" #include "proto.h" #include "proto_instance.h" #include "proto_types.h" #include "skill.h" #include "stat.h" #include "svga.h" #include "text_font.h" #include "tile.h" #include "window_manager.h" namespace fallout { // The width of connectors in the indicator box. // // There are male connectors on the left, and female connectors on the right. // When displaying series of boxes they appear to be plugged into a chain. #define INDICATOR_BOX_CONNECTOR_WIDTH 3 // Minimum radiation amount to display RADIATED indicator. #define RADATION_INDICATOR_THRESHOLD 65 // Minimum poison amount to display POISONED indicator. #define POISON_INDICATOR_THRESHOLD 0 // The maximum number of indicator boxes the indicator bar can display. // // For unknown reason this number is 6, even though there are only 5 different // indicator types. In addition to that, default screen width 640px cannot hold // 6 boxes 130px each. #define INDICATOR_SLOTS_COUNT (6) // The values of it's members are offsets to beginning of numbers in // numbers.frm. typedef enum InterfaceNumbersColor { INTERFACE_NUMBERS_COLOR_WHITE = 0, INTERFACE_NUMBERS_COLOR_YELLOW = 120, INTERFACE_NUMBERS_COLOR_RED = 240, } InterfaceNumbersColor; // Available indicators. // // Indicator boxes in the bar are displayed according to the order of this enum. typedef enum Indicator { INDICATOR_ADDICT, INDICATOR_SNEAK, INDICATOR_LEVEL, INDICATOR_POISONED, INDICATOR_RADIATED, INDICATOR_COUNT, } Indicator; // Provides metadata about indicator boxes. typedef struct IndicatorDescription { // An identifier of title in `intrface.msg`. int title; // A flag denoting this box represents something harmful to the player. It // affects color of the title. bool isBad; // Prerendered indicator data. // // This value is provided at runtime during indicator box initialization. // It includes indicator box background with it's title positioned in the // center and is green colored if indicator is good, or red otherwise, as // denoted by [isBad] property. unsigned char* data; } IndicatorDescription; typedef struct InterfaceItemState { Object* item; unsigned char isDisabled; unsigned char isWeapon; int primaryHitMode; int secondaryHitMode; int action; int itemFid; } InterfaceItemState; static int _intface_redraw_items_callback(Object* a1, Object* a2); static int _intface_change_fid_callback(Object* a1, Object* a2); static void interfaceBarSwapHandsAnimatePutAwayTakeOutSequence(int previousWeaponAnimationCode, int weaponAnimationCode); static int intface_init_items(); static int interfaceBarRefreshMainAction(); static int endTurnButtonInit(); static int endTurnButtonFree(); static int endCombatButtonInit(); static int endCombatButtonFree(); static void interfaceUpdateAmmoBar(int x, int ratio); static int _intface_item_reload(); static void interfaceRenderCounter(int x, int y, int previousValue, int value, int offset, int delay); static int intface_fatal_error(int rc); static int indicatorBarInit(); static void interfaceBarFree(); static void indicatorBarReset(); static int indicatorBoxCompareByPosition(const void* a, const void* b); static void indicatorBarRender(int count); static bool indicatorBarAdd(int indicator); // 0x518F08 static bool gInterfaceBarInitialized = false; // 0x518F0C static bool gInterfaceBarSwapHandsInProgress = false; // 0x518F10 static bool gInterfaceBarEnabled = false; // 0x518F14 static bool _intfaceHidden = false; // 0x518F18 static int gInventoryButton = -1; // 0x518F24 static int gOptionsButton = -1; // 0x518F30 static int gSkilldexButton = -1; // 0x518F40 static int gMapButton = -1; // 0x518F50 static int gPipboyButton = -1; // 0x518F5C static int gCharacterButton = -1; // 0x518F68 static int gSingleAttackButton = -1; // 0x518F78 static int gInterfaceCurrentHand = HAND_LEFT; // 0x518F7C static const Rect gInterfaceBarMainActionRect = { 267, 26, 455, 93 }; // 0x518F8C static int gChangeHandsButton = -1; // 0x518F9C static bool gInterfaceBarEndButtonsIsVisible = false; // Combat mode curtains rect. // // 0x518FA0 static const Rect gInterfaceBarEndButtonsRect = { 580, 38, 637, 96 }; // 0x518FB0 static int gEndTurnButton = -1; // 0x518FBC static int gEndCombatButton = -1; // 0x518FD4 static const Rect gInterfaceBarActionPointsBarRect = { 316, 14, 406, 19 }; // 0x518FE8 static IndicatorDescription gIndicatorDescriptions[INDICATOR_COUNT] = { { 102, true, NULL }, // ADDICT { 100, false, NULL }, // SNEAK { 101, false, NULL }, // LEVEL { 103, true, NULL }, // POISONED { 104, true, NULL }, // RADIATED }; // 0x519024 int gInterfaceBarWindow = -1; // 0x519028 static int gIndicatorBarWindow = -1; // Last hit points rendered in interface. // // Used to animate changes. // // 0x51902C static int gInterfaceLastRenderedHitPoints = 0; // Last color used to render hit points in interface. // // Used to animate changes. // // 0x519030 static int gInterfaceLastRenderedHitPointsColor = INTERFACE_NUMBERS_COLOR_RED; // Last armor class rendered in interface. // // Used to animate changes. // // 0x519034 static int gInterfaceLastRenderedArmorClass = 0; // Each slot contains one of indicators or -1 if slot is empty. // // 0x5970E0 static int gIndicatorSlots[INDICATOR_SLOTS_COUNT]; // 0x5970F8 static InterfaceItemState gInterfaceItemStates[HAND_COUNT]; // 0x597138 static bool gIndicatorBarIsVisible; // 0x597154 static unsigned char _itemButtonDown[188 * 67]; // 0x59A2B4 static unsigned char _itemButtonUp[188 * 67]; // 0x59D3F4 static unsigned char* gInterfaceWindowBuffer; // A slice of main interface background containing 10 shadowed action point // dots. In combat mode individual colored dots are rendered on top of this // background. // // This buffer is initialized once and does not change throughout the game. // // 0x59D40C static unsigned char gInterfaceActionPointsBarBackground[90 * 5]; // Should the game window stretch all the way to the bottom or sit at the top of the interface bar (default) bool gInterfaceBarMode = false; static FrmImage _inventoryButtonNormalFrmImage; static FrmImage _inventoryButtonPressedFrmImage; static FrmImage _optionsButtonNormalFrmImage; static FrmImage _optionsButtonPressedFrmImage; static FrmImage _skilldexButtonNormalFrmImage; static FrmImage _skilldexButtonPressedFrmImage; static FrmImage _skilldexButtonMaskFrmImage; static FrmImage _mapButtonNormalFrmImage; static FrmImage _mapButtonPressedFrmImage; static FrmImage _mapButtonMaskFrmImage; static FrmImage _pipboyButtonNormalFrmImage; static FrmImage _pipboyButtonPressedFrmImage; static FrmImage _characterButtonNormalFrmImage; static FrmImage _characterButtonPressedFrmImage; static FrmImage _changeHandsButtonNormalFrmImage; static FrmImage _changeHandsButtonPressedFrmImage; static FrmImage _changeHandsButtonMaskFrmImage; static FrmImage _itemButtonNormalFrmImage; static FrmImage _itemButtonPressedFrmImage; static FrmImage _itemButtonDisabledFrmImage; static FrmImage _endTurnButtonNormalFrmImage; static FrmImage _endTurnButtonPressedFrmImage; static FrmImage _endCombatButtonNormalFrmImage; static FrmImage _endCombatButtonPressedFrmImage; static FrmImage _numbersFrmImage; static FrmImage _greenLightFrmImage; static FrmImage _yellowLightFrmImage; static FrmImage _redLightFrmImage; // intface_init // 0x45D880 int interfaceInit() { int fid; if (gInterfaceBarWindow != -1) { return -1; } gInterfaceBarInitialized = 1; int interfaceBarWindowX = (screenGetWidth() - INTERFACE_BAR_WIDTH) / 2; int interfaceBarWindowY = screenGetHeight() - INTERFACE_BAR_HEIGHT; gInterfaceBarWindow = windowCreate(interfaceBarWindowX, interfaceBarWindowY, INTERFACE_BAR_WIDTH, INTERFACE_BAR_HEIGHT, _colorTable[0], WINDOW_HIDDEN); if (gInterfaceBarWindow == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } gInterfaceWindowBuffer = windowGetBuffer(gInterfaceBarWindow); if (gInterfaceWindowBuffer == NULL) { // NOTE: Uninline. return intface_fatal_error(-1); } FrmImage backgroundFrmImage; fid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0); if (!backgroundFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } blitBufferToBuffer(backgroundFrmImage.getData(), INTERFACE_BAR_WIDTH, INTERFACE_BAR_HEIGHT - 1, INTERFACE_BAR_WIDTH, gInterfaceWindowBuffer, 640); backgroundFrmImage.unlock(); fid = buildFid(OBJ_TYPE_INTERFACE, 47, 0, 0, 0); if (!_inventoryButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 46, 0, 0, 0); if (!_inventoryButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gInventoryButton = buttonCreate(gInterfaceBarWindow, 211, 40, 32, 21, -1, -1, -1, KEY_LOWERCASE_I, _inventoryButtonNormalFrmImage.getData(), _inventoryButtonPressedFrmImage.getData(), NULL, 0); if (gInventoryButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetCallbacks(gInventoryButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 18, 0, 0, 0); if (!_optionsButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 17, 0, 0, 0); if (!_optionsButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gOptionsButton = buttonCreate(gInterfaceBarWindow, 210, 61, 34, 34, -1, -1, -1, KEY_LOWERCASE_O, _optionsButtonNormalFrmImage.getData(), _optionsButtonPressedFrmImage.getData(), NULL, 0); if (gOptionsButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetCallbacks(gOptionsButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 6, 0, 0, 0); if (!_skilldexButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 7, 0, 0, 0); if (!_skilldexButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 6, 0, 0, 0); if (!_skilldexButtonMaskFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gSkilldexButton = buttonCreate(gInterfaceBarWindow, 523, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_S, _skilldexButtonNormalFrmImage.getData(), _skilldexButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT); if (gSkilldexButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetMask(gSkilldexButton, _skilldexButtonMaskFrmImage.getData()); buttonSetCallbacks(gSkilldexButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 13, 0, 0, 0); if (!_mapButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 10, 0, 0, 0); if (!_mapButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 13, 0, 0, 0); if (!_mapButtonMaskFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gMapButton = buttonCreate(gInterfaceBarWindow, 526, 39, 41, 19, -1, -1, -1, KEY_TAB, _mapButtonNormalFrmImage.getData(), _mapButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT); if (gMapButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetMask(gMapButton, _mapButtonMaskFrmImage.getData()); buttonSetCallbacks(gMapButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 59, 0, 0, 0); if (!_pipboyButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 58, 0, 0, 0); if (!_pipboyButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gPipboyButton = buttonCreate(gInterfaceBarWindow, 526, 77, 41, 19, -1, -1, -1, KEY_LOWERCASE_P, _pipboyButtonNormalFrmImage.getData(), _pipboyButtonPressedFrmImage.getData(), NULL, 0); if (gPipboyButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetMask(gPipboyButton, _mapButtonMaskFrmImage.getData()); buttonSetCallbacks(gPipboyButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 57, 0, 0, 0); if (!_characterButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 56, 0, 0, 0); if (!_characterButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } gCharacterButton = buttonCreate(gInterfaceBarWindow, 526, 58, 41, 19, -1, -1, -1, KEY_LOWERCASE_C, _characterButtonNormalFrmImage.getData(), _characterButtonPressedFrmImage.getData(), NULL, 0); if (gCharacterButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetMask(gCharacterButton, _mapButtonMaskFrmImage.getData()); buttonSetCallbacks(gCharacterButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 32, 0, 0, 0); if (!_itemButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 31, 0, 0, 0); if (!_itemButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 73, 0, 0, 0); if (!_itemButtonDisabledFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } memcpy(_itemButtonUp, _itemButtonNormalFrmImage.getData(), sizeof(_itemButtonUp)); memcpy(_itemButtonDown, _itemButtonPressedFrmImage.getData(), sizeof(_itemButtonDown)); gSingleAttackButton = buttonCreate(gInterfaceBarWindow, 267, 26, 188, 67, -1, -1, -1, -20, _itemButtonUp, _itemButtonDown, NULL, BUTTON_FLAG_TRANSPARENT); if (gSingleAttackButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetRightMouseCallbacks(gSingleAttackButton, -1, KEY_LOWERCASE_N, NULL, NULL); buttonSetCallbacks(gSingleAttackButton, _gsound_lrg_butt_press, _gsound_lrg_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 6, 0, 0, 0); if (!_changeHandsButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 7, 0, 0, 0); if (!_changeHandsButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 6, 0, 0, 0); if (!_changeHandsButtonMaskFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } // Swap hands button gChangeHandsButton = buttonCreate(gInterfaceBarWindow, 218, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_B, _changeHandsButtonNormalFrmImage.getData(), _changeHandsButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT); if (gChangeHandsButton == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } buttonSetMask(gChangeHandsButton, _changeHandsButtonMaskFrmImage.getData()); buttonSetCallbacks(gChangeHandsButton, _gsound_med_butt_press, _gsound_med_butt_release); fid = buildFid(OBJ_TYPE_INTERFACE, 82, 0, 0, 0); if (!_numbersFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 83, 0, 0, 0); if (!_greenLightFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 84, 0, 0, 0); if (!_yellowLightFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } fid = buildFid(OBJ_TYPE_INTERFACE, 85, 0, 0, 0); if (!_redLightFrmImage.lock(fid)) { // NOTE: Uninline. return intface_fatal_error(-1); } blitBufferToBuffer(gInterfaceWindowBuffer + 640 * 14 + 316, 90, 5, 640, gInterfaceActionPointsBarBackground, 90); if (indicatorBarInit() == -1) { // NOTE: Uninline. return intface_fatal_error(-1); } gInterfaceCurrentHand = HAND_LEFT; // NOTE: Uninline. intface_init_items(); displayMonitorInit(); gInterfaceBarEnabled = true; gInterfaceBarInitialized = false; _intfaceHidden = 1; return 0; } // 0x45E3D0 void interfaceReset() { interfaceBarEnable(); // NOTE: Uninline. intface_hide(); indicatorBarRefresh(); displayMonitorReset(); // NOTE: Uninline a seemingly inlined routine. indicatorBarReset(); gInterfaceCurrentHand = 0; } // 0x45E440 void interfaceFree() { if (gInterfaceBarWindow != -1) { displayMonitorExit(); _redLightFrmImage.unlock(); _yellowLightFrmImage.unlock(); _greenLightFrmImage.unlock(); _numbersFrmImage.unlock(); if (gChangeHandsButton != -1) { buttonDestroy(gChangeHandsButton); gChangeHandsButton = -1; } _changeHandsButtonMaskFrmImage.unlock(); _changeHandsButtonPressedFrmImage.unlock(); _changeHandsButtonNormalFrmImage.unlock(); if (gSingleAttackButton != -1) { buttonDestroy(gSingleAttackButton); gSingleAttackButton = -1; } _itemButtonDisabledFrmImage.unlock(); _itemButtonPressedFrmImage.unlock(); _itemButtonNormalFrmImage.unlock(); if (gCharacterButton != -1) { buttonDestroy(gCharacterButton); gCharacterButton = -1; } _characterButtonPressedFrmImage.unlock(); _characterButtonNormalFrmImage.unlock(); if (gPipboyButton != -1) { buttonDestroy(gPipboyButton); gPipboyButton = -1; } _pipboyButtonPressedFrmImage.unlock(); _pipboyButtonNormalFrmImage.unlock(); if (gMapButton != -1) { buttonDestroy(gMapButton); gMapButton = -1; } _mapButtonMaskFrmImage.unlock(); _mapButtonPressedFrmImage.unlock(); _mapButtonNormalFrmImage.unlock(); if (gSkilldexButton != -1) { buttonDestroy(gSkilldexButton); gSkilldexButton = -1; } _skilldexButtonMaskFrmImage.unlock(); _skilldexButtonPressedFrmImage.unlock(); _skilldexButtonNormalFrmImage.unlock(); if (gOptionsButton != -1) { buttonDestroy(gOptionsButton); gOptionsButton = -1; } _optionsButtonPressedFrmImage.unlock(); _optionsButtonNormalFrmImage.unlock(); if (gInventoryButton != -1) { buttonDestroy(gInventoryButton); gInventoryButton = -1; } _inventoryButtonPressedFrmImage.unlock(); _inventoryButtonNormalFrmImage.unlock(); if (gInterfaceBarWindow != -1) { windowDestroy(gInterfaceBarWindow); gInterfaceBarWindow = -1; } } interfaceBarFree(); } // 0x45E860 int interfaceLoad(File* stream) { if (gInterfaceBarWindow == -1) { if (interfaceInit() == -1) { return -1; } } int interfaceBarEnabled; if (fileReadInt32(stream, &interfaceBarEnabled) == -1) return -1; int v2; if (fileReadInt32(stream, &v2) == -1) return -1; int interfaceCurrentHand; if (fileReadInt32(stream, &interfaceCurrentHand) == -1) return -1; bool interfaceBarEndButtonsIsVisible; if (fileReadBool(stream, &interfaceBarEndButtonsIsVisible) == -1) return -1; if (!gInterfaceBarEnabled) { interfaceBarEnable(); } if (v2) { // NOTE: Uninline. intface_hide(); } else { _intface_show(); } interfaceRenderHitPoints(false); interfaceRenderArmorClass(false); gInterfaceCurrentHand = interfaceCurrentHand; interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); if (interfaceBarEndButtonsIsVisible != gInterfaceBarEndButtonsIsVisible) { if (interfaceBarEndButtonsIsVisible) { interfaceBarEndButtonsShow(false); } else { interfaceBarEndButtonsHide(false); } } if (!interfaceBarEnabled) { interfaceBarDisable(); } indicatorBarRefresh(); windowRefresh(gInterfaceBarWindow); return 0; } // 0x45E988 int interfaceSave(File* stream) { if (gInterfaceBarWindow == -1) { return -1; } if (fileWriteInt32(stream, gInterfaceBarEnabled) == -1) return -1; if (fileWriteInt32(stream, _intfaceHidden) == -1) return -1; if (fileWriteInt32(stream, gInterfaceCurrentHand) == -1) return -1; if (fileWriteInt32(stream, gInterfaceBarEndButtonsIsVisible) == -1) return -1; return 0; } // NOTE: Inlined. // // 0x45E9E0 void intface_hide() { if (gInterfaceBarWindow != -1) { if (!_intfaceHidden) { windowHide(gInterfaceBarWindow); _intfaceHidden = 1; } } indicatorBarRefresh(); } // 0x45EA10 void _intface_show() { if (gInterfaceBarWindow != -1) { if (_intfaceHidden) { interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); interfaceRenderHitPoints(false); interfaceRenderArmorClass(false); windowUnhide(gInterfaceBarWindow); _intfaceHidden = false; } } indicatorBarRefresh(); } // 0x45EA64 void interfaceBarEnable() { if (!gInterfaceBarEnabled) { buttonEnable(gInventoryButton); buttonEnable(gOptionsButton); buttonEnable(gSkilldexButton); buttonEnable(gMapButton); buttonEnable(gPipboyButton); buttonEnable(gCharacterButton); if (gInterfaceItemStates[gInterfaceCurrentHand].isDisabled == 0) { buttonEnable(gSingleAttackButton); } buttonEnable(gEndTurnButton); buttonEnable(gEndCombatButton); displayMonitorEnable(); gInterfaceBarEnabled = true; } } // 0x45EAFC void interfaceBarDisable() { if (gInterfaceBarEnabled) { displayMonitorDisable(); buttonDisable(gInventoryButton); buttonDisable(gOptionsButton); buttonDisable(gSkilldexButton); buttonDisable(gMapButton); buttonDisable(gPipboyButton); buttonDisable(gCharacterButton); if (gInterfaceItemStates[gInterfaceCurrentHand].isDisabled == 0) { buttonDisable(gSingleAttackButton); } buttonDisable(gEndTurnButton); buttonDisable(gEndCombatButton); gInterfaceBarEnabled = false; } } // 0x45EB90 bool interfaceBarEnabled() { return gInterfaceBarEnabled; } // 0x45EB98 void interfaceBarRefresh() { if (gInterfaceBarWindow != -1) { interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); interfaceRenderHitPoints(false); interfaceRenderArmorClass(false); indicatorBarRefresh(); windowRefresh(gInterfaceBarWindow); } indicatorBarRefresh(); } // Render hit points. // // 0x45EBD8 void interfaceRenderHitPoints(bool animate) { if (gInterfaceBarWindow == -1) { return; } int hp = critterGetHitPoints(gDude); int maxHp = critterGetStat(gDude, STAT_MAXIMUM_HIT_POINTS); int red = (int)((double)maxHp * 0.25); int yellow = (int)((double)maxHp * 0.5); int color; if (hp < red) { color = INTERFACE_NUMBERS_COLOR_RED; } else if (hp < yellow) { color = INTERFACE_NUMBERS_COLOR_YELLOW; } else { color = INTERFACE_NUMBERS_COLOR_WHITE; } int v1[4]; int v2[3]; int count = 1; v1[0] = gInterfaceLastRenderedHitPoints; v2[0] = gInterfaceLastRenderedHitPointsColor; if (gInterfaceLastRenderedHitPointsColor != color) { if (hp >= gInterfaceLastRenderedHitPoints) { if (gInterfaceLastRenderedHitPoints < red && hp >= red) { v1[count] = red; v2[count] = INTERFACE_NUMBERS_COLOR_YELLOW; count += 1; } if (gInterfaceLastRenderedHitPoints < yellow && hp >= yellow) { v1[count] = yellow; v2[count] = INTERFACE_NUMBERS_COLOR_WHITE; count += 1; } } else { if (gInterfaceLastRenderedHitPoints >= yellow && hp < yellow) { v1[count] = yellow; v2[count] = INTERFACE_NUMBERS_COLOR_YELLOW; count += 1; } if (gInterfaceLastRenderedHitPoints >= red && hp < red) { v1[count] = red; v2[count] = INTERFACE_NUMBERS_COLOR_RED; count += 1; } } } v1[count] = hp; if (animate) { int delay = 250 / (abs(gInterfaceLastRenderedHitPoints - hp) + 1); for (int index = 0; index < count; index++) { interfaceRenderCounter(473, 40, v1[index], v1[index + 1], v2[index], delay); } } else { interfaceRenderCounter(473, 40, gInterfaceLastRenderedHitPoints, hp, color, 0); } gInterfaceLastRenderedHitPoints = hp; gInterfaceLastRenderedHitPointsColor = color; } // Render armor class. // // 0x45EDA8 void interfaceRenderArmorClass(bool animate) { int armorClass = critterGetStat(gDude, STAT_ARMOR_CLASS); int delay = 0; if (animate) { delay = 250 / (abs(gInterfaceLastRenderedArmorClass - armorClass) + 1); } interfaceRenderCounter(473, 75, gInterfaceLastRenderedArmorClass, armorClass, 0, delay); gInterfaceLastRenderedArmorClass = armorClass; } // 0x45EE0C void interfaceRenderActionPoints(int actionPointsLeft, int bonusActionPoints) { unsigned char* frmData; if (gInterfaceBarWindow == -1) { return; } blitBufferToBuffer(gInterfaceActionPointsBarBackground, 90, 5, 90, gInterfaceWindowBuffer + 14 * 640 + 316, 640); if (actionPointsLeft == -1) { frmData = _redLightFrmImage.getData(); actionPointsLeft = 10; bonusActionPoints = 0; } else { frmData = _greenLightFrmImage.getData(); if (actionPointsLeft < 0) { actionPointsLeft = 0; } if (actionPointsLeft > 10) { actionPointsLeft = 10; } if (bonusActionPoints >= 0) { if (actionPointsLeft + bonusActionPoints > 10) { bonusActionPoints = 10 - actionPointsLeft; } } else { bonusActionPoints = 0; } } int index; for (index = 0; index < actionPointsLeft; index++) { blitBufferToBuffer(frmData, 5, 5, 5, gInterfaceWindowBuffer + 14 * 640 + 316 + index * 9, 640); } for (; index < (actionPointsLeft + bonusActionPoints); index++) { blitBufferToBuffer(_yellowLightFrmImage.getData(), 5, 5, 5, gInterfaceWindowBuffer + 14 * 640 + 316 + index * 9, 640); } if (!gInterfaceBarInitialized) { windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarActionPointsBarRect); } } // 0x45EF6C int interfaceGetCurrentHitMode(int* hitMode, bool* aiming) { if (gInterfaceBarWindow == -1) { return -1; } *aiming = false; switch (gInterfaceItemStates[gInterfaceCurrentHand].action) { case INTERFACE_ITEM_ACTION_PRIMARY_AIMING: *aiming = true; // FALLTHROUGH case INTERFACE_ITEM_ACTION_PRIMARY: *hitMode = gInterfaceItemStates[gInterfaceCurrentHand].primaryHitMode; return 0; case INTERFACE_ITEM_ACTION_SECONDARY_AIMING: *aiming = true; // FALLTHROUGH case INTERFACE_ITEM_ACTION_SECONDARY: *hitMode = gInterfaceItemStates[gInterfaceCurrentHand].secondaryHitMode; return 0; } return -1; } // 0x45EFEC int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) { if (isoIsDisabled()) { animated = false; } if (gInterfaceBarWindow == -1) { return -1; } Object* oldCurrentItem = gInterfaceItemStates[gInterfaceCurrentHand].item; InterfaceItemState* leftItemState = &(gInterfaceItemStates[HAND_LEFT]); Object* item1 = critterGetItem1(gDude); if (item1 == leftItemState->item && leftItemState->item != NULL) { if (leftItemState->item != NULL) { leftItemState->isDisabled = dudeIsWeaponDisabled(item1); leftItemState->itemFid = itemGetInventoryFid(item1); } } else { Object* oldItem = leftItemState->item; int oldAction = leftItemState->action; leftItemState->item = item1; if (item1 != NULL) { leftItemState->isDisabled = dudeIsWeaponDisabled(item1); leftItemState->primaryHitMode = HIT_MODE_LEFT_WEAPON_PRIMARY; leftItemState->secondaryHitMode = HIT_MODE_LEFT_WEAPON_SECONDARY; leftItemState->isWeapon = itemGetType(item1) == ITEM_TYPE_WEAPON; if (leftItemAction == INTERFACE_ITEM_ACTION_DEFAULT) { if (leftItemState->isWeapon != 0) { leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; } else { leftItemState->action = INTERFACE_ITEM_ACTION_USE; } } else { leftItemState->action = leftItemAction; } leftItemState->itemFid = itemGetInventoryFid(item1); } else { leftItemState->isDisabled = 0; leftItemState->isWeapon = 1; leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; leftItemState->itemFid = -1; // SFALL leftItemState->primaryHitMode = unarmedGetPunchHitMode(false); leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true); // SFALL: Keep selected attack mode. // CE: Implementation is different. if (oldItem == NULL) { leftItemState->action = oldAction; } } } InterfaceItemState* rightItemState = &(gInterfaceItemStates[HAND_RIGHT]); Object* item2 = critterGetItem2(gDude); if (item2 == rightItemState->item && rightItemState->item != NULL) { if (rightItemState->item != NULL) { rightItemState->isDisabled = dudeIsWeaponDisabled(rightItemState->item); rightItemState->itemFid = itemGetInventoryFid(rightItemState->item); } } else { Object* oldItem = rightItemState->item; int oldAction = rightItemState->action; rightItemState->item = item2; if (item2 != NULL) { rightItemState->isDisabled = dudeIsWeaponDisabled(item2); rightItemState->primaryHitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY; rightItemState->secondaryHitMode = HIT_MODE_RIGHT_WEAPON_SECONDARY; rightItemState->isWeapon = itemGetType(item2) == ITEM_TYPE_WEAPON; if (rightItemAction == INTERFACE_ITEM_ACTION_DEFAULT) { if (rightItemState->isWeapon != 0) { rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; } else { rightItemState->action = INTERFACE_ITEM_ACTION_USE; } } else { rightItemState->action = rightItemAction; } rightItemState->itemFid = itemGetInventoryFid(item2); } else { rightItemState->isDisabled = 0; rightItemState->isWeapon = 1; rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; rightItemState->itemFid = -1; // SFALL rightItemState->primaryHitMode = unarmedGetKickHitMode(false); rightItemState->secondaryHitMode = unarmedGetKickHitMode(true); // SFALL: Keep selected attack mode. // CE: Implementation is different. if (oldItem == NULL) { rightItemState->action = oldAction; } } } if (animated) { Object* newCurrentItem = gInterfaceItemStates[gInterfaceCurrentHand].item; if (newCurrentItem != oldCurrentItem) { int animationCode = 0; if (newCurrentItem != NULL) { if (itemGetType(newCurrentItem) == ITEM_TYPE_WEAPON) { animationCode = weaponGetAnimationCode(newCurrentItem); } } interfaceBarSwapHandsAnimatePutAwayTakeOutSequence((gDude->fid & 0xF000) >> 12, animationCode); return 0; } } interfaceBarRefreshMainAction(); return 0; } // 0x45F404 int interfaceBarSwapHands(bool animated) { if (gInterfaceBarWindow == -1) { return -1; } gInterfaceCurrentHand = 1 - gInterfaceCurrentHand; if (animated) { Object* item = gInterfaceItemStates[gInterfaceCurrentHand].item; int animationCode = 0; if (item != NULL) { if (itemGetType(item) == ITEM_TYPE_WEAPON) { animationCode = weaponGetAnimationCode(item); } } interfaceBarSwapHandsAnimatePutAwayTakeOutSequence((gDude->fid & 0xF000) >> 12, animationCode); } else { interfaceBarRefreshMainAction(); } int mode = gameMouseGetMode(); if (mode == GAME_MOUSE_MODE_CROSSHAIR || mode == GAME_MOUSE_MODE_USE_CROSSHAIR) { gameMouseSetMode(GAME_MOUSE_MODE_MOVE); } return 0; } // 0x45F4B4 int interfaceGetItemActions(int* leftItemAction, int* rightItemAction) { *leftItemAction = gInterfaceItemStates[HAND_LEFT].action; *rightItemAction = gInterfaceItemStates[HAND_RIGHT].action; return 0; } // 0x45F4E0 int interfaceCycleItemAction() { if (gInterfaceBarWindow == -1) { return -1; } InterfaceItemState* itemState = &(gInterfaceItemStates[gInterfaceCurrentHand]); int oldAction = itemState->action; if (itemState->isWeapon != 0) { bool done = false; while (!done) { itemState->action++; switch (itemState->action) { case INTERFACE_ITEM_ACTION_PRIMARY: done = true; break; case INTERFACE_ITEM_ACTION_PRIMARY_AIMING: if (critterCanAim(gDude, itemState->primaryHitMode)) { done = true; } break; case INTERFACE_ITEM_ACTION_SECONDARY: if (itemState->secondaryHitMode != HIT_MODE_PUNCH && itemState->secondaryHitMode != HIT_MODE_KICK && weaponGetAttackTypeForHitMode(itemState->item, itemState->secondaryHitMode) != ATTACK_TYPE_NONE) { done = true; } break; case INTERFACE_ITEM_ACTION_SECONDARY_AIMING: if (itemState->secondaryHitMode != HIT_MODE_PUNCH && itemState->secondaryHitMode != HIT_MODE_KICK && weaponGetAttackTypeForHitMode(itemState->item, itemState->secondaryHitMode) != ATTACK_TYPE_NONE && critterCanAim(gDude, itemState->secondaryHitMode)) { done = true; } break; case INTERFACE_ITEM_ACTION_RELOAD: if (ammoGetCapacity(itemState->item) != ammoGetQuantity(itemState->item)) { done = true; } break; case INTERFACE_ITEM_ACTION_COUNT: itemState->action = INTERFACE_ITEM_ACTION_USE; break; } } } if (oldAction != itemState->action) { interfaceBarRefreshMainAction(); } return 0; } // 0x45F5EC void _intface_use_item() { if (gInterfaceBarWindow == -1) { return; } InterfaceItemState* ptr = &(gInterfaceItemStates[gInterfaceCurrentHand]); if (ptr->isWeapon != 0) { if (ptr->action == INTERFACE_ITEM_ACTION_RELOAD) { if (isInCombat()) { int hitMode = gInterfaceCurrentHand == HAND_LEFT ? HIT_MODE_LEFT_WEAPON_RELOAD : HIT_MODE_RIGHT_WEAPON_RELOAD; int actionPointsRequired = itemGetActionPointCost(gDude, hitMode, false); if (actionPointsRequired <= gDude->data.critter.combat.ap) { if (_intface_item_reload() == 0) { if (actionPointsRequired > gDude->data.critter.combat.ap) { gDude->data.critter.combat.ap = 0; } else { gDude->data.critter.combat.ap -= actionPointsRequired; } interfaceRenderActionPoints(gDude->data.critter.combat.ap, _combat_free_move); } } } else { _intface_item_reload(); } } else { gameMouseSetCursor(MOUSE_CURSOR_CROSSHAIR); gameMouseSetMode(GAME_MOUSE_MODE_CROSSHAIR); if (!isInCombat()) { _combat(NULL); } } } else if (_proto_action_can_use_on(ptr->item->pid)) { gameMouseSetCursor(MOUSE_CURSOR_USE_CROSSHAIR); gameMouseSetMode(GAME_MOUSE_MODE_USE_CROSSHAIR); } else if (_obj_action_can_use(ptr->item)) { if (isInCombat()) { int actionPointsRequired = itemGetActionPointCost(gDude, ptr->secondaryHitMode, false); if (actionPointsRequired <= gDude->data.critter.combat.ap) { _obj_use_item(gDude, ptr->item); interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); if (actionPointsRequired > gDude->data.critter.combat.ap) { gDude->data.critter.combat.ap = 0; } else { gDude->data.critter.combat.ap -= actionPointsRequired; } interfaceRenderActionPoints(gDude->data.critter.combat.ap, _combat_free_move); } } else { _obj_use_item(gDude, ptr->item); interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); } } } // 0x45F7FC int interfaceGetCurrentHand() { return gInterfaceCurrentHand; } // 0x45F804 int interfaceGetActiveItem(Object** itemPtr) { if (gInterfaceBarWindow == -1) { return -1; } *itemPtr = gInterfaceItemStates[gInterfaceCurrentHand].item; return 0; } // 0x45F838 int _intface_update_ammo_lights() { if (gInterfaceBarWindow == -1) { return -1; } InterfaceItemState* p = &(gInterfaceItemStates[gInterfaceCurrentHand]); int ratio = 0; if (p->isWeapon != 0) { // calls sub_478674 twice, probably because if min/max kind macro int maximum = ammoGetCapacity(p->item); if (maximum > 0) { int current = ammoGetQuantity(p->item); ratio = (int)((double)current / (double)maximum * 70.0); } } else { if (itemGetType(p->item) == ITEM_TYPE_MISC) { // calls sub_4793D0 twice, probably because if min/max kind macro int maximum = miscItemGetMaxCharges(p->item); if (maximum > 0) { int current = miscItemGetCharges(p->item); ratio = (int)((double)current / (double)maximum * 70.0); } } } interfaceUpdateAmmoBar(463, ratio); return 0; } // 0x45F96C void interfaceBarEndButtonsShow(bool animated) { if (gInterfaceBarWindow == -1) { return; } if (gInterfaceBarEndButtonsIsVisible) { return; } int fid = buildFid(OBJ_TYPE_INTERFACE, 104, 0, 0, 0); CacheEntry* handle; Art* art = artLock(fid, &handle); if (art == NULL) { return; } int frameCount = artGetFrameCount(art); soundPlayFile("iciboxx1"); if (animated) { unsigned int delay = 1000 / artGetFramesPerSecond(art); int time = 0; int frame = 0; while (frame < frameCount) { if (getTicksSince(time) >= delay) { unsigned char* src = artGetFrameData(art, frame, 0); if (src != NULL) { blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + 640 * 38 + 580, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } time = getTicks(); frame++; } gameMouseRefresh(); } } else { unsigned char* src = artGetFrameData(art, frameCount - 1, 0); blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + 640 * 38 + 580, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } artUnlock(handle); gInterfaceBarEndButtonsIsVisible = true; endTurnButtonInit(); endCombatButtonInit(); interfaceBarEndButtonsRenderRedLights(); } // 0x45FAC0 void interfaceBarEndButtonsHide(bool animated) { if (gInterfaceBarWindow == -1) { return; } if (!gInterfaceBarEndButtonsIsVisible) { return; } int fid = buildFid(OBJ_TYPE_INTERFACE, 104, 0, 0, 0); CacheEntry* handle; Art* art = artLock(fid, &handle); if (art == NULL) { return; } endTurnButtonFree(); endCombatButtonFree(); soundPlayFile("icibcxx1"); if (animated) { unsigned int delay = 1000 / artGetFramesPerSecond(art); unsigned int time = 0; int frame = artGetFrameCount(art); while (frame != 0) { if (getTicksSince(time) >= delay) { unsigned char* src = artGetFrameData(art, frame - 1, 0); unsigned char* dest = gInterfaceWindowBuffer + 640 * 38 + 580; if (src != NULL) { blitBufferToBuffer(src, 57, 58, 57, dest, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } time = getTicks(); frame--; } gameMouseRefresh(); } } else { unsigned char* dest = gInterfaceWindowBuffer + 640 * 38 + 580; unsigned char* src = artGetFrameData(art, 0, 0); blitBufferToBuffer(src, 57, 58, 57, dest, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } artUnlock(handle); gInterfaceBarEndButtonsIsVisible = false; } // 0x45FC04 void interfaceBarEndButtonsRenderGreenLights() { if (gInterfaceBarEndButtonsIsVisible) { buttonEnable(gEndTurnButton); buttonEnable(gEndCombatButton); FrmImage lightsFrmImage; // endltgrn.frm - green lights around end turn/combat window int lightsFid = buildFid(OBJ_TYPE_INTERFACE, 109, 0, 0, 0); if (!lightsFrmImage.lock(lightsFid)) { return; } soundPlayFile("icombat2"); blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * 640 + 580, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } } // 0x45FC98 void interfaceBarEndButtonsRenderRedLights() { if (gInterfaceBarEndButtonsIsVisible) { buttonDisable(gEndTurnButton); buttonDisable(gEndCombatButton); FrmImage lightsFrmImage; // endltred.frm - red lights around end turn/combat window int lightsFid = buildFid(OBJ_TYPE_INTERFACE, 110, 0, 0, 0); if (!lightsFrmImage.lock(lightsFid)) { return; } soundPlayFile("icombat1"); blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * 640 + 580, 640); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect); } } // NOTE: Inlined. // // 0x45FD2C static int intface_init_items() { // FIXME: For unknown reason these values initialized with -1. It's never // checked for -1, so I have no explanation for this. gInterfaceItemStates[HAND_LEFT].item = (Object*)-1; gInterfaceItemStates[HAND_RIGHT].item = (Object*)-1; return 0; } // 0x45FD88 static int interfaceBarRefreshMainAction() { if (gInterfaceBarWindow == -1) { return -1; } buttonEnable(gSingleAttackButton); InterfaceItemState* itemState = &(gInterfaceItemStates[gInterfaceCurrentHand]); int actionPoints = -1; if (itemState->isDisabled == 0) { memcpy(_itemButtonUp, _itemButtonNormalFrmImage.getData(), sizeof(_itemButtonUp)); memcpy(_itemButtonDown, _itemButtonPressedFrmImage.getData(), sizeof(_itemButtonDown)); if (itemState->isWeapon == 0) { int fid; if (_proto_action_can_use_on(itemState->item->pid)) { // USE ON fid = buildFid(OBJ_TYPE_INTERFACE, 294, 0, 0, 0); } else if (_obj_action_can_use(itemState->item)) { // USE fid = buildFid(OBJ_TYPE_INTERFACE, 292, 0, 0, 0); } else { fid = -1; } if (fid != -1) { FrmImage useTextFrmImage; if (useTextFrmImage.lock(fid)) { int width = useTextFrmImage.getWidth(); int height = useTextFrmImage.getHeight(); unsigned char* data = useTextFrmImage.getData(); blitBufferToBufferTrans(data, width, height, width, _itemButtonUp + 188 * 7 + 181 - width, 188); _dark_trans_buf_to_buf(data, width, height, width, _itemButtonDown, 181 - width + 1, 5, 188, 59641); } actionPoints = itemGetActionPointCost(gDude, itemState->primaryHitMode, false); } } else { int primaryFid = -1; int bullseyeFid = -1; int hitMode = -1; // NOTE: This value is decremented at 0x45FEAC, probably to build // jump table. switch (itemState->action) { case INTERFACE_ITEM_ACTION_PRIMARY_AIMING: bullseyeFid = buildFid(OBJ_TYPE_INTERFACE, 288, 0, 0, 0); // FALLTHROUGH case INTERFACE_ITEM_ACTION_PRIMARY: hitMode = itemState->primaryHitMode; break; case INTERFACE_ITEM_ACTION_SECONDARY_AIMING: bullseyeFid = buildFid(OBJ_TYPE_INTERFACE, 288, 0, 0, 0); // FALLTHROUGH case INTERFACE_ITEM_ACTION_SECONDARY: hitMode = itemState->secondaryHitMode; break; case INTERFACE_ITEM_ACTION_RELOAD: actionPoints = itemGetActionPointCost(gDude, gInterfaceCurrentHand == HAND_LEFT ? HIT_MODE_LEFT_WEAPON_RELOAD : HIT_MODE_RIGHT_WEAPON_RELOAD, false); primaryFid = buildFid(OBJ_TYPE_INTERFACE, 291, 0, 0, 0); break; } if (bullseyeFid != -1) { FrmImage bullseyeFrmImage; if (bullseyeFrmImage.lock(bullseyeFid)) { int width = bullseyeFrmImage.getWidth(); int height = bullseyeFrmImage.getHeight(); unsigned char* data = bullseyeFrmImage.getData(); blitBufferToBufferTrans(data, width, height, width, _itemButtonUp + 188 * (60 - height) + (181 - width), 188); int v9 = 60 - height - 2; if (v9 < 0) { v9 = 0; height -= 2; } _dark_trans_buf_to_buf(data, width, height, width, _itemButtonDown, 181 - width + 1, v9, 188, 59641); } } if (hitMode != -1) { actionPoints = weaponGetActionPointCost(gDude, hitMode, bullseyeFid != -1); int id; int anim = critterGetAnimationForHitMode(gDude, hitMode); switch (anim) { case ANIM_THROW_PUNCH: switch (hitMode) { case HIT_MODE_STRONG_PUNCH: id = 432; // strong punch break; case HIT_MODE_HAMMER_PUNCH: id = 425; // hammer punch break; case HIT_MODE_HAYMAKER: id = 428; // lightning punch break; case HIT_MODE_JAB: id = 421; // chop punch break; case HIT_MODE_PALM_STRIKE: id = 423; // dragon punch break; case HIT_MODE_PIERCING_STRIKE: id = 424; // force punch break; default: id = 42; // punch break; } break; case ANIM_KICK_LEG: switch (hitMode) { case HIT_MODE_STRONG_KICK: id = 430; // skick.frm - strong kick text break; case HIT_MODE_SNAP_KICK: id = 431; // snapkick.frm - snap kick text break; case HIT_MODE_POWER_KICK: id = 429; // cm_pwkck.frm - roundhouse kick text break; case HIT_MODE_HIP_KICK: id = 426; // hipk.frm - kip kick text break; case HIT_MODE_HOOK_KICK: id = 427; // cm_hookk.frm - jump kick text break; case HIT_MODE_PIERCING_KICK: // cm_prckk.frm - death blossom kick text id = 422; break; default: id = 41; // kick.frm - kick text break; } break; case ANIM_THROW_ANIM: id = 117; // throw break; case ANIM_THRUST_ANIM: id = 45; // thrust break; case ANIM_SWING_ANIM: id = 44; // swing break; case ANIM_FIRE_SINGLE: id = 43; // single break; case ANIM_FIRE_BURST: case ANIM_FIRE_CONTINUOUS: id = 40; // burst break; } primaryFid = buildFid(OBJ_TYPE_INTERFACE, id, 0, 0, 0); } if (primaryFid != -1) { FrmImage primaryFrmImage; if (primaryFrmImage.lock(primaryFid)) { int width = primaryFrmImage.getWidth(); int height = primaryFrmImage.getHeight(); unsigned char* data = primaryFrmImage.getData(); blitBufferToBufferTrans(data, width, height, width, _itemButtonUp + 188 * 7 + 181 - width, 188); _dark_trans_buf_to_buf(data, width, height, width, _itemButtonDown, 181 - width + 1, 5, 188, 59641); } } } } if (actionPoints >= 0 && actionPoints < 10) { // movement point text int apFid = buildFid(OBJ_TYPE_INTERFACE, 289, 0, 0, 0); FrmImage apFrmImage; if (apFrmImage.lock(apFid)) { int width = apFrmImage.getWidth(); int height = apFrmImage.getHeight(); unsigned char* data = apFrmImage.getData(); blitBufferToBufferTrans(data, width, height, width, _itemButtonUp + 188 * (60 - height) + 7, 188); int v29 = 60 - height - 2; if (v29 < 0) { v29 = 0; height -= 2; } _dark_trans_buf_to_buf(data, width, height, width, _itemButtonDown, 7 + 1, v29, 188, 59641); int offset = width + 7; FrmImage apNumbersFrmImage; // movement point numbers - ten numbers 0 to 9, each 10 pixels wide. int apNumbersFid = buildFid(OBJ_TYPE_INTERFACE, 290, 0, 0, 0); if (apNumbersFrmImage.lock(apNumbersFid)) { int width = apNumbersFrmImage.getWidth(); int height = apNumbersFrmImage.getHeight(); unsigned char* data = apNumbersFrmImage.getData(); blitBufferToBufferTrans(data + actionPoints * 10, 10, height, width, _itemButtonUp + 188 * (60 - height) + 7 + offset, 188); int v40 = 60 - height - 2; if (v40 < 0) { v40 = 0; height -= 2; } _dark_trans_buf_to_buf(data + actionPoints * 10, 10, height, width, _itemButtonDown, offset + 7 + 1, v40, 188, 59641); } } } else { memcpy(_itemButtonUp, _itemButtonDisabledFrmImage.getData(), sizeof(_itemButtonUp)); memcpy(_itemButtonDown, _itemButtonDisabledFrmImage.getData(), sizeof(_itemButtonDown)); } if (itemState->itemFid != -1) { FrmImage itemFrmImage; if (itemFrmImage.lock(itemState->itemFid)) { int width = itemFrmImage.getWidth(); int height = itemFrmImage.getHeight(); unsigned char* data = itemFrmImage.getData(); int v46 = (188 - width) / 2; int v47 = (67 - height) / 2 - 2; blitBufferToBufferTrans(data, width, height, width, _itemButtonUp + 188 * ((67 - height) / 2) + v46, 188); if (v47 < 0) { v47 = 0; height -= 2; } _dark_trans_buf_to_buf(data, width, height, width, _itemButtonDown, v46 + 1, v47, 188, 63571); } } if (!gInterfaceBarInitialized) { _intface_update_ammo_lights(); windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarMainActionRect); if (itemState->isDisabled != 0) { buttonDisable(gSingleAttackButton); } else { buttonEnable(gSingleAttackButton); } } return 0; } // 0x460658 static int _intface_redraw_items_callback(Object* a1, Object* a2) { interfaceBarRefreshMainAction(); return 0; } // 0x460660 static int _intface_change_fid_callback(Object* a1, Object* a2) { gInterfaceBarSwapHandsInProgress = false; return 0; } // 0x46066C static void interfaceBarSwapHandsAnimatePutAwayTakeOutSequence(int previousWeaponAnimationCode, int weaponAnimationCode) { gInterfaceBarSwapHandsInProgress = true; reg_anim_clear(gDude); reg_anim_begin(ANIMATION_REQUEST_RESERVED); animationRegisterSetLightDistance(gDude, 4, 0); if (previousWeaponAnimationCode != 0) { const char* sfx = sfxBuildCharName(gDude, ANIM_PUT_AWAY, CHARACTER_SOUND_EFFECT_UNUSED); animationRegisterPlaySoundEffect(gDude, sfx, 0); animationRegisterAnimate(gDude, ANIM_PUT_AWAY, 0); } // TODO: Get rid of cast. animationRegisterCallbackForced(NULL, NULL, (AnimationCallback*)_intface_redraw_items_callback, -1); Object* item = gInterfaceItemStates[gInterfaceCurrentHand].item; if (item != NULL && item->lightDistance > 4) { animationRegisterSetLightDistance(gDude, item->lightDistance, 0); } if (weaponAnimationCode != 0) { animationRegisterTakeOutWeapon(gDude, weaponAnimationCode, -1); } else { int fid = buildFid(OBJ_TYPE_CRITTER, gDude->fid & 0xFFF, ANIM_STAND, 0, gDude->rotation + 1); animationRegisterSetFid(gDude, fid, -1); } // TODO: Get rid of cast. animationRegisterCallbackForced(NULL, NULL, (AnimationCallback*)_intface_change_fid_callback, -1); if (reg_anim_end() == -1) { return; } bool interfaceBarWasEnabled = gInterfaceBarEnabled; interfaceBarDisable(); _gmouse_disable(0); gameMouseSetCursor(MOUSE_CURSOR_WAIT_WATCH); while (gInterfaceBarSwapHandsInProgress) { if (_game_user_wants_to_quit) { break; } inputGetInput(); } gameMouseSetCursor(MOUSE_CURSOR_NONE); _gmouse_enable(); if (interfaceBarWasEnabled) { interfaceBarEnable(); } } // 0x4607E0 static int endTurnButtonInit() { int fid; if (gInterfaceBarWindow == -1) { return -1; } if (!gInterfaceBarEndButtonsIsVisible) { return -1; } fid = buildFid(OBJ_TYPE_INTERFACE, 105, 0, 0, 0); if (!_endTurnButtonNormalFrmImage.lock(fid)) { return -1; } fid = buildFid(OBJ_TYPE_INTERFACE, 106, 0, 0, 0); if (!_endTurnButtonPressedFrmImage.lock(fid)) { return -1; } gEndTurnButton = buttonCreate(gInterfaceBarWindow, 590, 43, 38, 22, -1, -1, -1, 32, _endTurnButtonNormalFrmImage.getData(), _endTurnButtonPressedFrmImage.getData(), NULL, 0); if (gEndTurnButton == -1) { return -1; } _win_register_button_disable(gEndTurnButton, _endTurnButtonNormalFrmImage.getData(), _endTurnButtonNormalFrmImage.getData(), _endTurnButtonNormalFrmImage.getData()); buttonSetCallbacks(gEndTurnButton, _gsound_med_butt_press, _gsound_med_butt_release); return 0; } // 0x4608C4 static int endTurnButtonFree() { if (gInterfaceBarWindow == -1) { return -1; } if (gEndTurnButton != -1) { buttonDestroy(gEndTurnButton); gEndTurnButton = -1; } _endTurnButtonNormalFrmImage.unlock(); _endTurnButtonPressedFrmImage.unlock(); return 0; } // 0x460940 static int endCombatButtonInit() { int fid; if (gInterfaceBarWindow == -1) { return -1; } if (!gInterfaceBarEndButtonsIsVisible) { return -1; } fid = buildFid(OBJ_TYPE_INTERFACE, 107, 0, 0, 0); if (!_endCombatButtonNormalFrmImage.lock(fid)) { return -1; } fid = buildFid(OBJ_TYPE_INTERFACE, 108, 0, 0, 0); if (!_endCombatButtonPressedFrmImage.lock(fid)) { return -1; } gEndCombatButton = buttonCreate(gInterfaceBarWindow, 590, 65, 38, 22, -1, -1, -1, 13, _endCombatButtonNormalFrmImage.getData(), _endCombatButtonPressedFrmImage.getData(), NULL, 0); if (gEndCombatButton == -1) { return -1; } _win_register_button_disable(gEndCombatButton, _endCombatButtonNormalFrmImage.getData(), _endCombatButtonNormalFrmImage.getData(), _endCombatButtonNormalFrmImage.getData()); buttonSetCallbacks(gEndCombatButton, _gsound_med_butt_press, _gsound_med_butt_release); return 0; } // 0x460A24 static int endCombatButtonFree() { if (gInterfaceBarWindow == -1) { return -1; } if (gEndCombatButton != -1) { buttonDestroy(gEndCombatButton); gEndCombatButton = -1; } _endCombatButtonNormalFrmImage.unlock(); _endCombatButtonPressedFrmImage.unlock(); return 0; } // 0x460AA0 static void interfaceUpdateAmmoBar(int x, int ratio) { if ((ratio & 1) != 0) { ratio -= 1; } unsigned char* dest = gInterfaceWindowBuffer + 640 * 26 + x; for (int index = 70; index > ratio; index--) { *dest = 14; dest += 640; } while (ratio > 0) { *dest = 196; dest += 640; *dest = 14; dest += 640; ratio -= 2; } if (!gInterfaceBarInitialized) { Rect rect; rect.left = x; rect.top = 26; rect.right = x + 1; rect.bottom = 26 + 70; windowRefreshRect(gInterfaceBarWindow, &rect); } } // 0x460B20 static int _intface_item_reload() { if (gInterfaceBarWindow == -1) { return -1; } bool v0 = false; while (weaponAttemptReload(gDude, gInterfaceItemStates[gInterfaceCurrentHand].item) != -1) { v0 = true; } interfaceCycleItemAction(); interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT); if (!v0) { return -1; } const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, gInterfaceItemStates[gInterfaceCurrentHand].item, HIT_MODE_RIGHT_WEAPON_PRIMARY, NULL); soundPlayFile(sfx); return 0; } // Renders hit points. // // [delay] is an animation delay. // [previousValue] is only meaningful for animation. // [offset] = 0 - grey, 120 - yellow, 240 - red. // // 0x460BA0 static void interfaceRenderCounter(int x, int y, int previousValue, int value, int offset, int delay) { if (value > 999) { value = 999; } else if (value < -999) { value = -999; } unsigned char* numbers = _numbersFrmImage.getData() + offset; unsigned char* dest = gInterfaceWindowBuffer + 640 * y; unsigned char* downSrc = numbers + 90; unsigned char* upSrc = numbers + 99; unsigned char* minusSrc = numbers + 108; unsigned char* plusSrc = numbers + 114; unsigned char* signDest = dest + x; unsigned char* hundredsDest = dest + x + 6; unsigned char* tensDest = dest + x + 6 + 9; unsigned char* onesDest = dest + x + 6 + 9 * 2; int normalizedSign; int normalizedValue; if (gInterfaceBarInitialized || delay == 0) { normalizedSign = value >= 0 ? 1 : -1; normalizedValue = abs(value); } else { normalizedSign = previousValue >= 0 ? 1 : -1; normalizedValue = previousValue; } int ones = normalizedValue % 10; int tens = (normalizedValue / 10) % 10; int hundreds = normalizedValue / 100; blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, 640); blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, 640); blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, 640); blitBufferToBuffer(normalizedSign >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, 640); if (!gInterfaceBarInitialized) { Rect numbersRect = { x, y, x + 33, y + 17 }; windowRefreshRect(gInterfaceBarWindow, &numbersRect); if (delay != 0) { int change = value - previousValue >= 0 ? 1 : -1; int v14 = previousValue >= 0 ? 1 : -1; int v49 = change * v14; while (previousValue != value) { if ((hundreds | tens | ones) == 0) { v49 = 1; } blitBufferToBuffer(upSrc, 9, 17, 360, onesDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); ones += v49; if (ones > 9 || ones < 0) { blitBufferToBuffer(upSrc, 9, 17, 360, tensDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); tens += v49; ones -= 10 * v49; if (tens == 10 || tens == -1) { blitBufferToBuffer(upSrc, 9, 17, 360, hundredsDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); hundreds += v49; tens -= 10 * v49; if (hundreds == 10 || hundreds == -1) { hundreds -= 10 * v49; } blitBufferToBuffer(downSrc, 9, 17, 360, hundredsDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); } blitBufferToBuffer(downSrc, 9, 17, 360, tensDest, 640); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); } blitBufferToBuffer(downSrc, 9, 17, 360, onesDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); previousValue += change; blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, 640); blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, 640); blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, 640); blitBufferToBuffer(previousValue >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, 640); _mouse_info(); gameMouseRefresh(); inputBlockForTocks(delay); windowRefreshRect(gInterfaceBarWindow, &numbersRect); } } } } // NOTE: Inlined. // // 0x461128 static int intface_fatal_error(int rc) { interfaceFree(); return rc; } // 0x461134 static int indicatorBarInit() { int oldFont = fontGetCurrent(); if (gIndicatorBarWindow != -1) { return 0; } MessageList messageList; MessageListItem messageListItem; int rc = 0; if (!messageListInit(&messageList)) { rc = -1; } char path[COMPAT_MAX_PATH]; sprintf(path, "%s%s", asc_5186C8, "intrface.msg"); if (rc != -1) { if (!messageListLoad(&messageList, path)) { rc = -1; } } if (rc == -1) { debugPrint("\nINTRFACE: Error indicator box messages! **\n"); return -1; } FrmImage indicatorBoxFrmImage; int indicatorBoxFid = buildFid(OBJ_TYPE_INTERFACE, 126, 0, 0, 0); if (!indicatorBoxFrmImage.lock(indicatorBoxFid)) { debugPrint("\nINTRFACE: Error initializing indicator box graphics! **\n"); messageListFree(&messageList); return -1; } for (int index = 0; index < INDICATOR_COUNT; index++) { IndicatorDescription* indicatorDescription = &(gIndicatorDescriptions[index]); indicatorDescription->data = (unsigned char*)internal_malloc(INDICATOR_BOX_WIDTH * INDICATOR_BOX_HEIGHT); if (indicatorDescription->data == NULL) { debugPrint("\nINTRFACE: Error initializing indicator box graphics! **"); while (--index >= 0) { internal_free(gIndicatorDescriptions[index].data); } messageListFree(&messageList); return -1; } } fontSetCurrent(101); for (int index = 0; index < INDICATOR_COUNT; index++) { IndicatorDescription* indicator = &(gIndicatorDescriptions[index]); char text[1024]; strcpy(text, getmsg(&messageList, &messageListItem, indicator->title)); int color = indicator->isBad ? _colorTable[31744] : _colorTable[992]; memcpy(indicator->data, indicatorBoxFrmImage.getData(), INDICATOR_BOX_WIDTH * INDICATOR_BOX_HEIGHT); // NOTE: For unknown reason it uses 24 as a height of the box to center // the title. One explanation is that these boxes were redesigned, but // this value was not changed. On the other hand 24 is // [INDICATOR_BOX_HEIGHT] + [INDICATOR_BOX_CONNECTOR_WIDTH]. Maybe just // a coincidence. I guess we'll never find out. int y = (24 - fontGetLineHeight()) / 2; int x = (INDICATOR_BOX_WIDTH - fontGetStringWidth(text)) / 2; fontDrawText(indicator->data + INDICATOR_BOX_WIDTH * y + x, text, INDICATOR_BOX_WIDTH, INDICATOR_BOX_WIDTH, color); } gIndicatorBarIsVisible = true; indicatorBarRefresh(); messageListFree(&messageList); fontSetCurrent(oldFont); return 0; } // 0x461454 static void interfaceBarFree() { if (gIndicatorBarWindow != -1) { windowDestroy(gIndicatorBarWindow); gIndicatorBarWindow = -1; } for (int index = 0; index < INDICATOR_COUNT; index++) { IndicatorDescription* indicatorBoxDescription = &(gIndicatorDescriptions[index]); if (indicatorBoxDescription->data != NULL) { internal_free(indicatorBoxDescription->data); indicatorBoxDescription->data = NULL; } } } // NOTE: This function is not referenced in the original code. // // 0x4614A0 static void indicatorBarReset() { if (gIndicatorBarWindow != -1) { windowDestroy(gIndicatorBarWindow); gIndicatorBarWindow = -1; } gIndicatorBarIsVisible = true; } // Updates indicator bar. // // 0x4614CC int indicatorBarRefresh() { if (gInterfaceBarWindow != -1 && gIndicatorBarIsVisible && !_intfaceHidden) { for (int index = 0; index < INDICATOR_SLOTS_COUNT; index++) { gIndicatorSlots[index] = -1; } int count = 0; if (dudeHasState(DUDE_STATE_SNEAKING)) { if (indicatorBarAdd(INDICATOR_SNEAK)) { ++count; } } if (dudeHasState(DUDE_STATE_LEVEL_UP_AVAILABLE)) { if (indicatorBarAdd(INDICATOR_LEVEL)) { ++count; } } if (dudeHasState(DUDE_STATE_ADDICTED)) { if (indicatorBarAdd(INDICATOR_ADDICT)) { ++count; } } if (critterGetPoison(gDude) > POISON_INDICATOR_THRESHOLD) { if (indicatorBarAdd(INDICATOR_POISONED)) { ++count; } } if (critterGetRadiation(gDude) > RADATION_INDICATOR_THRESHOLD) { if (indicatorBarAdd(INDICATOR_RADIATED)) { ++count; } } if (count > 1) { qsort(gIndicatorSlots, count, sizeof(*gIndicatorSlots), indicatorBoxCompareByPosition); } if (gIndicatorBarWindow != -1) { windowDestroy(gIndicatorBarWindow); gIndicatorBarWindow = -1; } if (count != 0) { Rect interfaceBarWindowRect; windowGetRect(gInterfaceBarWindow, &interfaceBarWindowRect); gIndicatorBarWindow = windowCreate(interfaceBarWindowRect.left, screenGetHeight() - INTERFACE_BAR_HEIGHT - INDICATOR_BOX_HEIGHT, (INDICATOR_BOX_WIDTH - INDICATOR_BOX_CONNECTOR_WIDTH) * count, INDICATOR_BOX_HEIGHT, _colorTable[0], 0); indicatorBarRender(count); windowRefresh(gIndicatorBarWindow); } return count; } if (gIndicatorBarWindow != -1) { windowDestroy(gIndicatorBarWindow); gIndicatorBarWindow = -1; } return 0; } // 0x461624 static int indicatorBoxCompareByPosition(const void* a, const void* b) { int indicatorBox1 = *(int*)a; int indicatorBox2 = *(int*)b; if (indicatorBox1 == indicatorBox2) { return 0; } else if (indicatorBox1 < indicatorBox2) { return -1; } else { return 1; } } // Renders indicator boxes into the indicator bar window. // // 0x461648 static void indicatorBarRender(int count) { if (gIndicatorBarWindow == -1) { return; } if (count == 0) { return; } int windowWidth = windowGetWidth(gIndicatorBarWindow); unsigned char* windowBuffer = windowGetBuffer(gIndicatorBarWindow); // The initial number of connections is 2 - one is first box to the screen // boundary, the other is female socket (initially empty). Every displayed // box adds one more connection (it is "plugged" into previous box and // exposes it's own empty female socket). int connections = 2; // The width of displayed indicator boxes as if there were no connections. int unconnectedIndicatorsWidth = 0; // The X offset to display next box. int x = 0; // The first box is connected to the screen boundary, so we have to clamp // male connectors on the left. int connectorWidthCompensation = INDICATOR_BOX_CONNECTOR_WIDTH; for (int index = 0; index < count; index++) { int indicator = gIndicatorSlots[index]; IndicatorDescription* indicatorDescription = &(gIndicatorDescriptions[indicator]); blitBufferToBufferTrans(indicatorDescription->data + connectorWidthCompensation, INDICATOR_BOX_WIDTH - connectorWidthCompensation, INDICATOR_BOX_HEIGHT, INDICATOR_BOX_WIDTH, windowBuffer + x, windowWidth); connectorWidthCompensation = 0; unconnectedIndicatorsWidth += INDICATOR_BOX_WIDTH; x = unconnectedIndicatorsWidth - INDICATOR_BOX_CONNECTOR_WIDTH * connections; connections++; } } // Adds indicator to the indicator bar. // // Returns `true` if indicator was added, or `false` if there is no available // space in the indicator bar. // // 0x4616F0 static bool indicatorBarAdd(int indicator) { for (int index = 0; index < INDICATOR_SLOTS_COUNT; index++) { if (gIndicatorSlots[index] == -1) { gIndicatorSlots[index] = indicator; return true; } } debugPrint("\nINTRFACE: no free bar box slots!\n"); return false; } // 0x461740 bool indicatorBarShow() { bool oldIsVisible = gIndicatorBarIsVisible; gIndicatorBarIsVisible = true; indicatorBarRefresh(); return oldIsVisible; } // 0x461760 bool indicatorBarHide() { bool oldIsVisible = gIndicatorBarIsVisible; gIndicatorBarIsVisible = false; indicatorBarRefresh(); return oldIsVisible; } } // namespace fallout