Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,920 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/riddle/gui/game_menu.h"
#include "m4/riddle/vars.h"
#include "graphics/thumbnail.h"
#include "m4/adv_r/other.h"
#include "m4/adv_r/adv_player.h"
#include "m4/core/imath.h"
#include "m4/gui/gui_event.h"
#include "m4/gui/hotkeys.h"
#include "m4/gui/gui_sys.h"
#include "m4/gui/gui_vmng.h"
#include "m4/mem/mem.h"
#include "m4/platform/keys.h"
#include "m4/m4.h"
#include "common/config-manager.h"
#include "m4/burger/gui/game_menu.h"
namespace M4 {
namespace Riddle {
namespace GUI {
/*-------------------- GAME MENU --------------------*/
#define GAME_MENU_X 212
#define GAME_MENU_Y 160
#define GM_TAG_QUIT 1
#define GM_QUIT_X 13
#define GM_QUIT_Y 31
#define GM_QUIT_W 26
#define GM_QUIT_H 26
#define GM_TAG_MAIN 2
#define GM_MAIN_X 108
#define GM_MAIN_Y 31
#define GM_MAIN_W 26
#define GM_MAIN_H 26
#define GM_TAG_OPTIONS 3
#define GM_OPTIONS_X 162
#define GM_OPTIONS_Y 31
#define GM_OPTIONS_W 26
#define GM_OPTIONS_H 26
#define GM_TAG_RESUME 4
#define GM_RESUME_X 54
#define GM_RESUME_Y 94
#define GM_RESUME_W 26
#define GM_RESUME_H 26
#define GM_TAG_SAVE 5
#define GM_SAVE_X 108
#define GM_SAVE_Y 94
#define GM_SAVE_W 26
#define GM_SAVE_H 26
#define GM_TAG_LOAD 6
#define GM_LOAD_X 162
#define GM_LOAD_Y 94
#define GM_LOAD_W 26
#define GM_LOAD_H 26
void GameMenu::show(RGB8 *myPalette) {
if (!_G(menuSystemInitialized)) {
guiMenu::initialize(myPalette);
}
// Load in the game menu sprites
if (!guiMenu::loadSprites("gamemenu", GM_TOTAL_SPRITES))
error("Error loading gamemenu");
_GM(gameMenu) = guiMenu::create(_GM(menuSprites)[GM_DIALOG_BOX],
GAME_MENU_X, GAME_MENU_Y, MENU_DEPTH | SF_GET_ALL | SF_BLOCK_ALL | SF_IMMOVABLE);
assert(_GM(gameMenu));
menuItemButton::add(_GM(gameMenu), GM_TAG_QUIT,
GM_QUIT_X, GM_QUIT_Y, GM_QUIT_W, GM_QUIT_H, cbQuitGame);
menuItemButton::add(_GM(gameMenu), GM_TAG_MAIN,
GM_MAIN_X, GM_MAIN_Y, GM_MAIN_W, GM_MAIN_H, cbMainMenu);
menuItemButton::add(_GM(gameMenu), GM_TAG_OPTIONS, GM_OPTIONS_X, GM_OPTIONS_Y, GM_OPTIONS_W, GM_OPTIONS_H, cbOptions);
menuItemButton::add(_GM(gameMenu), GM_TAG_RESUME, GM_RESUME_X, GM_RESUME_Y, GM_RESUME_W, GM_RESUME_H, cbResume);
if (!_GM(gameMenuFromMain)) {
menuItemButton::add(_GM(gameMenu), GM_TAG_SAVE, GM_SAVE_X, GM_SAVE_Y, GM_SAVE_W, GM_SAVE_H, cbSave);
} else {
menuItemButton::add(_GM(gameMenu), GM_TAG_SAVE, GM_SAVE_X, GM_SAVE_Y, GM_SAVE_W, GM_SAVE_H, cbSave, menuItemButton::BTN_TYPE_GM_GENERIC, true);
}
// See if there are any games to load
if (g_engine->savesExist()) {
menuItemButton::add(_GM(gameMenu), GM_TAG_LOAD, GM_LOAD_X, GM_LOAD_Y, GM_LOAD_W, GM_LOAD_H, cbLoad);
} else {
menuItemButton::add(_GM(gameMenu), GM_TAG_LOAD, GM_LOAD_X, GM_LOAD_Y, GM_LOAD_W, GM_LOAD_H, cbLoad, menuItemButton::BTN_TYPE_GM_GENERIC, true);
}
// Configure the game so pressing <esc> will cause the menu to disappear and the game to resume
guiMenu::configure(_GM(gameMenu), cbResume, cbResume);
vmng_screen_show((void *)_GM(gameMenu));
LockMouseSprite(0);
}
void GameMenu::destroyGameMenu() {
if (!_GM(gameMenu))
return;
// Remove the screen from the gui
vmng_screen_dispose(_GM(gameMenu));
// Destroy the menu resources
guiMenu::destroy(_GM(gameMenu));
// Unload the menu sprites
guiMenu::unloadSprites();
}
void GameMenu::cbQuitGame(void *, void *) {
// Destroy the game menu
destroyGameMenu();
// Shutdown the menu system
guiMenu::shutdown(false);
// Set the global that will cause the entire game to exit to dos
_G(kernel).going = false;
}
void GameMenu::cbMainMenu(void *, void *) {
// Destroy the game menu
destroyGameMenu();
if (!_GM(gameMenuFromMain)) {
// Save the game so we can resume from here if possible
if (_GM(interfaceWasVisible) && player_commands_allowed()) {
other_save_game_for_resurrection();
}
// Make sure the interface does not reappear
_GM(interfaceWasVisible) = false;
// Shutdown the menu system
guiMenu::shutdown(false);
} else {
guiMenu::shutdown(true);
}
// Go to the main menu
_G(game).setRoom(494);
}
void GameMenu::cbResume(void *, void *) {
// Destroy the game menu
destroyGameMenu();
// Shutdown the menu system
guiMenu::shutdown(true);
}
void GameMenu::cbOptions(void *, void *) {
// Destroy the game menu
destroyGameMenu();
_GM(buttonClosesDialog) = true;
// Create the options menu
OptionsMenu::show();
}
void GameMenu::cbSave(void *, void *) {
destroyGameMenu();
guiMenu::shutdown(true);
_GM(buttonClosesDialog) = true;
// Create the save game menu
g_engine->showSaveScreen();
}
void GameMenu::cbLoad(void *, void *) {
destroyGameMenu();
guiMenu::shutdown(true);
_GM(buttonClosesDialog) = true;
// Create the load game menu
g_engine->showLoadScreen(M4Engine::kLoadFromGameDialog);
}
/*-------------------- OPTIONS MENU --------------------*/
#define OPTIONS_MENU_X 212
#define OPTIONS_MENU_Y 160
#define OM_TAG_GAMEMENU 1
#define OM_GAMEMENU_X 14
#define OM_GAMEMENU_Y 94
#define OM_GAMEMENU_W 26
#define OM_GAMEMENU_H 26
#define OM_TAG_DIGI 2
#define OM_DIGI_X 70
#define OM_DIGI_Y 29
#define OM_DIGI_W 108
#define OM_DIGI_H 14
#define OM_TAG_MIDI 3
#define OM_MIDI_X 70
#define OM_MIDI_Y 72
#define OM_MIDI_W 108
#define OM_MIDI_H 14
#define OM_TAG_SCROLLING 4
#define OM_SCROLLING_X 131
#define OM_SCROLLING_Y 113
#define OM_SCROLLING_W 39
#define OM_SCROLLING_H 39
void OptionsMenu::show() {
// Load in the options menu sprites
if (!guiMenu::loadSprites("opmenu", OM_TOTAL_SPRITES))
error("Error loading opmenu");
_GM(opMenu) = guiMenu::create(_GM(menuSprites)[OM_DIALOG_BOX],
OPTIONS_MENU_X, OPTIONS_MENU_Y, MENU_DEPTH | SF_GET_ALL | SF_BLOCK_ALL | SF_IMMOVABLE);
assert(_GM(opMenu));
const int digiPercent = ConfMan.getInt("sfx_volume") * 100 / 256;
const int midiPercent = ConfMan.getInt("music_volume") * 100 / 256;
menuItemButton::add(_GM(opMenu), OM_TAG_GAMEMENU,
OM_GAMEMENU_X, OM_GAMEMENU_Y, OM_GAMEMENU_W, OM_GAMEMENU_H,
cbGameMenu, menuItemButton::BTN_TYPE_GM_GENERIC);
menuItemHSlider::add(_GM(opMenu), OM_TAG_DIGI,
OM_DIGI_X, OM_DIGI_Y, OM_DIGI_W, OM_DIGI_H,
digiPercent, (CALLBACK)cbSetDigi, true);
menuItemHSlider::add(_GM(opMenu), OM_TAG_MIDI,
OM_MIDI_X, OM_MIDI_Y, OM_MIDI_W, OM_MIDI_H,
midiPercent, (CALLBACK)cbSetMidi, true);
menuItemButton::add(_GM(opMenu), OM_TAG_SCROLLING,
OM_SCROLLING_X, OM_SCROLLING_Y, OM_SCROLLING_W, OM_SCROLLING_H, (CALLBACK)cbScrolling,
_G(kernel).cameraPans() ? menuItemButton::BTN_TYPE_OM_SCROLLING_ON :
menuItemButton::BTN_TYPE_OM_SCROLLING_OFF);
guiMenu::configure(_GM(opMenu), cbGameMenu, cbGameMenu);
vmng_screen_show((void *)_GM(opMenu));
LockMouseSprite(0);
}
void OptionsMenu::destroyOptionsMenu() {
// Remove the screen from the gui
vmng_screen_dispose(_GM(opMenu));
// Destroy the menu resources
guiMenu::destroy(_GM(opMenu));
// Unload the menu sprites
guiMenu::unloadSprites();
}
void OptionsMenu::cbGameMenu(void *, void *) {
destroyOptionsMenu();
GameMenu::show(nullptr);
}
void OptionsMenu::cbScrolling(M4::GUI::menuItemButton *myItem, M4::GUI::guiMenu *) {
_G(kernel).camera_pan_instant = myItem->buttonType ==
menuItemButton::BTN_TYPE_OM_SCROLLING_ON;
}
void OptionsMenu::cbSetDigi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *) {
ConfMan.setInt("sfx_volume", myItem->percent * 256 / 100);
g_engine->syncSoundSettings();
}
void OptionsMenu::cbSetMidi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *) {
ConfMan.setInt("music_volume", myItem->percent * 256 / 100);
g_engine->syncSoundSettings();
}
/*------------------- SAVE/LOAD METHODS ------------------*/
#define SAVE_LOAD_MENU_X 42
#define SAVE_LOAD_MENU_Y 155
#define SL_TITLE_X 111
#define SL_TITLE_Y 2
#define SL_TITLE_W 110
#define SL_TITLE_H 17
#define SL_THUMBNAIL_X 333
#define SL_THUMBNAIL_Y 5
#define SL_SAVELOAD_X 10
#define SL_SAVELOAD_Y 74
#define SL_SAVELOAD_W 26
#define SL_SAVELOAD_H 26
#define SL_SAVELOAD_LABEL_X 8
#define SL_SAVELOAD_LABEL_Y 103
#define SL_SAVELOAD_LABEL_W 30
#define SL_SAVELOAD_LABEL_H 14
#define SL_CANCEL_X 10
#define SL_CANCEL_Y 122
#define SL_CANCEL_W 26
#define SL_CANCEL_H 26
#define SL_SLIDER_X 305
#define SL_SLIDER_Y 21
#define SL_SLIDER_W 20
#define SL_SLIDER_H 140
#define SL_SCROLL_FIELD_X 46
#define SL_SCROLL_FIELD_Y 21
#define SL_SCROLL_LINE_W 258
#define SL_SCROLL_LINE_H 14
#define SL_SCROLL_FIELD_W 257
#define SL_SCROLL_FIELD_H 139
void SaveLoadMenu::show(RGB8 *myPalette, bool saveMenu) {
if (!_G(menuSystemInitialized))
guiMenu::initialize(myPalette);
// Load in the game menu sprites
if (!guiMenu::loadSprites("slmenu", GUI::SaveLoadMenuBase::SL_TOTAL_SPRITES)) {
return;
}
// Initialize some global vars
_GM(firstSlotIndex) = 0;
_GM(slotSelected) = -1;
_GM(saveLoadThumbNail) = nullptr;
_GM(thumbIndex) = 100;
_GM(currMenuIsSave) = saveMenu;
_GM(slMenu) = guiMenu::create(_GM(menuSprites)[GUI::SaveLoadMenuBase::SL_DIALOG_BOX],
SAVE_LOAD_MENU_X, SAVE_LOAD_MENU_Y,
MENU_DEPTH | SF_GET_ALL | SF_BLOCK_ALL | SF_IMMOVABLE);
if (!_GM(slMenu)) {
return;
}
if (_GM(currMenuIsSave)) {
menuItemMsg::msgAdd(_GM(slMenu), SL_TAG_SAVE_TITLE_LABEL,
SL_TITLE_X, SL_TITLE_Y, SL_TITLE_W, SL_TITLE_H);
menuItemButton::add(_GM(slMenu), SL_TAG_SAVE,
SL_SAVELOAD_X, SL_SAVELOAD_Y,
SL_SAVELOAD_W, SL_SAVELOAD_H, (CALLBACK)cbSave,
menuItemButton::BTN_TYPE_GM_GENERIC, true);
menuItemMsg::msgAdd(_GM(slMenu), SL_TAG_SAVE_LABEL,
SL_SAVELOAD_LABEL_X, SL_SAVELOAD_LABEL_Y,
SL_SAVELOAD_LABEL_W, SL_SAVELOAD_LABEL_H);
} else {
menuItemMsg::msgAdd(_GM(slMenu), SL_TAG_LOAD_TITLE_LABEL,
SL_TITLE_X, SL_TITLE_Y, SL_TITLE_W, SL_TITLE_H);
menuItemButton::add(_GM(slMenu), SL_TAG_LOAD,
SL_SAVELOAD_X, SL_SAVELOAD_Y,
SL_SAVELOAD_W, SL_SAVELOAD_H, (CALLBACK)cbSave,
menuItemButton::BTN_TYPE_GM_GENERIC, true);
menuItemMsg::msgAdd(_GM(slMenu), SL_TAG_LOAD_LABEL,
SL_SAVELOAD_LABEL_X, SL_SAVELOAD_LABEL_Y,
SL_SAVELOAD_LABEL_W, SL_SAVELOAD_LABEL_H);
}
menuItemButton::add(_GM(slMenu), SL_TAG_CANCEL, SL_CANCEL_X, SL_CANCEL_Y, SL_CANCEL_W, SL_CANCEL_H,
(CALLBACK)cbCancel, menuItemButton::BTN_TYPE_GM_GENERIC);
menuItemVSlider::add(_GM(slMenu), SL_TAG_VSLIDER, SL_SLIDER_X, SL_SLIDER_Y, SL_SLIDER_W, SL_SLIDER_H,
0, (CALLBACK)cbVSlider);
initializeSlotTables();
bool buttonGreyed;
ItemHandlerFunction i_handler;
if (_GM(currMenuIsSave)) {
buttonGreyed = false;
i_handler = (ItemHandlerFunction)menuItemButton::handler;
} else {
buttonGreyed = true;
i_handler = (ItemHandlerFunction)load_Handler;
}
for (int32 i = 0; i < MAX_SLOTS_SHOWN; i++) {
menuItemButton::add(_GM(slMenu), 1001 + i,
SL_SCROLL_FIELD_X, SL_SCROLL_FIELD_Y + i * SL_SCROLL_LINE_H,
SL_SCROLL_LINE_W, SL_SCROLL_LINE_H,
(CALLBACK)cbSlot, menuItemButton::BTN_TYPE_SL_TEXT,
buttonGreyed && (!_GM(slotInUse)[i]), true,
_GM(slotTitles)[i], i_handler);
}
if (_GM(currMenuIsSave)) {
// Create thumbnails. One in the original game format for displaying,
// and the other in the ScummVM format for actually using in the save files
_GM(saveLoadThumbNail) = menu_CreateThumbnail(&_GM(sizeofThumbData));
_GM(_thumbnail).free();
Graphics::createThumbnail(_GM(_thumbnail));
} else {
updateThumbnails(0, _GM(slMenu));
_GM(saveLoadThumbNail) = _GM(menuSprites)[GUI::SaveLoadMenuBase::SL_EMPTY_THUMB_25];
}
menuItemMsg::msgAdd(_GM(slMenu), SL_TAG_THUMBNAIL, SL_THUMBNAIL_X, SL_THUMBNAIL_Y,
SaveLoadMenuBase::SL_THUMBNAIL_W, SaveLoadMenuBase::SL_THUMBNAIL_H, false);
if (_GM(currMenuIsSave)) {
//<return> - if a slot has been selected, saves the game
//<esc> - cancels and returns to the game menu
guiMenu::configure(_GM(slMenu), (CALLBACK)cbSave, (CALLBACK)cbCancel);
} else {
//<return> - if a slot has been selected, loads the selected game
//<esc> - cancels and returns to the game menu
guiMenu::configure(_GM(slMenu), (CALLBACK)cbLoad, (CALLBACK)cbCancel);
}
vmng_screen_show((void *)_GM(slMenu));
LockMouseSprite(0);
}
void SaveLoadMenu::destroyMenu(bool saveMenu) {
if (!_GM(slMenu)) {
return;
}
// Determine whether the screen was the SAVE or the LOAD menu
if (saveMenu) {
// If SAVE, there should be a thumbnail to unload
if (_GM(saveLoadThumbNail)) {
DisposeHandle(_GM(saveLoadThumbNail)->sourceHandle);
mem_free(_GM(saveLoadThumbNail));
_GM(saveLoadThumbNail) = nullptr;
}
} else {
// Else there may be up to 10 somewhere in the list to be unloaded
for (int32 i = 0; i < MAX_SLOTS; i++) {
unloadThumbnail(i);
}
_GM(saveLoadThumbNail) = nullptr;
}
// Destroy the screen
vmng_screen_dispose(_GM(slMenu));
guiMenu::destroy(_GM(slMenu));
// Unload the save/load menu sprites
guiMenu::unloadSprites();
}
bool SaveLoadMenu::load_Handler(M4::GUI::menuItemButton *myItem, int32 eventType, int32 event, int32 x, int32 y, void **currItem) {
// Handle the event just like any other button
const bool handled = menuItemButton::handler(myItem, eventType, event, x, y, currItem);
// If we've selected a slot, we want the thumbNail to remain on the menu permanently
if (_GM(slotSelected) >= 0) {
return handled;
}
// But if the event moved the mouse, we want to display the correct thumbNail;
if ((eventType == EVENT_MOUSE) && ((event == _ME_move) || (event == _ME_L_drag) || (event == _ME_L_release) ||
(event == _ME_doubleclick_drag) || (event == _ME_doubleclick_release))) {
// Get the button
if (!myItem)
return handled;
// This determines that we are over the button
if ((myItem->itemFlags == menuItemButton::BTN_STATE_OVER) || (myItem->itemFlags == menuItemButton::BTN_STATE_PRESS)) {
// See if the current _GM(saveLoadThumbNail) is pointing to the correct sprite
if (_GM(saveLoadThumbNail) != _GM(thumbNails)[myItem->specialTag - 1]) {
_GM(saveLoadThumbNail) = _GM(thumbNails)[myItem->specialTag - 1];
guiMenu::itemRefresh(nullptr, SL_TAG_THUMBNAIL, (guiMenu *)myItem->myMenu);
}
}
// Else we must determine whether the thumbnail needs to be replaced with the empty thumbnail.
else {
// If the mouse has moved outside of the entire range of all 10 buttons,
//or it is over a button which is not hilited it is to be removed.
if (menuItem::cursorInsideItem(myItem, x, y)
|| (x < SL_SCROLL_FIELD_X)
|| (x > SL_SCROLL_FIELD_X + SL_SCROLL_FIELD_W)
|| (y < SL_SCROLL_FIELD_Y)
|| (y > SL_SCROLL_FIELD_Y + SL_SCROLL_FIELD_H)) {
// Remove the thumbnail
if (_GM(saveLoadThumbNail)) {
_GM(saveLoadThumbNail) = _GM(menuSprites)[GUI::SaveLoadMenuBase::SL_EMPTY_THUMB_25];
guiMenu::itemRefresh(nullptr, SL_TAG_THUMBNAIL, (guiMenu *)myItem->myMenu);
}
}
}
}
return handled;
}
void SaveLoadMenu::cbSave(void *, M4::GUI::guiMenu *myMenu) {
// If (slotSelected < 0) this callback is being executed by pressing return prematurely
if (_GM(slotSelected) < 0) {
return;
}
// First make the textfield NORM
menuItemTextField *myText = (menuItemTextField *)guiMenu::getItem(2000, myMenu);
if (!myText)
return;
myText->itemFlags = menuItemTextField::TF_NORM;
// Set the vars
_GM(slotInUse)[_GM(slotSelected) - 1] = true;
Common::strcpy_s(_GM(slotTitles)[_GM(slotSelected) - 1], 80, myText->prompt);
// Save the game
const bool saveGameFailed = !g_engine->saveGameFromMenu(_GM(slotSelected),
myText->prompt, _GM(_thumbnail));
// If the save game failed, bring up the err menu
if (saveGameFailed) {
// Kill the save menu
destroyMenu(true);
// Create the err menu
ErrorMenu::show(nullptr);
// Abort this procedure
return;
}
// Kill the save menu
destroyMenu(true);
// Shutdown the menu system
guiMenu::shutdown(true);
}
void SaveLoadMenu::cbLoad(void *, M4::GUI::guiMenu *) {
// If (slotSelected < 0) this callback is being executed by pressing return prematurely
if (_GM(slotSelected) < 0) {
return;
}
// Kill the menu
destroyMenu(false);
// Shutdown the menu system
guiMenu::shutdown(false);
// See if we need to reset the ESC, F2, and F3 hotkeys
if (_GM(gameMenuFromMain)) {
AddSystemHotkey(KEY_ESCAPE, Riddle::Hotkeys::escape_key_pressed);
AddSystemHotkey(KEY_F2, M4::Hotkeys::saveGame);
AddSystemHotkey(KEY_F3, M4::Hotkeys::loadGame);
}
// Start the restore process
_G(kernel).restore_slot = _GM(slotSelected);
KernelTriggerType oldMode = _G(kernel).trigger_mode;
_G(kernel).trigger_mode = KT_DAEMON;
kernel_trigger_dispatch_now(TRIG_RESTORE_GAME);
_G(kernel).trigger_mode = oldMode;
}
void SaveLoadMenu::cbCancel(M4::GUI::menuItemButton *, M4::GUI::guiMenu *myMenu) {
// If a slot has been selected, cancel will re-enable all slots
if (_GM(slotSelected) >= 0) {
// Enable the prev buttons
for (int32 i = 1001; i <= 1010; i++) {
if (_GM(currMenuIsSave) || _GM(slotInUse)[i - 1001 + _GM(firstSlotIndex)]) {
menuItemButton::enableButton(nullptr, i, myMenu);
guiMenu::itemRefresh(nullptr, i, myMenu);
}
}
// Find the textfield and use it's coords to place the button
menuItem *myItem = guiMenu::getItem(2000, myMenu);
const int32 x = myItem->x1;
const int32 y = myItem->y1;
const int32 w = myItem->x2 - myItem->x1 + 1;
const int32 h = myItem->y2 - myItem->y1 + 1;
// Delete the textfield
guiMenu::itemDelete(myItem, 2000, myMenu);
// Add the button back in
if (_GM(currMenuIsSave)) {
menuItemButton::add(myMenu, 1000 + _GM(slotSelected) - _GM(firstSlotIndex), x, y, w, h,
(CALLBACK)cbSlot, menuItemButton::BTN_TYPE_SL_TEXT, false, true, _GM(slotTitles)[_GM(slotSelected) - 1]);
} else {
menuItemButton::add(myMenu, 1000 + _GM(slotSelected) - _GM(firstSlotIndex), x, y, w, h,
(CALLBACK)cbSlot, menuItemButton::BTN_TYPE_SL_TEXT, false, true, _GM(slotTitles)[_GM(slotSelected) - 1],
(ItemHandlerFunction)load_Handler);
// Remove the thumbnail
if (_GM(saveLoadThumbNail)) {
_GM(saveLoadThumbNail) = _GM(menuSprites)[GUI::SaveLoadMenuBase::SL_EMPTY_THUMB_25];
guiMenu::itemRefresh(nullptr, SL_TAG_THUMBNAIL, myMenu);
}
}
setFirstSlot(_GM(firstSlotIndex), myMenu);
// Enable the slider
menuItemVSlider::enableVSlider(nullptr, SL_TAG_VSLIDER, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_VSLIDER, myMenu);
// Disable the save/load button
if (_GM(currMenuIsSave)) {
menuItemButton::disableButton(nullptr, SL_TAG_SAVE, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_SAVE, myMenu);
} else {
menuItemButton::disableButton(nullptr, SL_TAG_LOAD, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_LOAD, myMenu);
}
// Reset the slot selected var
_GM(slotSelected) = -1;
} else {
// Otherwise, back to the game menu
// Destroy the menu
destroyMenu(_GM(currMenuIsSave));
if (_GM(saveLoadFromHotkey)) {
// Shutdown the menu system
guiMenu::shutdown(true);
} else {
// Create the game menu
GameMenu::show(nullptr);
}
}
_GM(buttonClosesDialog) = true;
}
void SaveLoadMenu::cbSlot(M4::GUI::menuItemButton *myButton, M4::GUI::guiMenu *myMenu) {
// Verify params
if (!myMenu || !myButton)
return;
// Get the button
char prompt[80];
Common::strcpy_s(prompt, 80, myButton->prompt);
const int32 specialTag = myButton->specialTag;
// Set the globals
_GM(slotSelected) = myButton->specialTag;
_GM(deleteSaveDesc) = true;
// Disable all other buttons
for (int32 i = 1001; i <= 1010; i++) {
if (i != myButton->tag) {
menuItemButton::disableButton(nullptr, i, myMenu);
guiMenu::itemRefresh(nullptr, i, myMenu);
}
}
// Get the slot coords, and delete it
const int32 x = myButton->x1;
const int32 y = myButton->y1;
const int32 w = myButton->x2 - myButton->x1 + 1;
const int32 h = myButton->y2 - myButton->y1 + 1;
guiMenu::itemDelete(myButton, -1, myMenu);
if (_GM(currMenuIsSave)) {
// Replace the current button with a textfield
if (!strcmp(prompt, "<empty>")) {
menuItemTextField::add(myMenu, 2000, x, y, w, h, menuItemTextField::TF_OVER,
nullptr, specialTag, (CALLBACK)cbSave, true);
} else {
menuItemTextField::add(myMenu, 2000, x, y, w, h, menuItemTextField::TF_OVER,
prompt, specialTag, (CALLBACK)cbSave, true);
}
} else {
menuItemTextField::add(myMenu, 2000, x, y, w, h, menuItemTextField::TF_NORM,
prompt, specialTag, (CALLBACK)cbLoad, true);
}
// Disable the slider
menuItemVSlider::disableVSlider(nullptr, SL_TAG_VSLIDER, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_VSLIDER, myMenu);
// Enable the save/load button
if (_GM(currMenuIsSave)) {
menuItemButton::enableButton(nullptr, SL_TAG_SAVE, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_SAVE, myMenu);
} else {
menuItemButton::enableButton(nullptr, SL_TAG_LOAD, myMenu);
guiMenu::itemRefresh(nullptr, SL_TAG_LOAD, myMenu);
}
}
void SaveLoadMenu::cbVSlider(M4::GUI::menuItemVSlider *myItem, M4::GUI::guiMenu *myMenu) {
if (!myMenu || !myItem)
return;
if ((myItem->itemFlags & menuItemVSlider::VS_COMPONENT) != menuItemVSlider::VS_THUMB) {
bool redraw = (DrawFunction)false;
switch (myItem->itemFlags & menuItemVSlider::VS_COMPONENT) {
case menuItemVSlider::VS_UP:
if (_GM(firstSlotIndex) > 0) {
_GM(firstSlotIndex)--;
redraw = (DrawFunction)true;
}
break;
case menuItemVSlider::VS_PAGE_UP:
if (_GM(firstSlotIndex) > 0) {
_GM(firstSlotIndex) = imath_max(_GM(firstSlotIndex) - 10, 0);
redraw = (DrawFunction)true;
}
break;
case menuItemVSlider::VS_PAGE_DOWN:
if (_GM(firstSlotIndex) < 89) {
_GM(firstSlotIndex) = imath_min(_GM(firstSlotIndex) + 10, 89);
redraw = (DrawFunction)true;
}
break;
case menuItemVSlider::VS_DOWN:
if (_GM(firstSlotIndex) < 89) {
_GM(firstSlotIndex)++;
redraw = (DrawFunction)true;
}
break;
default:
break;
}
// See if we were able to set a new first slot index
if (redraw) {
setFirstSlot(_GM(firstSlotIndex), myMenu);
// Calculate the new percent
myItem->percent = (_GM(firstSlotIndex) * 100) / 89;
// Calculate the new thumbY
myItem->thumbY = myItem->minThumbY +
((myItem->percent * (myItem->maxThumbY - myItem->minThumbY)) / 100);
// Redraw the slider
guiMenu::itemRefresh(myItem, -1, myMenu);
}
} else {
// Else the callback came from the thumb - set the _GM(firstSlotIndex) based on the slider percent
_GM(firstSlotIndex) = (myItem->percent * 89) / 100;
setFirstSlot(_GM(firstSlotIndex), myMenu);
}
}
/*------------------ ERROR MENU METHODS ------------------*/
enum error_menu_sprites {
EM_DIALOG_BOX,
EM_RETURN_BTN_NORM,
EM_RETURN_BTN_OVER,
EM_RETURN_BTN_PRESS,
EM_TOTAL_SPRITES = 5
};
#define ERROR_MENU_X 237
#define ERROR_MENU_Y 191
#define EM_TAG_RETURN 1
#define EM_RETURN_X 12
#define EM_RETURN_Y 50
#define EM_RETURN_W 26
#define EM_RETURN_H 26
void ErrorMenu::show(RGB8 *myPalette) {
if (!_G(menuSystemInitialized)) {
guiMenu::initialize(myPalette);
}
// Load in the game menu sprites
if (!guiMenu::loadSprites("errmenu", EM_TOTAL_SPRITES)) {
return;
}
_GM(errMenu) = guiMenu::create(_GM(menuSprites)[EM_DIALOG_BOX],
ERROR_MENU_X, ERROR_MENU_Y, MENU_DEPTH | SF_GET_ALL | SF_BLOCK_ALL | SF_IMMOVABLE);
if (!_GM(errMenu)) {
return;
}
// Get the menu buffer
Buffer *myBuff = _GM(errMenu)->menuBuffer->get_buffer();
if (!myBuff) {
return;
}
//write the err message
gr_font_set_color(96);
gr_font_write(myBuff, "Save game failed!", 48, 8, 0, -1);
gr_font_write(myBuff, "A disk error has", 48, 23, 0, -1);
gr_font_write(myBuff, "occurred.", 48, 33, 0, -1);
gr_font_write(myBuff, "Please ensure you", 48, 48, 0, -1);
gr_font_write(myBuff, "have write access", 48, 58, 0, -1);
gr_font_write(myBuff, "and sufficient", 48, 68, 0, -1);
gr_font_write(myBuff, "disk space (40k).", 48, 78, 0, -1);
_GM(errMenu)->menuBuffer->release();
// Add the done button
menuItemButton::add(_GM(errMenu), EM_TAG_RETURN, EM_RETURN_X, EM_RETURN_Y,
EM_RETURN_W, EM_RETURN_H, cbDone);
// Configure the game so pressing <esc> will cause the menu to disappear and the gamemenu to reappear
guiMenu::configure(_GM(errMenu), cbDone, cbDone);
vmng_screen_show((void *)_GM(errMenu));
LockMouseSprite(0);
}
void ErrorMenu::cbDone(void *, void *) {
// Destroy the game menu
destroyMenu();
// Shutdown the menu system
guiMenu::shutdown(true);
}
void ErrorMenu::destroyMenu() {
if (!_GM(errMenu)) {
return;
}
// Remove the screen from the gui
vmng_screen_dispose(_GM(errMenu));
// Destroy the menu resources
guiMenu::destroy(_GM(errMenu));
// Unload the menu sprites
guiMenu::unloadSprites();
}
/*-------------------- ACCESS METHODS --------------------*/
void CreateGameMenu(RGB8 *myPalette) {
if (!player_commands_allowed() || !INTERFACE_VISIBLE ||
_G(pal_fade_in_progress) || _G(menuSystemInitialized)) {
return;
}
GameMenu::show(myPalette);
}
void CreateF2SaveMenu(RGB8 *myPalette) {
if (!player_commands_allowed() || !INTERFACE_VISIBLE ||
_G(pal_fade_in_progress) || _G(menuSystemInitialized)) {
return;
}
_GM(saveLoadFromHotkey) = true;
_GM(gameMenuFromMain) = false;
SaveLoadMenu::show(myPalette, true);
}
void CreateLoadMenu(RGB8 *myPalette) {
_GM(saveLoadFromHotkey) = false;
SaveLoadMenu::show(myPalette, false);
}
void CreateLoadMenuFromMain(RGB8 *myPalette) {
if (_G(pal_fade_in_progress) || _G(menuSystemInitialized)) {
return;
}
_GM(saveLoadFromHotkey) = true;
_GM(gameMenuFromMain) = true;
SaveLoadMenu::show(myPalette, false);
}
void CreateF3LoadMenu(RGB8 *myPalette) {
if (!player_commands_allowed() || !INTERFACE_VISIBLE ||
_G(pal_fade_in_progress) || _G(menuSystemInitialized)) {
return;
}
_GM(saveLoadFromHotkey) = true;
_GM(gameMenuFromMain) = false;
SaveLoadMenu::show(myPalette, false);
}
} // namespace GUI
} // namespace Riddle
} // namespace M4

View File

@@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_RIDDLE_GUI_GAME_MENU_H
#define M4_RIDDLE_GUI_GAME_MENU_H
#include "m4/gui/gui_menu_items.h"
#include "m4/gui/game_menu.h"
namespace M4 {
namespace Riddle {
namespace GUI {
class GameMenu {
private:
static void destroyGameMenu();
static void cbQuitGame(void *, void *);
static void cbMainMenu(void *, void *);
static void cbResume(void *, void *);
static void cbOptions(void *, void *);
static void cbSave(void *, void *);
static void cbLoad(void *, void *);
public:
static void show(RGB8 *myPalette);
};
class OptionsMenu {
private:
static void destroyOptionsMenu();
static void cbGameMenu(void *, void *);
static void cbScrolling(M4::GUI::menuItemButton *myItem, M4::GUI::guiMenu *);
static void cbSetDigi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *);
static void cbSetMidi(M4::GUI::menuItemHSlider *myItem, M4::GUI::guiMenu *);
public:
static void show();
};
class SaveLoadMenu : public M4::GUI::SaveLoadMenuBase {
private:
static void destroyMenu(bool saveMenu);
static bool load_Handler(M4::GUI::menuItemButton *myItem, int32 eventType,
int32 event, int32 x, int32 y, void **currItem);
static void cbCancel(M4::GUI::menuItemButton *, M4::GUI::guiMenu *myMenu);
static void cbSave(void *, M4::GUI::guiMenu *myMenu);
static void cbLoad(void *, M4::GUI::guiMenu *myMenu);
static void cbSlot(M4::GUI::menuItemButton *myButton, M4::GUI::guiMenu *myMenu);
static void cbVSlider(M4::GUI::menuItemVSlider *myItem, M4::GUI::guiMenu *myMenu);
public:
static void show(RGB8 *myPalette, bool saveMenu);
};
class ErrorMenu {
private:
static void destroyMenu();
static void cbDone(void *, void *);
public:
static void show(RGB8 *myPalette);
};
extern void CreateGameMenu(RGB8 *myPalette);
extern void CreateF2SaveMenu(RGB8 *myPalette);
extern void CreateLoadMenu(RGB8 *myPalette);
extern void CreateF3LoadMenu(RGB8 *myPalette);
// Routines used by the main menu
void CreateLoadMenuFromMain(RGB8 *myPalette);
void CreateGameMenuFromMain(RGB8 *myPalette);
} // namespace GUI
} // namespace Riddle
} // namespace M4
#endif

View File

@@ -0,0 +1,32 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/riddle/gui/gui_messages.h"
namespace M4 {
namespace Riddle {
namespace GUI {
int MessageLog::_result;
} // namespace GUI
} // namespace Riddle
} // namespace M4

View File

@@ -0,0 +1,41 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_RIDDLE_GUI_MESSAGES_H
#define M4_RIDDLE_GUI_MESSAGES_H
#include "m4/m4_types.h"
namespace M4 {
namespace Riddle {
namespace GUI {
class MessageLog {
public:
static int _result;
};
} // namespace GUI
} // namespace Riddle
} // namespace M4
#endif

View File

@@ -0,0 +1,457 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/riddle/gui/interface.h"
#include "m4/riddle/gui/game_menu.h"
#include "m4/riddle/gui/inventory.h"
#include "m4/riddle/riddle.h"
#include "m4/riddle/vars.h"
#include "m4/adv_r/other.h"
#include "m4/core/cstring.h"
#include "m4/core/errors.h"
#include "m4/gui/gui_event.h"
#include "m4/gui/gui_vmng.h"
namespace M4 {
namespace Riddle {
namespace GUI {
Interface::Interface() : M4::Interface() {
_x1 = 0;
_y1 = LETTERBOX_Y + 373;
_x2 = SCREEN_WIDTH;
_y2 = SCREEN_HEIGHT;
}
bool Interface::init(int arrow, int wait, int look, int grab, int use) {
M4::Interface::init(arrow, wait, look, grab, use);
_sprite = AddWSAssetCELS("INTERFACE STUFF", 22, _G(master_palette));
gr_pal_interface(_G(master_palette));
if (_sprite != 22)
error_show(FL, 'SLF!');
mouse_set_sprite(arrow);
if (!_G(gameInterfaceBuff)) {
_G(gameInterfaceBuff) = new GrBuff(_x2 - _x1, _y2 - _y1);
setup();
return true;
}
return false;
}
Interface::~Interface() {
delete _interfaceBox;
delete _inventory;
delete _textField;
delete _btnTake;
delete _btnManipulate;
delete _btnHandle;
delete _btnBackpack;
delete _btnBinky;
}
void Interface::show() {
M4::Interface::show();
_interfaceBox->_must_redraw_all = true;
vmng_screen_show(_G(gameInterfaceBuff));
_visible = true;
track_hotspots_refresh();
}
void Interface::setup() {
_interfaceBox = new InterfaceBox(RectClass(10, 10, SCREEN_WIDTH - 11, 101));
_inventory = new GUI::Inventory(RectClass(207, 2, 557, 74),
_sprite, 4, 2, 35, 35, 3);
_textField = new TextField(13, 2, 189, 20);
_btnTake = new ButtonClass(RectClass(2, 10, 40, 50), "take", 4,3, 5, 4, INTERFACE_SPRITES);
_btnManipulate = new ButtonClass(RectClass(47, 10, 86, 50), "manipulate", 7, 6, 8, 7, INTERFACE_SPRITES);
_btnHandle = new ButtonClass(RectClass(92, 10, 130, 50), "handle", 5, 0, 2, 4, INTERFACE_SPRITES);
_interfaceBox->add(_btnTake);
_interfaceBox->add(_btnManipulate);
_interfaceBox->add(_btnHandle);
_btnBackpack = new BackpackClass(RectClass(135, 10, 176, 50), "backpack", 6, 10, 10, 9, INTERFACE_SPRITES);
_btnBinky = new ButtonClass(RectClass(582, 10, 629, 50), "binky", 8, 11, 13, 12, INTERFACE_SPRITES);
_interfaceBox->add(_btnBackpack);
_interfaceBox->add(_btnBinky);
_inventory->addToInterfaceBox(_interfaceBox);
}
void Interface::cancel_sentence() {
_textField->set_string(" ");
_G(player).need_to_walk = false;
_G(player).ready_to_walk = false;
_G(player).command_ready = false;
_prepText[0] = '\0';
_nounText[0] = '\0';
_verbText[0] = '\0';
_iconSelected = false;
track_hotspots_refresh();
}
void Interface::freshen_sentence() {
Common::strcpy_s(_verbText, _nounText);
_nounText[0] = '\0';
_textField->set_string(" ");
_iconSelected = true;
_G(player).waiting_for_walk = false;
_G(player).ready_to_walk = false;
_G(player).need_to_walk = false;
_G(player).command_ready = false;
track_hotspots_refresh();
}
bool Interface::set_interface_palette(RGB8 *myPalette) {
gr_pal_set_RGB8(&myPalette[0], 0, 0, 0);
gr_pal_set_RGB8(&myPalette[1], 0, 0, 0);
gr_pal_set_RGB8(&myPalette[2], 39, 39, 39);
gr_pal_set_RGB8(&myPalette[3], 71, 75, 71);
gr_pal_set_RGB8(&myPalette[4], 103, 107, 103);
gr_pal_set_RGB8(&myPalette[5], 131, 135, 131);
gr_pal_set_RGB8(&myPalette[6], 163, 171, 163);
gr_pal_set_RGB8(&myPalette[7], 199, 215, 207);
gr_pal_set_RGB8(&myPalette[8], 235, 247, 231);
gr_pal_set_RGB8(&myPalette[9], 131, 103, 63);
gr_pal_set_RGB8(&myPalette[10], 143, 115, 75);
gr_pal_set_RGB8(&myPalette[11], 155, 127, 91);
gr_pal_set_RGB8(&myPalette[12], 167, 143, 107);
gr_pal_set_RGB8(&myPalette[13], 175, 131, 115);
gr_pal_set_RGB8(&myPalette[14], 199, 155, 131);
gr_pal_set_RGB8(&myPalette[15], 227, 183, 143);
gr_pal_set_RGB8(&myPalette[16], 255, 215, 159);
gr_pal_set_RGB8(&myPalette[17], 99, 27, 39);
gr_pal_set_RGB8(&myPalette[18], 83, 19, 27);
gr_pal_set_RGB8(&myPalette[19], 71, 11, 19);
gr_pal_set_RGB8(&myPalette[20], 59, 7, 15);
return true;
}
void Interface::track_hotspots_refresh() {
_hotspot = nullptr;
--_savedX;
bool z = false;
eventHandler(_G(gameInterfaceBuff), EVENT_MOUSE, 1,
_G(MouseState).CursorColumn, _G(MouseState).CursorRow, &z);
}
bool Interface::eventHandler(void *bufferPtr, int32 eventType, int32 event, int32 x, int32 y, bool *z) {
if (eventType != EVENT_MOUSE)
return false;
if (_G(kernel).fading_to_grey && event == _ME_L_release) {
kernel_unexamine_inventory_object(_G(master_palette), 5, 1);
return true;
}
if (player_commands_allowed()) {
if (x == _savedX && y == _savedY && event != _ME_L_click &&
event != _ME_L_release && event != _ME_L_hold && event != _ME_L_drag)
return true;
_savedX = x;
_savedY = y;
ControlStatus status = _interfaceBox->track(event, x - _x1, y - _y1);
switch (status) {
case NOTHING:
_state = NOTHING;
break;
case SELECTED:
trackIcons();
break;
default:
_state = IN_CONTROL;
break;
}
if (_state == NOTHING || _state == OVER_CONTROL) {
status = _inventory->track(event, x - _x1, y - _y1);
handleState(status);
_state = status ? OVER_CONTROL : NOTHING;
}
if (_state == NOTHING) {
int32 scrStatus;
ScreenContext *screen = vmng_screen_find(_G(gameDrawBuff), &scrStatus);
if (!screen)
return false;
if (y >= _y1) {
if (!_iconSelected)
mouse_set_sprite(kArrowCursor);
_textField->set_string(" ");
}
if (trackHotspots(event, x - screen->x1, y - screen->y1) == SELECTED)
dispatch_command();
}
}
if (_interfaceBox->_must_redraw_all) {
_textField->_must_redraw = true;
_inventory->_must_redraw_all = true;
}
_interfaceBox->draw(_G(gameInterfaceBuff));
_textField->draw(_G(gameInterfaceBuff));
_inventory->draw(_G(gameInterfaceBuff));
return true;
}
void Interface::trackIcons() {
switch (_interfaceBox->_highlight_index) {
case 4:
// Take
t_cb();
break;
case 5:
// Look
l_cb();
break;
case 6:
// Backpack
mouse_set_sprite(_arrow);
_iconSelected = false;
_btnBackpack->swap_sprites();
_inventory->toggleHidden();
_inventory->refresh_scrollbars();
break;
case 7:
// Use
u_cb();
break;
case 8:
// Game menu
GUI::CreateGameMenu(_G(master_palette));
break;
case 9:
// Scroll inventory left
_inventory->check_left();
break;
case 10:
// Scroll inventory right
_inventory->check_right();
break;
default:
break;
}
}
ControlStatus Interface::trackHotspots(int event, int x, int y) {
const HotSpotRec *hotspot = g_engine->_activeRoom->custom_hotspot_which(x, y);
if (!hotspot)
hotspot = hotspot_which(_G(currentSceneDef).hotspots, x, y);
if (hotspot != _hotspot) {
if (!hotspot) {
_textField->set_string(" ");
_hotspot = nullptr;
return NOTHING;
}
if (!_iconSelected) {
if (!mouse_set_sprite(hotspot->cursor_number))
mouse_set_sprite(kArrowCursor);
Common::strlcpy(_verbText, hotspot->verb, 40);
}
Common::String vocab(hotspot->vocab ? hotspot->vocab : "");
Common::String prep(hotspot->prep ? hotspot->prep : "");
Common::String tmp = (g_engine->getLanguage() == Common::EN_ANY) ? vocab : prep;
tmp.toUppercase();
_textField->set_string(tmp.c_str());
tmp = vocab;
tmp.toUppercase();
Common::strlcpy(_nounText, tmp.c_str(), 40);
_hotspot = hotspot;
}
if (event == 5 && hotspot) {
_G(player).walk_x = x;
_G(player).walk_y = y;
_G(player).click_x = x;
_G(player).click_y = y;
if (hotspot->feet_x != 0x7fff)
_G(player).walk_x = hotspot->feet_x;
if (hotspot->feet_y != 0x7fff)
_G(player).walk_y = hotspot->feet_y;
_G(player).walk_facing = hotspot->facing;
_hotspot = nullptr;
return SELECTED;
}
return IN_CONTROL;
}
void Interface::dispatch_command() {
--_savedX;
cstrncpy(_G(player).verb, _verbText, 40);
cstrncpy(_G(player).noun, _nounText, 40);
cstrncpy(_G(player).prep, _prepText, 40);
_G(player).waiting_for_walk = true;
_G(player).ready_to_walk = true;
_G(player).need_to_walk = true;
_G(player).command_ready = true;
_G(kernel).trigger = -1;
_G(kernel).trigger_mode = KT_PREPARSE;
_iconSelected = false;
mouse_set_sprite(_arrow);
_G(player).walker_trigger = -1;
g_engine->_activeRoom->pre_parser();
g_engine->_activeSection->pre_parser();
g_engine->global_pre_parser();
}
void Interface::handleState(ControlStatus status) {
const int highlight = _inventory->_highlight;
const int index = _inventory->_scroll + highlight;
switch (status) {
case NOTHING:
_hotspot = nullptr;
cstrncpy(_nounText, " ", 40);
_textField->set_string(" ");
break;
case OVER_CONTROL:
if (highlight < -1 || (highlight != -1 && (
highlight < 128 || highlight > 129))) {
_hotspot = nullptr;
cstrncpy(_nounText, _inventory->_items[index]._name.c_str(), 40);
if (g_engine->getLanguage() == Common::EN_ANY) {
_textField->set_string(_inventory->_items[index]._name.c_str());
} else {
_textField->set_string(_inventory->_items[index]._verb.c_str());
}
}
break;
case SELECTED:
if (highlight != -1 && _inventory->_items[index]._cell != -1) {
if (_iconSelected) {
_hotspot = nullptr;
cstrncpy(_nounText, _inventory->_items[index]._name.c_str(), 40);
if (g_engine->getLanguage() == Common::EN_ANY) {
_textField->set_string(_inventory->_items[index]._name.c_str());
} else {
_textField->set_string(_inventory->_items[index]._verb.c_str());
}
term_message("got %d", index);
dispatch_command();
_G(player).ready_to_walk = true;
_G(player).need_to_walk = false;
_G(player).waiting_for_walk = false;
} else {
_hotspot = nullptr;
cstrncpy(_verbText, _inventory->_items[index]._name.c_str(), 40);
if (g_engine->getLanguage() == Common::EN_ANY) {
_textField->set_string(_inventory->_items[index]._name.c_str());
} else {
_textField->set_string(_inventory->_items[index]._verb.c_str());
}
mouse_set_sprite(_inventory->_items[index]._cursor);
_iconSelected = true;
}
}
break;
default:
break;
}
}
void Interface::l_cb() {
if (player_commands_allowed() && INTERFACE_VISIBLE) {
Common::strcpy_s(_verbText, "look at");
mouse_set_sprite(_look);
_iconSelected = true;
_G(cursor_state) = kLOOK;
}
}
void Interface::u_cb() {
if (player_commands_allowed() && INTERFACE_VISIBLE) {
Common::strcpy_s(_verbText, "gear");
mouse_set_sprite(_use);
_iconSelected = true;
_G(cursor_state) = kUSE;
}
}
void Interface::t_cb() {
if (player_commands_allowed() && INTERFACE_VISIBLE) {
Common::strcpy_s(_verbText, "take");
mouse_set_sprite(_grab);
_iconSelected = true;
_G(cursor_state) = kTAKE;
}
}
void Interface::a_cb() {
if (player_commands_allowed() && INTERFACE_VISIBLE) {
Common::strcpy_s(_verbText, "<><><><><><><><>");
mouse_set_sprite(_arrow);
_iconSelected = true;
_G(cursor_state) = kARROW;
}
}
} // namespace GUI
} // namespace Riddle
} // namespace M4

View File

@@ -0,0 +1,110 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_RIDDLE_INTERFACE_H
#define M4_RIDDLE_INTERFACE_H
#include "m4/riddle/gui/inventory.h"
#include "m4/adv_r/adv_interface.h"
#include "m4/adv_r/adv_hotspot.h"
#include "m4/graphics/graphics.h"
#include "m4/graphics/gr_buff.h"
namespace M4 {
namespace Riddle {
namespace GUI {
using M4::GUI::ControlStatus;
struct Interface : public M4::Interface {
class BackpackClass : public ButtonClass {
public:
BackpackClass(const RectClass &r, const Common::String &btnName, int16 tag,
int16 relaxed, int16 over, int16 picked, int sprite) :
ButtonClass(r, btnName, tag, relaxed, over, picked, sprite) {}
~BackpackClass() override {}
void swap_sprites() {
const int16 picked = _picked;
const int16 over = _over;
_picked = over;
_relaxed = _over = picked;
}
};
private:
void setup();
void trackIcons();
ControlStatus trackHotspots(int event, int x, int y);
void dispatch_command();
void handleState(ControlStatus status);
public:
GUI::InterfaceBox *_interfaceBox = nullptr;
GUI::Inventory *_inventory = nullptr;
GUI::TextField *_textField = nullptr;
GUI::ButtonClass *_btnTake = nullptr;
GUI::ButtonClass *_btnManipulate = nullptr;
GUI::ButtonClass *_btnHandle = nullptr;
BackpackClass *_btnBackpack = nullptr;
GUI::ButtonClass *_btnBinky = nullptr;
int _sprite = 22; // main_interface_sprite;
const HotSpotRec *_hotspot = nullptr;
int _savedX = 0, _savedY = 0;
char _prepText[40] = { 0 };
char _nounText[40] = { 0 };
char _verbText[40] = { 0 };
bool _iconSelected = false;
int _state = 0;
Interface();
~Interface() override;
bool init(int arrow, int wait, int look, int grab, int use) override;
void cancel_sentence() override;
void freshen_sentence() override;
bool set_interface_palette(RGB8 *myPalette) override;
void track_hotspots_refresh() override;
bool eventHandler(void *bufferPtr, int32 eventType, int32 event, int32 x, int32 y, bool *z) override;
void show() override;
void l_cb();
void u_cb();
void t_cb();
void a_cb();
};
} // namespace Interface
} // namespace Riddle
} // namespace M4
#endif

View File

@@ -0,0 +1,366 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/riddle/gui/inventory.h"
#include "m4/riddle/vars.h"
#include "m4/core/errors.h"
#include "m4/graphics/gr_line.h"
#include "m4/graphics/gr_series.h"
#include "m4/gui/gui_vmng_core.h"
#include "m4/gui/gui_vmng_screen.h"
namespace M4 {
namespace Riddle {
namespace GUI {
Inventory::Inventory(const RectClass &r, int32 sprite, int16 cells_h, int16 cells_v, int16 cell_w, int16 cell_h, int16 tag)
: RectClass(r) {
_sprite = sprite;
for (int16 iter = 0; iter < INVENTORY_CELLS_COUNT; iter++) {
_items[iter]._cell = -1;
_items[iter]._cursor = -1;
}
_num_cells = 0;
_tag = tag;
_cells_h = cells_h;
_cells_v = cells_v;
_cell_w = cell_w;
_cell_h = cell_h;
// If requested cell configuration doesn't fit, blow up.
if ((cells_h * cell_w > (_x2 - _x1)) || (cells_v * cell_h > (_y2 - _y1))) {
error_show(FL, 'CGIC');
}
_highlight = -1;
_must_redraw_all = true;
_must_redraw1 = -1;
_must_redraw2 = -1;
_scroll = 0;
_right_arrow_visible = false;
_btnScrollLeft = new ButtonClass(RectClass(178, -8, 198, 101), "scroll left", 9, 129, 130, 131, INTERFACE_SPRITES);
_btnScrollRight = new ButtonClass(RectClass(551, -8, 571, 101), "scroll right", 10, 133, 134, 135, INTERFACE_SPRITES);
refresh_left_arrow();
refresh_right_arrow();
}
Inventory::~Inventory() {
delete _btnScrollLeft;
delete _btnScrollRight;
}
void Inventory::addToInterfaceBox(InterfaceBox *box) {
box->add(_btnScrollLeft);
box->add(_btnScrollRight);
}
bool Inventory::add(const Common::String &name, const Common::String &verb, int32 invSprite, int32 cursor) {
// Don't add something twice
for (int iter = 0; iter < _num_cells; iter++) {
if (name.equals(_items[iter]._name))
return true;
}
if (_num_cells >= INVENTORY_CELLS_COUNT) {
error_show(FL, 'CGIA');
return false;
}
auto &item = _items[_num_cells++];
item._name = name;
item._verb = verb;
item._cell = invSprite;
item._cursor = cursor;
_must_redraw_all = true;
if (INTERFACE_VISIBLE)
_G(interface).show();
return true;
}
bool Inventory::need_left() const {
return (_scroll != 0);
}
bool Inventory::need_right() const {
if ((_num_cells - _scroll - MAX_INVENTORY) > 0)
return true;
return false;
}
void Inventory::set_scroll(int32 new_scroll) {
_scroll = new_scroll;
_must_redraw_all = true;
}
void Inventory::toggleHidden() {
_hidden = !_hidden;
_must_redraw_all = true;
}
bool Inventory::remove(const Common::String &name) {
for (int iter = 0; iter < _num_cells; iter++) {
// Found the thing?
if (name.equals(_items[iter]._name)) {
// Eat up its slot by moving everything down
for (; iter < _num_cells; ++iter)
_items[iter] = _items[iter + 1];
--_num_cells;
_must_redraw_all = true;
_scroll = 0;
if (INTERFACE_VISIBLE)
_G(interface).show();
return true;
}
}
// Didn't find that thing.
return false;
}
int16 Inventory::inside(int16 x, int16 y) const {
if ((x < _x1) || (x >= (_x2 - 1)) || (y < _y1 + 2) ||
(y > _y1 + _cells_v * _cell_h - 2))
return -1;
x -= _x1;
y -= _y1;
return (int16)((x / _cell_w) * _cells_v + (y / _cell_h));
}
int16 Inventory::cell_pos_x(int16 index) {
if (_cells_h > _cells_v) { // Horizontal orientation, fill left to right
return (int16)((index / _cells_v) * _cell_w);
} else { // Vertical orientation, fill top to bottom
return (int16)((index / _cells_h) * _cell_w);
}
}
int16 Inventory::cell_pos_y(int16 index) {
if (_cells_h > _cells_v) {
// Horizontal orientation, fill left to right
return (int16)((index % _cells_v) * _cell_h);
} else {
// Vertical orientation, fill top to bottom
return (int16)((index % _cells_h) * _cell_h);
}
}
void Inventory::highlight_part(int16 index) {
if (_highlight == index)
return;
_must_redraw1 = _highlight;
_highlight = index;
_must_redraw2 = _highlight;
}
void Inventory::draw(GrBuff *myBuffer) {
if (!_must_redraw1 && !_must_redraw2 && !_must_redraw_all)
return;
Buffer *myBuff = myBuffer->get_buffer();
if (_must_redraw_all || _hidden) {
gr_color_set(__BLACK);
gr_buffer_rect_fill(myBuff, _x1, _y1, _x2 - _x1, _y2 - _y1);
}
if (!_hidden) {
_right_arrow_visible = false;
const int X_BORDER = 2, Y_BORDER = 2;
for (int cell_iter = 0; (cell_iter + _scroll < _num_cells) && (cell_iter < MAX_INVENTORY); cell_iter++) {
int16 left =_x1 + X_BORDER + cell_pos_x(cell_iter);
int16 top = _y1 + Y_BORDER + cell_pos_y(cell_iter);
int16 leftOffset = left + _cell_w;
int16 topOffset = top + _cell_h;
if (_must_redraw1 == cell_iter || _must_redraw2 == cell_iter || _must_redraw_all) {
// Update the scroll buttons
refresh_right_arrow();
refresh_left_arrow();
// Draw icon here
gr_color_set(__BLACK);
gr_buffer_rect_fill(myBuff, left, top, leftOffset - left, topOffset - top);
series_show_frame(_sprite, _items[cell_iter + _scroll]._cell,
myBuff, left - 3, top - 3);
// Draw box around icon
if (_highlight == cell_iter) {
gr_line(left, top, left + _cell_w - 2, top, __LTGRAY, myBuff);
gr_line(left, top + _cell_h - 2, left + _cell_w - 2, top + _cell_h - 2, __LTGRAY, myBuff);
gr_line(left, top, left, top + _cell_w - 2, __LTGRAY, myBuff);
gr_line(left + _cell_w - 2, top, left + _cell_w - 2, top + _cell_h - 2, __LTGRAY, myBuff);
}
}
}
}
ScreenContext *iC = vmng_screen_find(_G(gameInterfaceBuff), nullptr);
RestoreScreensInContext(_x1, _y1, _x2, _y2, iC);
_must_redraw1 = _must_redraw2 = -1;
_must_redraw_all = false;
myBuffer->release();
}
ControlStatus Inventory::track(int32 eventType, int16 x, int16 y) {
if (!INTERFACE_VISIBLE)
return NOTHING;
ControlStatus result = NOTHING;
int16 over = inside(x, y);
bool button_clicked = eventType == _ME_L_click || eventType == _ME_L_hold || eventType == _ME_L_drag;
// If Button is pressed
if (button_clicked) {
// If we are not tracking, start tracking
if (interface_tracking == -1) {
highlight_part(over);
interface_tracking = over;
result = IN_CONTROL;
} else {
// Else if we are over something we are tracking
if (interface_tracking == over) {
highlight_part(over);
result = IN_CONTROL;
} else {
// Else highlight nothing
highlight_part(-1);
result = NOTHING;
}
}
} else {
// If Button isn't pressed
// If we unpressed on something we were tracking
if (interface_tracking == over) {
if (interface_tracking == -1)
result = NOTHING;
else
result = SELECTED;
} else {
if (over + _scroll < _num_cells)
result = OVER_CONTROL;
else
result = NOTHING;
}
// Stop tracking anything
highlight_part(over);
interface_tracking = -1;
}
if (result == NOTHING && button_clicked)
return TRACKING;
return result;
}
void Inventory::refresh_right_arrow() {
if (need_right() || need_left()) {
_btnScrollRight->unhide();
if (need_right()) {
_btnScrollRight->set_sprite_relaxed(133);
_btnScrollRight->set_sprite_picked(135);
_btnScrollRight->set_sprite_over(134);
} else {
_btnScrollRight->set_sprite_relaxed(136);
_btnScrollRight->set_sprite_picked(136);
_btnScrollRight->set_sprite_over(136);
}
} else {
_btnScrollRight->hide();
_btnScrollLeft->hide();
}
}
void Inventory::refresh_left_arrow() {
if (need_right() || need_left()) {
_btnScrollLeft->unhide();
if (need_left()) {
_btnScrollLeft->set_sprite_relaxed(129);
_btnScrollLeft->set_sprite_picked(131);
_btnScrollLeft->set_sprite_over(130);
} else {
_btnScrollLeft->set_sprite_relaxed(132);
_btnScrollLeft->set_sprite_picked(132);
_btnScrollLeft->set_sprite_over(132);
}
} else {
_btnScrollRight->hide();
_btnScrollLeft->hide();
}
}
void Inventory::refresh_scrollbars() {
if (_btnScrollRight->is_hidden())
refresh_right_arrow();
else
_btnScrollRight->hide();
if (_btnScrollLeft->is_hidden())
refresh_left_arrow();
else
_btnScrollLeft->hide();
}
void Inventory::check_left() {
if (!_btnScrollLeft->is_hidden()) {
if (need_left()) {
_scroll = (_scroll <= 0) ? 0 : _scroll - _cells_v;
}
refresh_right_arrow();
refresh_left_arrow();
_must_redraw_all = true;
}
}
void Inventory::check_right() {
if (!_btnScrollRight->is_hidden()) {
if (need_right())
_scroll += _cells_v;
refresh_right_arrow();
refresh_left_arrow();
_must_redraw_all = true;
}
}
} // namespace GUI
} // namespace Riddle
} // namespace M4

View File

@@ -0,0 +1,115 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef M4_RIDDLE_GUI_INVENTORY_H
#define M4_RIDDLE_GUI_INVENTORY_H
#include "m4/gui/gui_cheapo.h"
namespace M4 {
namespace Riddle {
namespace GUI {
using namespace M4::GUI;
constexpr int16 INVENTORY_CELLS_COUNT = 128;
constexpr int16 ARROW_WIDTH = 8;
constexpr int16 MAX_INVENTORY = 20;
constexpr int16 LEFT_ARROW_TAG = 128;
constexpr int16 RIGHT_ARROW_TAG = 129;
constexpr int16 LEFT_ARROW_TAG_DORMANT = 130;
constexpr int16 RIGHT_ARROW_TAG_DORMANT = 134;
constexpr int16 LEFT_ARROW_TAG_ROLL = 131;
constexpr int16 RIGHT_ARROW_TAG_ROLL = 135;
constexpr int16 LEFT_ARROW_TAG_DOWN = 132;
constexpr int16 RIGHT_ARROW_TAG_DOWN = 136;
constexpr int16 LEFT_ARROW_TAG_NONFUNC = 133;
constexpr int16 RIGHT_ARROW_TAG_NONFUNC = 137;
class Inventory : public RectClass {
struct Entry {
Common::String _name;
Common::String _verb;
int16 _cell = -1;
int16 _cursor = -1;
};
private:
GUI::ButtonClass *_btnScrollLeft = nullptr;
GUI::ButtonClass *_btnScrollRight = nullptr;
int32 _sprite = 0;
int16 _tag = 0;
int16 _num_cells = 0;
bool _right_arrow_visible = false;
bool _hidden = false;
int16 cell_pos_x(int16 index);
int16 cell_pos_y(int16 index);
int16 interface_tracking = -1;
public:
int16 _scroll = 0;
int16 _cells_h = 0, _cells_v = 0;
int16 _cell_w = 0, _cell_h = 0;
int16 _must_redraw1 = 0, _must_redraw2 = 0;
int16 _highlight = 0;
bool _must_redraw_all = false;
Entry _items[INVENTORY_CELLS_COUNT];
public:
Inventory(const RectClass &r, int32 sprite, int16 cells_h, int16 cells_v, int16 cell_w, int16 cell_h, int16 tag);
~Inventory();
void addToInterfaceBox(InterfaceBox *box);
void draw(GrBuff *interface_buffer);
int16 inside(int16 x, int16 y) const override;
ControlStatus track(int32 eventType, int16 x, int16 y);
bool add(const Common::String &name, const Common::String &verb, int32 cel, int32 cursor);
bool remove(const Common::String &name);
void highlight_part(int16 index);
bool need_left() const;
bool need_right() const;
void refresh_right_arrow();
void refresh_left_arrow();
void refresh_scrollbars();
void check_left();
void check_right();
void set_scroll(int32 new_scroll);
/**
* Toggles whether the inventory is visible
*/
void toggleHidden();
};
} // namespace GUI
} // namespace Riddle
} // namespace M4
#endif