REminiscence/menu.cpp

516 lines
13 KiB
C++

/*
* REminiscence - Flashback interpreter
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/
#include "game.h"
#include "menu.h"
#include "resource.h"
#include "systemstub.h"
#include "util.h"
#include "video.h"
Menu::Menu(Resource *res, SystemStub *stub, Video *vid)
: _res(res), _stub(stub), _vid(vid) {
_skill = kSkillNormal;
_level = 0;
}
void Menu::drawString(const char *str, int16_t y, int16_t x, uint8_t color) {
debug(DBG_MENU, "Menu::drawString()");
uint8_t v1b = _vid->_charFrontColor;
uint8_t v2b = _vid->_charTransparentColor;
uint8_t v3b = _vid->_charShadowColor;
switch (color) {
case 0:
_vid->_charFrontColor = _charVar1;
_vid->_charTransparentColor = _charVar2;
_vid->_charShadowColor = _charVar2;
break;
case 1:
_vid->_charFrontColor = _charVar2;
_vid->_charTransparentColor = _charVar1;
_vid->_charShadowColor = _charVar1;
break;
case 2:
_vid->_charFrontColor = _charVar3;
_vid->_charTransparentColor = 0xFF;
_vid->_charShadowColor = _charVar1;
break;
case 3:
_vid->_charFrontColor = _charVar4;
_vid->_charTransparentColor = 0xFF;
_vid->_charShadowColor = _charVar1;
break;
case 4:
_vid->_charFrontColor = _charVar2;
_vid->_charTransparentColor = 0xFF;
_vid->_charShadowColor = _charVar1;
break;
case 5:
_vid->_charFrontColor = _charVar2;
_vid->_charTransparentColor = 0xFF;
_vid->_charShadowColor = _charVar5;
break;
}
drawString2(str, y, x);
_vid->_charFrontColor = v1b;
_vid->_charTransparentColor = v2b;
_vid->_charShadowColor = v3b;
}
void Menu::drawString2(const char *str, int16_t y, int16_t x) {
debug(DBG_MENU, "Menu::drawString2()");
int w = Video::CHAR_W;
int h = Video::CHAR_H;
int len = 0;
switch (_res->_type) {
case kResourceTypeAmiga:
for (; str[len]; ++len) {
_vid->AMIGA_drawStringChar(_vid->_frontLayer, _vid->_w, Video::CHAR_W * (x + len), Video::CHAR_H * y, _res->_fnt, _vid->_charFrontColor, (uint8_t)str[len]);
}
break;
case kResourceTypeDOS:
for (; str[len]; ++len) {
_vid->PC_drawChar((uint8_t)str[len], y, x + len, true);
}
break;
case kResourceTypeMac:
for (; str[len]; ++len) {
_vid->MAC_drawStringChar(_vid->_frontLayer, _vid->_w, Video::CHAR_W * (x + len), Video::CHAR_H * y, _res->_fnt, _vid->_charFrontColor, (uint8_t)str[len]);
}
break;
}
_vid->markBlockAsDirty(x * w, y * h, len * w, h, _vid->_layerScale);
}
void Menu::loadPicture(const char *prefix) {
debug(DBG_MENU, "Menu::loadPicture('%s')", prefix);
static const int kPictureW = 256;
static const int kPictureH = 224;
_res->load_MAP_menu(prefix, _res->_scratchBuffer);
for (int i = 0; i < 4; ++i) {
for (int y = 0; y < kPictureH; ++y) {
for (int x = 0; x < kPictureW / 4; ++x) {
_vid->_frontLayer[i + x * 4 + kPictureW * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y];
}
}
}
memcpy(_vid->_backLayer, _vid->_frontLayer, _vid->_layerSize);
_res->load_PAL_menu(prefix, _res->_scratchBuffer);
_stub->setPalette(_res->_scratchBuffer, 256);
_vid->updateWidescreen();
}
void Menu::handleInfoScreen() {
debug(DBG_MENU, "Menu::handleInfoScreen()");
_vid->fadeOut();
if (_res->_lang == LANG_FR) {
loadPicture("instru_f");
} else {
loadPicture("instru_e");
}
_vid->fullRefresh();
_vid->updateScreen();
do {
_stub->sleep(EVENTS_DELAY);
_stub->processEvents();
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
break;
}
} while (!_stub->_pi.quit);
}
void Menu::handleSkillScreen() {
debug(DBG_MENU, "Menu::handleSkillScreen()");
static const uint8_t colors[3][3] = {
{ 2, 3, 3 }, // easy
{ 3, 2, 3 }, // normal
{ 3, 3, 2 } // expert
};
_vid->fadeOut();
loadPicture("menu3");
_vid->fullRefresh();
drawString(_res->getMenuString(LocaleData::LI_12_SKILL_LEVEL), 12, 4, 3);
int currentSkill = _skill;
do {
drawString(_res->getMenuString(LocaleData::LI_13_EASY), 15, 14, colors[currentSkill][0]);
drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 17, 14, colors[currentSkill][1]);
drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 19, 14, colors[currentSkill][2]);
_vid->updateScreen();
_stub->sleep(EVENTS_DELAY);
_stub->processEvents();
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
switch (currentSkill) {
case kSkillNormal:
currentSkill = kSkillEasy;
break;
case kSkillExpert:
currentSkill = kSkillNormal;
break;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
switch (currentSkill) {
case kSkillEasy:
currentSkill = kSkillNormal;
break;
case kSkillNormal:
currentSkill = kSkillExpert;
break;
}
}
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
_skill = currentSkill;
return;
}
} while (!_stub->_pi.quit);
_skill = 1;
}
bool Menu::handlePasswordScreen() {
debug(DBG_MENU, "Menu::handlePasswordScreen()");
_vid->fadeOut();
_vid->_charShadowColor = _charVar1;
_vid->_charTransparentColor = 0xFF;
_vid->_charFrontColor = _charVar4;
_vid->fullRefresh();
char password[7];
int len = 0;
do {
loadPicture("menu2");
drawString2(_res->getMenuString(LocaleData::LI_16_ENTER_PASSWORD1), 15, 3);
drawString2(_res->getMenuString(LocaleData::LI_17_ENTER_PASSWORD2), 17, 3);
for (int i = 0; i < len; ++i) {
_vid->PC_drawChar((uint8_t)password[i], 21, i + 15);
}
_vid->PC_drawChar(0x20, 21, len + 15);
_vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H, _vid->_layerScale);
_vid->updateScreen();
_stub->sleep(EVENTS_DELAY);
_stub->processEvents();
char c = _stub->_pi.lastChar;
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < 6) {
if ((c >= 'A' && c <= 'Z') || (c == 0x20)) {
password[len] = c;
++len;
}
}
}
if (_stub->_pi.backspace) {
_stub->_pi.backspace = false;
if (len > 0) {
--len;
}
}
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
password[len] = '\0';
for (int level = 0; level < 8; ++level) {
for (int skill = 0; skill < 3; ++skill) {
if (strcmp(getLevelPassword(level, skill), password) == 0) {
_level = level;
_skill = skill;
return true;
}
}
}
return false;
}
} while (!_stub->_pi.quit);
return false;
}
bool Menu::handleLevelScreen() {
debug(DBG_MENU, "Menu::handleLevelScreen()");
_vid->fadeOut();
loadPicture("menu2");
_vid->fullRefresh();
int currentSkill = _skill;
int currentLevel = _level;
do {
for (int i = 0; i < 7; ++i) {
drawString(_levelNames[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3);
}
_vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H, _vid->_layerScale);
drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3);
drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3);
drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3);
_vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H, _vid->_layerScale);
_vid->updateScreen();
_stub->sleep(EVENTS_DELAY);
_stub->processEvents();
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (currentLevel != 0) {
--currentLevel;
} else {
currentLevel = 6;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
if (currentLevel != 6) {
++currentLevel;
} else {
currentLevel = 0;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT;
switch (currentSkill) {
case kSkillNormal:
currentSkill = kSkillEasy;
break;
case kSkillExpert:
currentSkill = kSkillNormal;
break;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
switch (currentSkill) {
case kSkillEasy:
currentSkill = kSkillNormal;
break;
case kSkillNormal:
currentSkill = kSkillExpert;
break;
}
}
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
_skill = currentSkill;
_level = currentLevel;
return true;
}
} while (!_stub->_pi.quit);
return false;
}
void Menu::handleTitleScreen() {
debug(DBG_MENU, "Menu::handleTitleScreen()");
_charVar1 = 0;
_charVar2 = 0;
_charVar3 = 0;
_charVar4 = 0;
_charVar5 = 0;
static const int MAX_MENU_ITEMS = 6;
Item menuItems[MAX_MENU_ITEMS];
int menuItemsCount = 0;
menuItems[menuItemsCount].str = LocaleData::LI_07_START;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START;
++menuItemsCount;
if (!_res->_isDemo) {
if (g_options.enable_password_menu) {
menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL;
++menuItemsCount;
menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD;
++menuItemsCount;
} else {
menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL;
++menuItemsCount;
}
}
menuItems[menuItemsCount].str = LocaleData::LI_10_INFO;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_INFO;
++menuItemsCount;
menuItems[menuItemsCount].str = LocaleData::LI_23_DEMO;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_DEMO;
++menuItemsCount;
menuItems[menuItemsCount].str = LocaleData::LI_11_QUIT;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_QUIT;
++menuItemsCount;
_selectedOption = -1;
_nextScreen = SCREEN_TITLE;
int currentEntry = 0;
static const struct {
Language lang;
const uint8_t *bitmap16x12;
} languages[] = {
{ LANG_EN, _flagEn16x12 },
{ LANG_FR, _flagFr16x12 },
{ LANG_DE, _flagDe16x12 },
{ LANG_SP, _flagSp16x12 },
{ LANG_IT, _flagIt16x12 },
{ LANG_JP, _flagJp16x12 },
};
int currentLanguage = 0;
for (int i = 0; i < ARRAYSIZE(languages); ++i) {
if (languages[i].lang == _res->_lang) {
currentLanguage = i;
break;
}
}
while (!_stub->_pi.quit) {
int selectedItem = -1;
int previousLanguage = currentLanguage;
if (_nextScreen == SCREEN_TITLE) {
_vid->fadeOut();
loadPicture("menu1");
_vid->fullRefresh();
_charVar3 = 1;
_charVar4 = 2;
currentEntry = 0;
_nextScreen = -1;
}
if (g_options.enable_language_selection) {
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT;
if (currentLanguage != 0) {
--currentLanguage;
} else {
currentLanguage = ARRAYSIZE(languages) - 1;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
if (currentLanguage != ARRAYSIZE(languages) - 1) {
++currentLanguage;
} else {
currentLanguage = 0;
}
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (currentEntry != 0) {
--currentEntry;
} else {
currentEntry = menuItemsCount - 1;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
if (currentEntry != menuItemsCount - 1) {
++currentEntry;
} else {
currentEntry = 0;
}
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
selectedItem = currentEntry;
}
if (selectedItem != -1) {
_selectedOption = menuItems[selectedItem].opt;
switch (_selectedOption) {
case MENU_OPTION_ITEM_START:
return;
case MENU_OPTION_ITEM_SKILL:
handleSkillScreen();
break;
case MENU_OPTION_ITEM_PASSWORD:
if (handlePasswordScreen()) {
return;
}
break;
case MENU_OPTION_ITEM_LEVEL:
if (handleLevelScreen()) {
return;
}
break;
case MENU_OPTION_ITEM_INFO:
handleInfoScreen();
break;
case MENU_OPTION_ITEM_DEMO:
return;
case MENU_OPTION_ITEM_QUIT:
return;
}
_nextScreen = SCREEN_TITLE;
continue;
}
if (previousLanguage != currentLanguage) {
_res->setLanguage(languages[currentLanguage].lang);
// clear previous language text
memcpy(_vid->_frontLayer, _vid->_backLayer, _vid->_layerSize);
}
// draw the options
const int yPos = 26 - menuItemsCount * 2;
for (int i = 0; i < menuItemsCount; ++i) {
drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3);
}
// draw the language flag in the top right corner
if (previousLanguage != currentLanguage) {
_stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid->_frontLayer, Video::GAMESCREEN_W);
static const int flagW = 16;
static const int flagH = 12;
static const int flagX = Video::GAMESCREEN_W - flagW - 8;
static const int flagY = 8;
_stub->copyRectRgb24(flagX, flagY, flagW, flagH, languages[currentLanguage].bitmap16x12);
}
_vid->updateScreen();
_stub->sleep(EVENTS_DELAY);
_stub->processEvents();
}
}
const char *Menu::getLevelPassword(int level, int skill) const {
switch (_res->_type) {
case kResourceTypeAmiga:
if (level < 7) {
if (_res->_lang == LANG_FR) {
return _passwordsFrAmiga[skill * 7 + level];
} else {
return _passwordsEnAmiga[skill * 7 + level];
}
}
break;
case kResourceTypeMac:
return _passwordsMac[skill * 8 + level];
case kResourceTypeDOS:
// default
break;
}
return _passwordsDOS[skill * 8 + level];
}