#include "mainmenu.h" #include #include "art.h" #include "color.h" #include "draw.h" #include "game.h" #include "game_sound.h" #include "input.h" #include "kb.h" #include "mouse.h" #include "palette.h" #include "preferences.h" #include "sfall_config.h" #include "svga.h" #include "text_font.h" #include "version.h" #include "window_manager.h" namespace fallout { #define MAIN_MENU_WINDOW_WIDTH 640 #define MAIN_MENU_WINDOW_HEIGHT 480 typedef enum MainMenuButton { MAIN_MENU_BUTTON_INTRO, MAIN_MENU_BUTTON_NEW_GAME, MAIN_MENU_BUTTON_LOAD_GAME, MAIN_MENU_BUTTON_OPTIONS, MAIN_MENU_BUTTON_CREDITS, MAIN_MENU_BUTTON_EXIT, MAIN_MENU_BUTTON_COUNT, } MainMenuButton; static int main_menu_fatal_error(); static void main_menu_play_sound(const char* fileName); // 0x5194F0 static int gMainMenuWindow = -1; // 0x5194F4 static unsigned char* gMainMenuWindowBuffer = NULL; // 0x519504 static bool _in_main_menu = false; // 0x519508 static bool gMainMenuWindowInitialized = false; // 0x51950C static unsigned int gMainMenuScreensaverDelay = 120000; // 0x519510 static const int gMainMenuButtonKeyBindings[MAIN_MENU_BUTTON_COUNT] = { KEY_LOWERCASE_I, // intro KEY_LOWERCASE_N, // new game KEY_LOWERCASE_L, // load game KEY_LOWERCASE_O, // options KEY_LOWERCASE_C, // credits KEY_LOWERCASE_E, // exit }; // 0x519528 static const int _return_values[MAIN_MENU_BUTTON_COUNT] = { MAIN_MENU_INTRO, MAIN_MENU_NEW_GAME, MAIN_MENU_LOAD_GAME, MAIN_MENU_OPTIONS, MAIN_MENU_CREDITS, MAIN_MENU_EXIT, }; // 0x614840 static int gMainMenuButtons[MAIN_MENU_BUTTON_COUNT]; // 0x614858 static bool gMainMenuWindowHidden; static FrmImage _mainMenuBackgroundFrmImage; static FrmImage _mainMenuButtonNormalFrmImage; static FrmImage _mainMenuButtonPressedFrmImage; // 0x481650 int mainMenuWindowInit() { int fid; MessageListItem msg; int len; if (gMainMenuWindowInitialized) { return 0; } colorPaletteLoad("color.pal"); int mainMenuWindowX = (screenGetWidth() - MAIN_MENU_WINDOW_WIDTH) / 2; int mainMenuWindowY = (screenGetHeight() - MAIN_MENU_WINDOW_HEIGHT) / 2; gMainMenuWindow = windowCreate(mainMenuWindowX, mainMenuWindowY, MAIN_MENU_WINDOW_WIDTH, MAIN_MENU_WINDOW_HEIGHT, 0, WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP); if (gMainMenuWindow == -1) { // NOTE: Uninline. return main_menu_fatal_error(); } gMainMenuWindowBuffer = windowGetBuffer(gMainMenuWindow); // mainmenu.frm int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 140, 0, 0, 0); if (!_mainMenuBackgroundFrmImage.lock(backgroundFid)) { // NOTE: Uninline. return main_menu_fatal_error(); } blitBufferToBuffer(_mainMenuBackgroundFrmImage.getData(), 640, 480, 640, gMainMenuWindowBuffer, 640); _mainMenuBackgroundFrmImage.unlock(); int oldFont = fontGetCurrent(); fontSetCurrent(100); // SFALL: Allow to change font color/flags of copyright/version text // It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option // 0x010000 - change the color for version string only // 0x020000 - underline text (only for the version string) // 0x040000 - monospace font (only for the version string) int fontSettings = _colorTable[21091], fontSettingsSFall = 0; configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall); if (fontSettingsSFall && !(fontSettingsSFall & 0x010000)) fontSettings = fontSettingsSFall & 0xFF; // SFALL: Allow to move copyright text int offsetX = 0, offsetY = 0; configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX); configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY); // Copyright. msg.num = 20; if (messageListGetItem(&gMiscMessageList, &msg)) { windowDrawText(gMainMenuWindow, msg.text, 0, offsetX + 15, offsetY + 460, fontSettings | 0x06000000); } // SFALL: Make sure font settings are applied when using 0x010000 flag if (fontSettingsSFall) fontSettings = fontSettingsSFall; // TODO: Allow to move version text // Version. char version[VERSION_MAX]; versionGetVersion(version, sizeof(version)); len = fontGetStringWidth(version); windowDrawText(gMainMenuWindow, version, 0, 615 - len, 460, fontSettings | 0x06000000); // menuup.frm fid = buildFid(OBJ_TYPE_INTERFACE, 299, 0, 0, 0); if (!_mainMenuButtonNormalFrmImage.lock(fid)) { // NOTE: Uninline. return main_menu_fatal_error(); } // menudown.frm fid = buildFid(OBJ_TYPE_INTERFACE, 300, 0, 0, 0); if (!_mainMenuButtonPressedFrmImage.lock(fid)) { // NOTE: Uninline. return main_menu_fatal_error(); } for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { gMainMenuButtons[index] = -1; } // SFALL: Allow to move menu buttons offsetX = offsetY = 0; configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX); configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY); for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { gMainMenuButtons[index] = buttonCreate(gMainMenuWindow, offsetX + 30, offsetY + 19 + index * 42 - index, 26, 26, -1, -1, 1111, gMainMenuButtonKeyBindings[index], _mainMenuButtonNormalFrmImage.getData(), _mainMenuButtonPressedFrmImage.getData(), 0, BUTTON_FLAG_TRANSPARENT); if (gMainMenuButtons[index] == -1) { // NOTE: Uninline. return main_menu_fatal_error(); } buttonSetMask(gMainMenuButtons[index], _mainMenuButtonNormalFrmImage.getData()); } fontSetCurrent(104); // SFALL: Allow to change font color of buttons fontSettings = _colorTable[21091]; fontSettingsSFall = 0; configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall); if (fontSettingsSFall) fontSettings = fontSettingsSFall & 0xFF; for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { msg.num = 9 + index; if (messageListGetItem(&gMiscMessageList, &msg)) { len = fontGetStringWidth(msg.text); fontDrawText(gMainMenuWindowBuffer + offsetX + 640 * (offsetY + 42 * index - index + 20) + 126 - (len / 2), msg.text, 640 - (126 - (len / 2)) - 1, 640, fontSettings); } } fontSetCurrent(oldFont); gMainMenuWindowInitialized = true; gMainMenuWindowHidden = true; return 0; } // 0x481968 void mainMenuWindowFree() { if (!gMainMenuWindowInitialized) { return; } for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { // FIXME: Why it tries to free only invalid buttons? if (gMainMenuButtons[index] == -1) { buttonDestroy(gMainMenuButtons[index]); } } _mainMenuButtonPressedFrmImage.unlock(); _mainMenuButtonNormalFrmImage.unlock(); if (gMainMenuWindow != -1) { windowDestroy(gMainMenuWindow); } gMainMenuWindowInitialized = false; } // 0x481A00 void mainMenuWindowHide(bool animate) { if (!gMainMenuWindowInitialized) { return; } if (gMainMenuWindowHidden) { return; } soundContinueAll(); if (animate) { paletteFadeTo(gPaletteBlack); soundContinueAll(); } windowHide(gMainMenuWindow); gMainMenuWindowHidden = true; } // 0x481A48 void mainMenuWindowUnhide(bool animate) { if (!gMainMenuWindowInitialized) { return; } if (!gMainMenuWindowHidden) { return; } windowShow(gMainMenuWindow); if (animate) { colorPaletteLoad("color.pal"); paletteFadeTo(_cmap); } gMainMenuWindowHidden = false; } // 0x481AA8 int _main_menu_is_enabled() { return 1; } // 0x481AEC int mainMenuWindowHandleEvents() { _in_main_menu = true; bool oldCursorIsHidden = cursorIsHidden(); if (oldCursorIsHidden) { mouseShowCursor(); } unsigned int tick = getTicks(); int rc = -1; while (rc == -1) { sharedFpsLimiter.mark(); int keyCode = inputGetInput(); for (int buttonIndex = 0; buttonIndex < MAIN_MENU_BUTTON_COUNT; buttonIndex++) { if (keyCode == gMainMenuButtonKeyBindings[buttonIndex] || keyCode == toupper(gMainMenuButtonKeyBindings[buttonIndex])) { // NOTE: Uninline. main_menu_play_sound("nmselec1"); rc = _return_values[buttonIndex]; if (buttonIndex == MAIN_MENU_BUTTON_CREDITS && (gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT] != KEY_STATE_UP || gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] != KEY_STATE_UP)) { rc = MAIN_MENU_QUOTES; } break; } } if (rc == -1) { if (keyCode == KEY_CTRL_R) { rc = MAIN_MENU_SELFRUN; continue; } else if (keyCode == KEY_PLUS || keyCode == KEY_EQUAL) { brightnessIncrease(); } else if (keyCode == KEY_MINUS || keyCode == KEY_UNDERSCORE) { brightnessDecrease(); } else if (keyCode == KEY_UPPERCASE_D || keyCode == KEY_LOWERCASE_D) { rc = MAIN_MENU_SCREENSAVER; continue; } else if (keyCode == 1111) { if (!(mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT)) { // NOTE: Uninline. main_menu_play_sound("nmselec0"); } continue; } } if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit == 3) { rc = MAIN_MENU_EXIT; // NOTE: Uninline. main_menu_play_sound("nmselec1"); break; } else if (_game_user_wants_to_quit == 2) { _game_user_wants_to_quit = 0; } else { if (getTicksSince(tick) >= gMainMenuScreensaverDelay) { rc = MAIN_MENU_TIMEOUT; } } renderPresent(); sharedFpsLimiter.throttle(); } if (oldCursorIsHidden) { mouseHideCursor(); } _in_main_menu = false; return rc; } // NOTE: Inlined. // // 0x481C88 static int main_menu_fatal_error() { mainMenuWindowFree(); return -1; } // NOTE: Inlined. // // 0x481C94 static void main_menu_play_sound(const char* fileName) { soundPlayFile(fileName); } } // namespace fallout