Initial commit
This commit is contained in:
214
engines/twine/menu/interface.cpp
Normal file
214
engines/twine/menu/interface.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/* 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 "twine/menu/interface.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/primitives.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
Interface::Interface(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
bool Interface::drawLine(int32 x0, int32 y0, int32 x1, int32 y1, uint8 color) {
|
||||
// always from left to right
|
||||
if (x0 > x1) {
|
||||
SWAP(x0, x1);
|
||||
SWAP(y0, y1);
|
||||
}
|
||||
|
||||
const int32 cright = _clip.right;
|
||||
const int32 cleft = _clip.left;
|
||||
const int32 cbottom = _clip.bottom;
|
||||
const int32 ctop = _clip.top;
|
||||
|
||||
uint16 clipFlags;
|
||||
do {
|
||||
if (x0 > cright || x1 < cleft) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 dx = x1 - x0;
|
||||
int32 dy = y1 - y0;
|
||||
|
||||
clipFlags = 0;
|
||||
|
||||
if (x0 < cleft) {
|
||||
clipFlags |= 0x100;
|
||||
}
|
||||
|
||||
if (y0 < ctop) {
|
||||
clipFlags |= 0x800;
|
||||
} else if (y0 > cbottom) {
|
||||
clipFlags |= 0x400;
|
||||
}
|
||||
|
||||
if (x1 > cright) {
|
||||
clipFlags |= 0x2;
|
||||
}
|
||||
|
||||
if (y1 < ctop) {
|
||||
clipFlags |= 0x8;
|
||||
} else if (y1 > cbottom) {
|
||||
clipFlags |= 0x4;
|
||||
}
|
||||
|
||||
if (clipFlags & (clipFlags >> 8)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clipFlags) {
|
||||
if (clipFlags & 0x100) {
|
||||
y0 += ((cleft - x0) * dy) / dx;
|
||||
x0 = cleft;
|
||||
} else if (clipFlags & 0x800) {
|
||||
x0 += ((ctop - y0) * dx) / dy;
|
||||
y0 = ctop;
|
||||
} else if (clipFlags & 0x400) {
|
||||
x0 += ((cbottom - y0) * dx) / dy;
|
||||
y0 = cbottom;
|
||||
} else if (clipFlags & 0x2) {
|
||||
y1 = (((cright - x0) * dy) / dx) + y0;
|
||||
x1 = cright;
|
||||
} else if (clipFlags & 0x8) {
|
||||
x1 = (((ctop - y0) * dx) / dy) + x0;
|
||||
y1 = ctop;
|
||||
} else if (clipFlags & 0x4) {
|
||||
x1 = (((cbottom - y0) * dx) / dy) + x0;
|
||||
y1 = cbottom;
|
||||
}
|
||||
}
|
||||
} while (clipFlags);
|
||||
|
||||
int16 lineOffset = (int16)_engine->width();
|
||||
x1 -= x0;
|
||||
y1 -= y0;
|
||||
if (y1 < 0) {
|
||||
lineOffset = -lineOffset;
|
||||
y1 = -y1;
|
||||
}
|
||||
|
||||
byte *out = (byte *)_engine->_frontVideoBuffer.getBasePtr(x0, y0);
|
||||
|
||||
if (x1 < y1) {
|
||||
// vertical
|
||||
SWAP(x1, y1);
|
||||
|
||||
int32 dy = x1 << 1;
|
||||
y0 = x1;
|
||||
y1 <<= 1;
|
||||
x1++;
|
||||
|
||||
do {
|
||||
*out = color;
|
||||
y0 -= y1;
|
||||
out += lineOffset;
|
||||
if (y0 < 0) {
|
||||
y0 += dy;
|
||||
out++;
|
||||
}
|
||||
} while (--x1);
|
||||
} else {
|
||||
// horizontal
|
||||
int32 dy = x1 << 1;
|
||||
y0 = x1;
|
||||
y1 <<= 1;
|
||||
x1++;
|
||||
|
||||
do {
|
||||
*out++ = color;
|
||||
y0 -= y1;
|
||||
if (y0 < 0) {
|
||||
y0 += dy;
|
||||
out += lineOffset;
|
||||
}
|
||||
} while (--x1);
|
||||
}
|
||||
|
||||
_engine->_frontVideoBuffer.addDirtyRect(Common::Rect(MIN<int16>(x0, x1), MIN<int16>(y0, y1), MAX<int16>(x0, x1), MAX<int16>(y0, y1)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Interface::blitBox(const Common::Rect &rect, const Graphics::ManagedSurface &source, Graphics::ManagedSurface &dest) {
|
||||
Common::Rect r(rect);
|
||||
r.right += 1;
|
||||
r.bottom += 1;
|
||||
dest.blitFrom(source, r, Common::Point(rect.left, rect.top));
|
||||
}
|
||||
|
||||
void Interface::shadeBox(const Common::Rect &rect, int32 colorAdj) {
|
||||
Common::Rect r = rect;
|
||||
r.clip(_engine->rect());
|
||||
if (r.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 *pos = (uint8*)_engine->_frontVideoBuffer.getBasePtr(0, r.top);
|
||||
|
||||
for (int32 y = r.top; y <= r.bottom; ++y) {
|
||||
for (int32 x = r.left; x <= r.right; ++x) {
|
||||
const int8 color = (pos[x] & 0x0F) - colorAdj;
|
||||
const int8 color2 = pos[x] & 0xF0;
|
||||
if (color < 0) {
|
||||
pos[x] = color2;
|
||||
} else {
|
||||
pos[x] = color + color2;
|
||||
}
|
||||
}
|
||||
pos += _engine->_frontVideoBuffer.pitch;
|
||||
}
|
||||
_engine->_frontVideoBuffer.addDirtyRect(r);
|
||||
}
|
||||
|
||||
void Interface::box(const Common::Rect &rect, uint8 colorIndex) { // Box
|
||||
if (!rect.isValidRect()) {
|
||||
return;
|
||||
}
|
||||
Common::Rect clipped(rect.left, rect.top, rect.right + 1, rect.bottom + 1);
|
||||
if (_clip.isValidRect()) {
|
||||
clipped.clip(_clip);
|
||||
}
|
||||
_engine->_frontVideoBuffer.fillRect(clipped, colorIndex);
|
||||
}
|
||||
|
||||
bool Interface::setClip(const Common::Rect &rect) {
|
||||
if (!_clip.isValidRect()) {
|
||||
return false;
|
||||
}
|
||||
_clip = rect;
|
||||
_clip.clip(_engine->rect());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Interface::memoClip() {
|
||||
_memoClip = _clip;
|
||||
}
|
||||
|
||||
void Interface::restoreClip() {
|
||||
_clip = _memoClip;
|
||||
}
|
||||
|
||||
void Interface::unsetClip() {
|
||||
_clip = _engine->rect();
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
79
engines/twine/menu/interface.h
Normal file
79
engines/twine/menu/interface.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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 TWINE_INTERFACE_H
|
||||
#define TWINE_INTERFACE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Graphics {
|
||||
class ManagedSurface;
|
||||
}
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class TwinEEngine;
|
||||
|
||||
class Interface {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
Common::Rect _memoClip;
|
||||
|
||||
public:
|
||||
Interface(TwinEEngine *engine);
|
||||
// ClipXmin, ClipXmax, ClipYmin, ClipYmax
|
||||
Common::Rect _clip { 0, 0, 0, 0 };
|
||||
bool _animateTexture = false; // lba2: AnimateTexture
|
||||
|
||||
/**
|
||||
* Draw button line
|
||||
* @param startWidth width value where the line starts
|
||||
* @param startHeight height value where the line starts
|
||||
* @param endWidth width value where the line ends
|
||||
* @param endHeight height value where the line ends
|
||||
* @param lineColor line color in the current palette
|
||||
*/
|
||||
bool drawLine(int32 startWidth, int32 startHeight, int32 endWidth, int32 endHeight, uint8 lineColor);
|
||||
|
||||
/**
|
||||
* Blit button box from working buffer to front buffer
|
||||
* @param source source screen buffer, in this case working buffer
|
||||
* @param dest destination screen buffer, in this case front buffer
|
||||
*/
|
||||
void blitBox(const Common::Rect &rect, const Graphics::ManagedSurface &source, Graphics::ManagedSurface &dest);
|
||||
/**
|
||||
* Draws inside buttons transparent area
|
||||
* @param colorAdj index to adjust the transparent box color
|
||||
*/
|
||||
void shadeBox(const Common::Rect &rect, int32 colorAdj);
|
||||
|
||||
void box(const Common::Rect &rect, uint8 colorIndex);
|
||||
|
||||
bool setClip(const Common::Rect &rect);
|
||||
void memoClip(); // saveTextWindow
|
||||
void restoreClip(); // loadSavedTextWindow
|
||||
void unsetClip();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
1488
engines/twine/menu/menu.cpp
Normal file
1488
engines/twine/menu/menu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
259
engines/twine/menu/menu.h
Normal file
259
engines/twine/menu/menu.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/* 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 TWINE_MENU_H
|
||||
#define TWINE_MENU_H
|
||||
|
||||
#include "twine/twine.h"
|
||||
#include "twine/text.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
#define MAX_BUTTONS 10
|
||||
#define PLASMA_WIDTH 320
|
||||
#define PLASMA_HEIGHT 50
|
||||
#define kDemoMenu 9999
|
||||
#define kQuitEngine 9998
|
||||
|
||||
class BodyData;
|
||||
class SpriteData;
|
||||
|
||||
class MenuSettings {
|
||||
private:
|
||||
enum MenuSettingsType {
|
||||
// button number
|
||||
MenuSettings_CurrentLoadedButton = 0,
|
||||
// is used to calc the height where the first button will appear
|
||||
MenuSettings_NumberOfButtons = 1,
|
||||
MenuSettings_ButtonsBoxHeight = 2,
|
||||
MenuSettings_TextBankId = 3,
|
||||
|
||||
MenuSettings_FirstButtonState,
|
||||
MenuSettings_FirstButton
|
||||
};
|
||||
|
||||
int16 _settings[4 + MAX_BUTTONS * 2] {0};
|
||||
Common::String _buttonTexts[MAX_BUTTONS];
|
||||
int8 _activeButtonIdx = 0;
|
||||
|
||||
public:
|
||||
TextId getButtonTextId(int buttonIndex) const {
|
||||
return (TextId)_settings[MenuSettings_FirstButton + buttonIndex * 2];
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (int32 i = 0; i < MAX_BUTTONS; ++i) {
|
||||
_buttonTexts[i] = "";
|
||||
}
|
||||
_settings[MenuSettings_NumberOfButtons] = 0;
|
||||
setButtonsBoxHeight(0);
|
||||
setActiveButton(0);
|
||||
}
|
||||
|
||||
// used to calc the height where the first button will appear
|
||||
void setButtonsBoxHeight(int16 height) {
|
||||
_settings[MenuSettings_ButtonsBoxHeight] = height;
|
||||
}
|
||||
|
||||
void setActiveButton(int16 buttonIdx) {
|
||||
_activeButtonIdx = buttonIdx;
|
||||
_settings[MenuSettings_CurrentLoadedButton] = buttonIdx;
|
||||
}
|
||||
|
||||
void setActiveButtonTextId(TextId textIndex) {
|
||||
setButtonTextId(getActiveButton(), textIndex);
|
||||
}
|
||||
|
||||
void setButtonTextId(int16 buttonIdx, TextId textIndex) {
|
||||
_settings[MenuSettings_FirstButton + buttonIdx * 2] = (int16)textIndex;
|
||||
_buttonTexts[buttonIdx].clear();
|
||||
}
|
||||
|
||||
TextId getActiveButtonTextId() const {
|
||||
return getButtonTextId(getActiveButton());
|
||||
}
|
||||
|
||||
int16 getActiveButtonState() const {
|
||||
return getButtonState(getActiveButton());
|
||||
}
|
||||
|
||||
int16 getButtonState(int buttonIndex) const {
|
||||
return _settings[MenuSettings_FirstButtonState + buttonIndex * 2];
|
||||
}
|
||||
|
||||
const char *getButtonText(Text *text, int buttonIndex);
|
||||
|
||||
int16 getActiveButton() const {
|
||||
return _activeButtonIdx;
|
||||
}
|
||||
|
||||
int16 getButtonBoxHeight() const {
|
||||
return _settings[MenuSettings_ButtonsBoxHeight];
|
||||
}
|
||||
|
||||
int16 getButtonCount() const {
|
||||
return _settings[MenuSettings_NumberOfButtons];
|
||||
}
|
||||
|
||||
void setTextBankId(TextBankId textBankIndex) {
|
||||
_settings[MenuSettings_TextBankId] = (int16)textBankIndex;
|
||||
}
|
||||
|
||||
void addButton(TextId textId, int16 state = 0) {
|
||||
const int16 i = _settings[MenuSettings_NumberOfButtons];
|
||||
_settings[i * 2 + MenuSettings_FirstButtonState] = state;
|
||||
_settings[i * 2 + MenuSettings_FirstButton] = (int16)textId;
|
||||
++_settings[MenuSettings_NumberOfButtons];
|
||||
}
|
||||
|
||||
void addButton(const char *text, int16 state = 0) {
|
||||
const int16 i = _settings[MenuSettings_NumberOfButtons];
|
||||
_settings[i * 2 + MenuSettings_FirstButtonState] = state;
|
||||
// will return the button index
|
||||
_settings[i * 2 + MenuSettings_FirstButton] = i;
|
||||
_buttonTexts[i] = text;
|
||||
++_settings[MenuSettings_NumberOfButtons];
|
||||
}
|
||||
};
|
||||
|
||||
class Menu {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
/** Hero behaviour menu entity */
|
||||
BodyData *_behaviourEntity = nullptr;
|
||||
/** Behaviour menu anim state */
|
||||
uint _behaviourAnimState[4]; // winTab
|
||||
/** Behaviour menu anim data pointer */
|
||||
AnimTimerDataStruct _behaviourAnimData[4];
|
||||
|
||||
int32 _inventorySelectedColor = COLOR_BLACK;
|
||||
int32 _inventorySelectedItem = 0; // currentSelectedObjectInInventory
|
||||
|
||||
/** Plasma Effect pointer to file content: RESS.HQR:51 */
|
||||
uint8 *_plasmaEffectPtr = nullptr;
|
||||
|
||||
MenuSettings _giveUpMenuWithSaveState;
|
||||
MenuSettings _volumeMenuState;
|
||||
MenuSettings _saveManageMenuState;
|
||||
MenuSettings _giveUpMenuState;
|
||||
MenuSettings _mainMenuState;
|
||||
MenuSettings _newGameMenuState;
|
||||
MenuSettings _advOptionsMenuState;
|
||||
MenuSettings _optionsMenuState;
|
||||
MenuSettings _languageMenuState;
|
||||
|
||||
// objectRotation
|
||||
int16 _itemAngle[NUM_INVENTORY_ITEMS];
|
||||
/** Behaviour menu move pointer */
|
||||
RealValue _moveMenu;
|
||||
|
||||
/**
|
||||
* Draws main menu button
|
||||
* @param buttonId current button identification from menu settings
|
||||
* @param dialText
|
||||
* @param hover flag to know if should draw as a hover button or not
|
||||
*/
|
||||
void drawButtonGfx(const MenuSettings *menuSettings, const Common::Rect &rect, int32 buttonId, const char *dialText, bool hover);
|
||||
void plasmaEffectRenderFrame();
|
||||
/**
|
||||
* Process the menu button draw
|
||||
* @param data menu settings array
|
||||
* @param mode flag to know if should draw as a hover button or not
|
||||
*/
|
||||
int16 drawButtons(MenuSettings *menuSettings, bool hover);
|
||||
/** Used to run the advanced options menu */
|
||||
int32 advoptionsMenu();
|
||||
/** Used to run the volume menu */
|
||||
int32 volumeOptions();
|
||||
int32 languageMenu();
|
||||
/** Used to run the save game management menu */
|
||||
int32 savemanageMenu();
|
||||
void drawInfoMenu(int16 left, int16 top, int16 width);
|
||||
Common::Rect calcBehaviourRect(int32 left, int32 top, HeroBehaviourType behaviour) const;
|
||||
bool isBehaviourHovered(int32 left, int32 top, HeroBehaviourType behaviour) const;
|
||||
void drawBehaviour(int32 left, int32 top, HeroBehaviourType behaviour, int32 angle, bool cantDrawBox);
|
||||
void drawListInventory(int32 left, int32 top);
|
||||
void prepareAndDrawBehaviour(int32 left, int32 top, int32 angle, HeroBehaviourType behaviour); // DrawComportement
|
||||
void drawBehaviourMenu(int32 left, int32 top, int32 angle); // DrawMenuComportement
|
||||
Common::Rect calcItemRect(int32 left, int32 top, int32 item, int32 *centerX = nullptr, int32 *centerY = nullptr) const;
|
||||
// draw the 2d sprite of the item
|
||||
void drawOneInventory(int32 left, int32 top, int32 item);
|
||||
|
||||
void drawSpriteAndString(int32 left, int32 top, const SpriteData &spriteData, const Common::String &str, int32 color = COLOR_GOLD);
|
||||
|
||||
public:
|
||||
Menu(TwinEEngine *engine);
|
||||
~Menu();
|
||||
|
||||
/**
|
||||
* Process the plasma effect
|
||||
* @param color plasma effect start color
|
||||
*/
|
||||
void processPlasmaEffect(const Common::Rect &rect, int32 color);
|
||||
|
||||
void drawHealthBar(int32 left, int32 right, int32 top, int32 barLeftPadding, int32 barHeight);
|
||||
void drawCloverLeafs(int32 newBoxLeft, int32 boxRight, int32 top);
|
||||
void drawMagicPointsBar(int32 left, int32 right, int32 top, int32 barLeftPadding, int32 barHeight);
|
||||
void drawCoins(int32 left, int32 top);
|
||||
void drawKeys(int32 left, int32 top);
|
||||
|
||||
/**
|
||||
* Draw the rect lines without filling the area
|
||||
* @param left start width to draw the button
|
||||
* @param top start height to draw the button
|
||||
* @param right end width to draw the button
|
||||
* @param bottom end height to draw the button
|
||||
*/
|
||||
void drawRectBorders(int32 left, int32 top, int32 right, int32 bottom, int32 colorLeftTop = COLOR_79, int32 colorRightBottom = COLOR_73);
|
||||
void drawRectBorders(const Common::Rect &rect, int32 colorLeftTop = COLOR_79, int32 colorRightBottom = COLOR_73);
|
||||
/**
|
||||
* Where the main menu options are processed
|
||||
* @param menuSettings menu settings array with the information to build the menu options
|
||||
* @return pressed menu button identification
|
||||
*/
|
||||
void menuDemo();
|
||||
int32 doGameMenu(MenuSettings *menuSettings);
|
||||
|
||||
bool init();
|
||||
|
||||
/** Used to run the main menu */
|
||||
EngineState run();
|
||||
|
||||
/** Used to run the in-game give-up menu */
|
||||
int32 quitMenu();
|
||||
|
||||
void inGameOptionsMenu();
|
||||
|
||||
/** Used to run the options menu */
|
||||
int32 optionsMenu();
|
||||
|
||||
/** Process hero behaviour menu */
|
||||
void processBehaviourMenu(bool behaviourMenu); // MenuComportement
|
||||
|
||||
int32 newGameClassicMenu();
|
||||
|
||||
/** Process in-game inventory menu */
|
||||
void inventory();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
450
engines/twine/menu/menuoptions.cpp
Normal file
450
engines/twine/menu/menuoptions.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/* 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 "twine/menu/menuoptions.h"
|
||||
#include "common/error.h"
|
||||
#include "common/keyboard.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
#include "savestate.h"
|
||||
#include "twine/audio/music.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/movies.h"
|
||||
#include "twine/scene/gamestate.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/menu/menu.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/scene.h"
|
||||
#include "twine/text.h"
|
||||
#include "twine/twine.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
static const char allowedCharIndex[] = " ABCDEFGHIJKLM.NOPQRSTUVWXYZ-abcdefghijklm?nopqrstuvwxyz!0123456789\040\b\r\0";
|
||||
|
||||
void MenuOptions::newGame() {
|
||||
_engine->setTotalPlayTime(0);
|
||||
_engine->_music->stopMusic();
|
||||
_engine->_sound->stopSamples();
|
||||
|
||||
if (_engine->isLBA1()) {
|
||||
int32 tmpFlagDisplayText = _engine->_cfgfile.FlagDisplayText;
|
||||
_engine->_cfgfile.FlagDisplayText = true;
|
||||
|
||||
// intro screen 1 - twinsun
|
||||
_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 15, 16));
|
||||
|
||||
_engine->_text->_flagMessageShade = false;
|
||||
_engine->_text->_renderTextTriangle = true;
|
||||
|
||||
_engine->_text->initDial(TextBankId::Inventory_Intro_and_Holomap);
|
||||
_engine->_text->bigWinDial();
|
||||
_engine->_text->setFontCrossColor(COLOR_WHITE);
|
||||
|
||||
bool aborted = _engine->_text->drawTextProgressive(TextId::kIntroText1);
|
||||
|
||||
// intro screen 2
|
||||
if (!aborted) {
|
||||
_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 17, 18));
|
||||
aborted |= _engine->_text->drawTextProgressive(TextId::kIntroText2);
|
||||
|
||||
if (!aborted) {
|
||||
_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 19, 20));
|
||||
aborted |= _engine->_text->drawTextProgressive(TextId::kIntroText3);
|
||||
}
|
||||
}
|
||||
_engine->_cfgfile.FlagDisplayText = tmpFlagDisplayText;
|
||||
|
||||
_engine->_screens->fadeToBlack(_engine->_screens->_palettePcx);
|
||||
_engine->_screens->clearScreen();
|
||||
|
||||
if (!aborted) {
|
||||
_engine->_music->playMidiFile(1);
|
||||
_engine->_movie->playMovie(FLA_INTROD);
|
||||
}
|
||||
|
||||
_engine->_text->normalWinDial();
|
||||
} else {
|
||||
_engine->_movie->playMovie(ACF_INTRO);
|
||||
}
|
||||
_engine->_screens->clearScreen();
|
||||
|
||||
_engine->_text->_flagMessageShade = true;
|
||||
_engine->_text->_renderTextTriangle = false;
|
||||
|
||||
// set main palette back
|
||||
_engine->setPalette(_engine->_screens->_ptrPal);
|
||||
}
|
||||
|
||||
// TODO: dotemu has credits_<lang>.txt files
|
||||
void MenuOptions::showCredits() {
|
||||
const int32 tmpShadowMode = _engine->_cfgfile.ShadowMode;
|
||||
_engine->_cfgfile.ShadowMode = 0;
|
||||
_engine->_gameState->initEngineVars();
|
||||
_engine->_scene->_numCube = LBA1SceneId::Credits_List_Sequence;
|
||||
_engine->_scene->_newCube = LBA1SceneId::Credits_List_Sequence;
|
||||
_engine->_screens->clearScreen();
|
||||
|
||||
flagCredits = true;
|
||||
_engine->mainLoop();
|
||||
_engine->_scene->stopRunningGame();
|
||||
flagCredits = false;
|
||||
|
||||
_engine->_cfgfile.ShadowMode = tmpShadowMode;
|
||||
|
||||
_engine->_screens->clearScreen();
|
||||
|
||||
_engine->_input->enableKeyMap(uiKeyMapId);
|
||||
}
|
||||
|
||||
void MenuOptions::showEndSequence() {
|
||||
_engine->_movie->playMovie(FLA_THEEND);
|
||||
|
||||
_engine->_screens->clearScreen();
|
||||
_engine->setPalette(_engine->_screens->_ptrPal);
|
||||
}
|
||||
|
||||
void MenuOptions::drawSelectableCharacter(int32 x, int32 y) {
|
||||
const int32 borderTop = 200;
|
||||
const int32 borderLeft = _engine->width() / 2 - 295;
|
||||
const int32 halfButtonHeight = 25;
|
||||
const int32 halfButtonWidth = 20;
|
||||
const int32 buttonDistanceX = halfButtonWidth * 2 + 5;
|
||||
const int32 buttonDistanceY = halfButtonHeight * 2 + 5;
|
||||
const int32 centerX = x * buttonDistanceX + borderLeft;
|
||||
const int32 centerY = y * buttonDistanceY + borderTop;
|
||||
const Common::Rect rect(centerX - halfButtonWidth, centerY - halfButtonHeight, centerX + halfButtonWidth, centerY + halfButtonHeight);
|
||||
|
||||
if (_engine->_input->isMouseHovering(rect)) {
|
||||
setOnScreenKeyboard(x, y);
|
||||
}
|
||||
|
||||
const int idx = x + y * ONSCREENKEYBOARD_WIDTH;
|
||||
if (_onScreenKeyboardDirty[idx] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
--_onScreenKeyboardDirty[idx];
|
||||
|
||||
const char buffer[]{allowedCharIndex[idx], '\0'};
|
||||
|
||||
const bool selected = _onScreenKeyboardX == x && _onScreenKeyboardY == y;
|
||||
if (selected) {
|
||||
_engine->_interface->box(rect, COLOR_91);
|
||||
} else {
|
||||
_engine->blitWorkToFront(rect);
|
||||
_engine->_interface->shadeBox(rect, 4);
|
||||
}
|
||||
|
||||
_engine->_menu->drawRectBorders(rect);
|
||||
|
||||
_engine->_text->setFontColor(COLOR_WHITE);
|
||||
const uint8 character = (uint8)allowedCharIndex[idx];
|
||||
const int32 textX = centerX - _engine->_text->getCharWidth(character) / 2;
|
||||
const int32 textY = centerY - _engine->_text->getCharHeight(character) / 2;
|
||||
_engine->_text->drawText(textX, textY, buffer);
|
||||
}
|
||||
|
||||
void MenuOptions::setOnScreenKeyboard(int x, int y) {
|
||||
if (x < 0) {
|
||||
x = ONSCREENKEYBOARD_WIDTH - 1;
|
||||
} else if (x >= ONSCREENKEYBOARD_WIDTH) {
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
y = ONSCREENKEYBOARD_HEIGHT - 1;
|
||||
} else if (y >= ONSCREENKEYBOARD_HEIGHT) {
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if (_onScreenKeyboardX == x && _onScreenKeyboardY == y) {
|
||||
return;
|
||||
}
|
||||
|
||||
++_onScreenKeyboardDirty[_onScreenKeyboardX + _onScreenKeyboardY * ONSCREENKEYBOARD_WIDTH];
|
||||
++_onScreenKeyboardDirty[x + y * ONSCREENKEYBOARD_WIDTH];
|
||||
|
||||
_onScreenKeyboardX = x;
|
||||
_onScreenKeyboardY = y;
|
||||
|
||||
_onScreenKeyboardLeaveViaOkButton = true;
|
||||
}
|
||||
|
||||
void MenuOptions::drawSelectableCharacters() {
|
||||
for (int8 x = 0; x < ONSCREENKEYBOARD_WIDTH; x++) {
|
||||
for (int8 y = 0; y < ONSCREENKEYBOARD_HEIGHT; y++) {
|
||||
drawSelectableCharacter(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuOptions::drawInputText(int32 centerx, int32 top, int32 type, const char *text) {
|
||||
const int32 left = 10;
|
||||
const int right = _engine->width() - left;
|
||||
const int bottom = top + PLASMA_HEIGHT;
|
||||
const Common::Rect rect(left, top, right, bottom);
|
||||
if (type == 1) {
|
||||
_engine->_menu->processPlasmaEffect(rect, 32);
|
||||
}
|
||||
|
||||
Common::Rect rectBox(rect);
|
||||
rectBox.grow(-1);
|
||||
_engine->_menu->drawRectBorders(rect);
|
||||
_engine->_interface->shadeBox(rectBox, 3);
|
||||
|
||||
_engine->_text->drawText(centerx - _engine->_text->sizeFont(text) / 2, top + 6, text);
|
||||
_engine->copyBlockPhys(rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle a given @c OSystem::Feature and restore the previous state on destruction
|
||||
*/
|
||||
class ScopedFeatureState {
|
||||
private:
|
||||
OSystem::Feature _feature;
|
||||
bool _changeTo;
|
||||
|
||||
public:
|
||||
ScopedFeatureState(OSystem::Feature feature, bool enable) : _feature(feature), _changeTo(enable) {
|
||||
if (g_system->getFeatureState(feature) != enable) {
|
||||
g_system->setFeatureState(feature, enable);
|
||||
_changeTo = !g_system->getFeatureState(feature);
|
||||
}
|
||||
}
|
||||
~ScopedFeatureState() {
|
||||
g_system->setFeatureState(_feature, _changeTo);
|
||||
}
|
||||
};
|
||||
|
||||
bool MenuOptions::enterText(TextId textIdx, char *textTargetBuf, size_t bufSize) {
|
||||
_engine->_text->initDial(TextBankId::Options_and_menus);
|
||||
char buffer[256];
|
||||
_engine->_text->getMenuText(textIdx, buffer, sizeof(buffer));
|
||||
_engine->_text->setFontColor(COLOR_WHITE);
|
||||
const int halfScreenWidth = (_engine->width() / 2);
|
||||
_engine->_text->drawText(halfScreenWidth - (_engine->_text->sizeFont(buffer) / 2), 20, buffer);
|
||||
_engine->copyBlockPhys(0, 0, _engine->width() - 1, 99);
|
||||
|
||||
Common::fill(&_onScreenKeyboardDirty[0], &_onScreenKeyboardDirty[ARRAYSIZE(_onScreenKeyboardDirty)], 1);
|
||||
ScopedFeatureState scopedVirtualKeyboard(OSystem::kFeatureVirtualKeyboard, true);
|
||||
for (;;) {
|
||||
FrameMarker frame(_engine);
|
||||
Common::Event event;
|
||||
while (g_system->getEventManager()->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
|
||||
_engine->_input->processCustomEngineEventEnd(event);
|
||||
break;
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
_engine->_input->processCustomEngineEventStart(event);
|
||||
|
||||
if (_engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
||||
if (_onScreenKeyboardLeaveViaOkButton) {
|
||||
if (_onScreenKeyboardX == ONSCREENKEYBOARD_WIDTH - 1 && _onScreenKeyboardY == ONSCREENKEYBOARD_HEIGHT - 1) {
|
||||
if (textTargetBuf[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const size_t size = strlen(textTargetBuf);
|
||||
if (_onScreenKeyboardX == ONSCREENKEYBOARD_WIDTH - 2 && _onScreenKeyboardY == ONSCREENKEYBOARD_HEIGHT - 1) {
|
||||
if (size >= 1) {
|
||||
textTargetBuf[size - 1] = '\0';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const char chr = allowedCharIndex[_onScreenKeyboardX + _onScreenKeyboardY * ONSCREENKEYBOARD_WIDTH];
|
||||
textTargetBuf[size] = chr;
|
||||
textTargetBuf[size + 1] = '\0';
|
||||
if (size + 1 >= bufSize - 1) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (textTargetBuf[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (_engine->_input->toggleActionIfActive(TwinEActionType::UIAbort)) {
|
||||
return false;
|
||||
}
|
||||
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) {
|
||||
setOnScreenKeyboard(_onScreenKeyboardX - 1, _onScreenKeyboardY);
|
||||
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight)) {
|
||||
setOnScreenKeyboard(_onScreenKeyboardX + 1, _onScreenKeyboardY);
|
||||
}
|
||||
if (_engine->_input->toggleActionIfActive(TwinEActionType::UIUp)) {
|
||||
setOnScreenKeyboard(_onScreenKeyboardX, _onScreenKeyboardY - 1);
|
||||
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIDown)) {
|
||||
setOnScreenKeyboard(_onScreenKeyboardX, _onScreenKeyboardY + 1);
|
||||
}
|
||||
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN: {
|
||||
const size_t size = strlen(textTargetBuf);
|
||||
if (!Common::isPrint(event.kbd.ascii)) {
|
||||
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
|
||||
if (size >= 1) {
|
||||
textTargetBuf[size - 1] = '\0';
|
||||
_onScreenKeyboardLeaveViaOkButton = false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (size >= bufSize - 1) {
|
||||
return true;
|
||||
}
|
||||
if (strchr(allowedCharIndex, event.kbd.ascii)) {
|
||||
textTargetBuf[size] = event.kbd.ascii;
|
||||
textTargetBuf[size + 1] = '\0';
|
||||
_onScreenKeyboardLeaveViaOkButton = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_engine->shouldQuit()) {
|
||||
break;
|
||||
}
|
||||
drawInputText(halfScreenWidth, 100, 1, textTargetBuf);
|
||||
drawSelectableCharacters();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuOptions::newGameMenu() {
|
||||
_engine->restoreFrontBuffer();
|
||||
_saveGameName[0] = '\0';
|
||||
if (!enterText(TextId::kEnterYourName, _saveGameName, sizeof(_saveGameName))) {
|
||||
return false;
|
||||
}
|
||||
_engine->_gameState->initEngineVars();
|
||||
newGame();
|
||||
return true;
|
||||
}
|
||||
|
||||
int MenuOptions::chooseSave(TextId textIdx, bool showEmptySlots) {
|
||||
const SaveStateList &savegames = _engine->getSaveSlots();
|
||||
if (savegames.empty() && !showEmptySlots) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_engine->_text->initDial(TextBankId::Options_and_menus);
|
||||
|
||||
MenuSettings saveFiles;
|
||||
saveFiles.addButton(TextId::kReturnMenu);
|
||||
|
||||
const int maxButtons = _engine->getMetaEngine()->getMaximumSaveSlot() + 1;
|
||||
uint savesIndex = 0;
|
||||
for (int i = 1; i < maxButtons; ++i) {
|
||||
if (savesIndex < savegames.size()) {
|
||||
const SaveStateDescriptor &savegame = savegames[savesIndex];
|
||||
if (savegame.getSaveSlot() == i - 1) {
|
||||
// manually creating a savegame should not overwrite the autosave slot
|
||||
if (textIdx != TextId::kCreateSaveGame || i > 1) {
|
||||
saveFiles.addButton(savegame.getDescription().encode().c_str(), i);
|
||||
}
|
||||
++savesIndex;
|
||||
} else if (showEmptySlots) {
|
||||
saveFiles.addButton("EMPTY", i);
|
||||
}
|
||||
} else if (showEmptySlots) {
|
||||
saveFiles.addButton("EMPTY", i);
|
||||
}
|
||||
}
|
||||
|
||||
const int32 id = _engine->_menu->doGameMenu(&saveFiles);
|
||||
switch (id) {
|
||||
case kQuitEngine:
|
||||
case (int32)TextId::kReturnMenu:
|
||||
return -1;
|
||||
default:
|
||||
const int slot = saveFiles.getButtonState(id) - 1;
|
||||
debug("Selected savegame slot %d", slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool MenuOptions::continueGameMenu() {
|
||||
_engine->restoreFrontBuffer();
|
||||
const int slot = chooseSave(TextId::kContinueGame);
|
||||
if (slot >= 0) {
|
||||
debug("Load slot %i", slot);
|
||||
Common::Error state = _engine->loadGameState(slot);
|
||||
if (state.getCode() != Common::kNoError) {
|
||||
error("Failed to load slot %i", slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuOptions::deleteSaveMenu() {
|
||||
_engine->restoreFrontBuffer();
|
||||
const int slot = chooseSave(TextId::kDeleteSaveGame);
|
||||
if (slot >= 0) {
|
||||
_engine->wipeSaveSlot(slot);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuOptions::saveGameMenu() {
|
||||
if (!_engine->_scene->isGameRunning()) {
|
||||
return false;
|
||||
}
|
||||
_engine->restoreFrontBuffer();
|
||||
const int slot = chooseSave(TextId::kCreateSaveGame, true);
|
||||
if (slot >= 0) {
|
||||
char buf[30];
|
||||
strncpy(buf, _engine->_gameState->_sceneName, sizeof(buf));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
_engine->restoreFrontBuffer();
|
||||
enterText(TextId::kEnterYourNewName, buf, sizeof(buf));
|
||||
// may not be empty
|
||||
if (buf[0] == '\0') {
|
||||
Common::strlcpy(buf, _engine->_gameState->_sceneName, sizeof(buf));
|
||||
}
|
||||
Common::Error state = _engine->saveGameState(slot, buf, false);
|
||||
if (state.getCode() != Common::kNoError) {
|
||||
error("Failed to save slot %i", slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
73
engines/twine/menu/menuoptions.h
Normal file
73
engines/twine/menu/menuoptions.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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 TWINE_MENUOPTIONS_H
|
||||
#define TWINE_MENUOPTIONS_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#define ONSCREENKEYBOARD_WIDTH 14
|
||||
#define ONSCREENKEYBOARD_HEIGHT 5
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "twine/scene/actor.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
class MenuOptions {
|
||||
private:
|
||||
TwinEEngine *_engine;
|
||||
|
||||
uint8 _onScreenKeyboardDirty[ONSCREENKEYBOARD_WIDTH * ONSCREENKEYBOARD_HEIGHT] { 0 };
|
||||
|
||||
int _onScreenKeyboardX = 0;
|
||||
int _onScreenKeyboardY = 0;
|
||||
bool _onScreenKeyboardLeaveViaOkButton = false;
|
||||
|
||||
void setOnScreenKeyboard(int x, int y);
|
||||
|
||||
bool enterText(TextId textIdx, char *textTargetBuf, size_t bufSize);
|
||||
void drawSelectableCharacters();
|
||||
void drawInputText(int32 centerx, int32 top, int32 type, const char *text);
|
||||
void drawSelectableCharacter(int32 x, int32 y);
|
||||
int chooseSave(TextId textIdx, bool showEmptySlots = false);
|
||||
|
||||
public:
|
||||
MenuOptions(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
void showEndSequence();
|
||||
void showCredits();
|
||||
bool flagCredits = false;
|
||||
|
||||
char _saveGameName[32] {'\0'};
|
||||
|
||||
/** Main menu new game options */
|
||||
bool newGameMenu();
|
||||
void newGame();
|
||||
|
||||
/** Main menu continue game options */
|
||||
bool continueGameMenu();
|
||||
bool saveGameMenu();
|
||||
bool deleteSaveMenu();
|
||||
};
|
||||
|
||||
} // namespace TwinE
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user