Initial commit
This commit is contained in:
195
engines/stark/ui/cursor.cpp
Normal file
195
engines/stark/ui/cursor.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 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 "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/services/gameinterface.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
const float Cursor::_fadeValueMax = 0.3f;
|
||||
|
||||
Cursor::Cursor(Gfx::Driver *gfx) :
|
||||
_gfx(gfx),
|
||||
_cursorImage(nullptr),
|
||||
_mouseText(nullptr),
|
||||
_currentCursorType(kImage),
|
||||
_itemActive(false),
|
||||
_fadeLevelIncreasing(true),
|
||||
_fadeLevel(0),
|
||||
_hintDisplayDelay(150) {
|
||||
setCursorType(kDefault);
|
||||
_actionHoverSound = StarkStaticProvider->getUISound(StaticProvider::kActionHover);
|
||||
}
|
||||
|
||||
Cursor::~Cursor() {
|
||||
delete _mouseText;
|
||||
}
|
||||
|
||||
void Cursor::setCursorType(CursorType type) {
|
||||
assert(type != kImage);
|
||||
if (type == _currentCursorType) {
|
||||
return;
|
||||
}
|
||||
_currentCursorType = type;
|
||||
_cursorImage = nullptr;
|
||||
}
|
||||
|
||||
void Cursor::setCursorImage(VisualImageXMG *image) {
|
||||
_currentCursorType = kImage;
|
||||
_cursorImage = image;
|
||||
}
|
||||
|
||||
void Cursor::setMousePosition(const Common::Point &pos) {
|
||||
_mousePos = pos;
|
||||
_hintDisplayDelay = 150;
|
||||
}
|
||||
|
||||
void Cursor::setItemActive(bool itemActive) {
|
||||
if (_itemActive == itemActive) {
|
||||
return;
|
||||
}
|
||||
if (itemActive) {
|
||||
_actionHoverSound->play();
|
||||
} else {
|
||||
_actionHoverSound->stop();
|
||||
}
|
||||
_itemActive = itemActive;
|
||||
}
|
||||
|
||||
void Cursor::onScreenChanged() {
|
||||
if (_mouseText) {
|
||||
_mouseText->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Cursor::updateFadeLevel() {
|
||||
if (_itemActive) {
|
||||
if (_fadeLevelIncreasing) {
|
||||
_fadeLevel += 0.001f * StarkGlobal->getMillisecondsPerGameloop();
|
||||
} else {
|
||||
_fadeLevel -= 0.001f * StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
if (ABS(_fadeLevel) >= _fadeValueMax) {
|
||||
_fadeLevelIncreasing = !_fadeLevelIncreasing;
|
||||
_fadeLevel = CLIP(_fadeLevel, -_fadeValueMax, _fadeValueMax);
|
||||
}
|
||||
} else {
|
||||
_fadeLevel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Cursor::updateHintDelay() {
|
||||
if (_hintDisplayDelay >= 0) {
|
||||
_hintDisplayDelay -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
if (_hintDisplayDelay <= 0) {
|
||||
_hintDisplayDelay = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cursor::render() {
|
||||
updateFadeLevel();
|
||||
updateHintDelay();
|
||||
|
||||
if (!_gfx->isPosInScreenBounds(_mousePos)) {
|
||||
setCursorType(Cursor::kPassive);
|
||||
}
|
||||
|
||||
if (_mouseText && _gfx->gameViewport().contains(_mousePos) && _hintDisplayDelay <= 0) {
|
||||
_gfx->setScreenViewport(false);
|
||||
|
||||
// TODO: Should probably query the image for the width of the cursor
|
||||
// TODO: Add delay to the mouse hints like in the game
|
||||
const int16 cursorDistance = 32;
|
||||
Common::Rect mouseRect = _mouseText->getRect();
|
||||
Common::Point pos = _gfx->convertCoordinateCurrentToOriginal(_mousePos);
|
||||
pos.x = CLIP<int16>(pos.x, 48, Gfx::Driver::kOriginalWidth - 48);
|
||||
pos.y = CLIP<int16>(pos.y, Gfx::Driver::kTopBorderHeight, Gfx::Driver::kOriginalHeight - Gfx::Driver::kBottomBorderHeight - cursorDistance - mouseRect.height());
|
||||
pos.x -= mouseRect.width() / 2;
|
||||
pos.y += cursorDistance;
|
||||
|
||||
_mouseText->render(pos);
|
||||
}
|
||||
|
||||
if (_currentCursorType != kImage) {
|
||||
_cursorImage = StarkStaticProvider->getCursorImage(_currentCursorType);
|
||||
}
|
||||
|
||||
if (_cursorImage) {
|
||||
_gfx->setScreenViewport(true); // Unscaled viewport so that cursor is drawn on native pixel space, thus no 'skipping', perform scaling below instead
|
||||
|
||||
_cursorImage->setFadeLevel(_fadeLevel);
|
||||
_cursorImage->render(_mousePos, true, false); // Draws image (scaled)
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point Cursor::getMousePosition(bool unscaled) const {
|
||||
if (unscaled) {
|
||||
return _mousePos;
|
||||
} else {
|
||||
// Most of the engine expects 640x480 coordinates
|
||||
return _gfx->convertCoordinateCurrentToOriginal(_mousePos);
|
||||
}
|
||||
}
|
||||
|
||||
void Cursor::setMouseHint(const Common::String &hint) {
|
||||
if (hint != _currentHint) {
|
||||
delete _mouseText;
|
||||
if (!hint.empty()) {
|
||||
_mouseText = new VisualText(_gfx);
|
||||
_mouseText->setText(hint);
|
||||
_mouseText->setColor(Gfx::Color(0xFF, 0xFF, 0xFF));
|
||||
_mouseText->setBackgroundColor(Gfx::Color(0x00, 0x00, 0x00, 0x80));
|
||||
_mouseText->setFont(FontProvider::kSmallFont);
|
||||
_mouseText->setTargetWidth(96);
|
||||
} else {
|
||||
_mouseText = nullptr;
|
||||
}
|
||||
_currentHint = hint;
|
||||
_hintDisplayDelay = 150;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect Cursor::getHotRectangle() const {
|
||||
if (!_cursorImage) {
|
||||
return Common::Rect();
|
||||
} else {
|
||||
Common::Point hotSpot = _cursorImage->getHotspot();
|
||||
|
||||
Common::Rect hotRectangle;
|
||||
hotRectangle.setWidth(_cursorImage->getWidth());
|
||||
hotRectangle.setHeight(_cursorImage->getHeight());
|
||||
hotRectangle.translate(-hotSpot.x, -hotSpot.y);
|
||||
|
||||
return hotRectangle;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
104
engines/stark/ui/cursor.h
Normal file
104
engines/stark/ui/cursor.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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 STARK_UI_CURSOR_H
|
||||
#define STARK_UI_CURSOR_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualImageXMG;
|
||||
class VisualText;
|
||||
|
||||
namespace Gfx {
|
||||
class Driver;
|
||||
}
|
||||
|
||||
namespace Resources {
|
||||
class Sound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manager for the current game Cursor
|
||||
*/
|
||||
class Cursor {
|
||||
public:
|
||||
explicit Cursor(Gfx::Driver *gfx);
|
||||
~Cursor();
|
||||
|
||||
/** Render the Cursor */
|
||||
void render();
|
||||
|
||||
/** Update the mouse position */
|
||||
void setMousePosition(const Common::Point &pos);
|
||||
|
||||
/** Make cycle the cursor's brightness and play sound */
|
||||
void setItemActive(bool fading);
|
||||
|
||||
/** Update when the screen resolution has changed */
|
||||
void onScreenChanged();
|
||||
|
||||
Common::Point getMousePosition(bool unscaled = false) const;
|
||||
|
||||
/** Rectangle at the mouse position to consider to hit test small world items */
|
||||
Common::Rect getHotRectangle() const;
|
||||
|
||||
enum CursorType {
|
||||
kImage = -1,
|
||||
kDefault = 0,
|
||||
kActive = 3,
|
||||
kPassive = 9,
|
||||
kEye = 10,
|
||||
kHand = 11,
|
||||
kMouth = 12
|
||||
};
|
||||
|
||||
void setCursorType(CursorType type);
|
||||
void setCursorImage(VisualImageXMG *image);
|
||||
void setMouseHint(const Common::String &hint);
|
||||
private:
|
||||
void updateFadeLevel();
|
||||
void updateHintDelay();
|
||||
|
||||
Gfx::Driver *_gfx;
|
||||
|
||||
Common::String _currentHint;
|
||||
int32 _hintDisplayDelay;
|
||||
|
||||
Common::Point _mousePos;
|
||||
VisualImageXMG *_cursorImage;
|
||||
VisualText *_mouseText;
|
||||
CursorType _currentCursorType;
|
||||
|
||||
Resources::Sound *_actionHoverSound;
|
||||
|
||||
bool _itemActive;
|
||||
float _fadeLevel;
|
||||
bool _fadeLevelIncreasing;
|
||||
static const float _fadeValueMax;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_CURSOR_H
|
||||
314
engines/stark/ui/dialogbox.cpp
Normal file
314
engines/stark/ui/dialogbox.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/* 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 "engines/stark/ui/dialogbox.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/fontprovider.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/formats/winexe_pe.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "image/bmp.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
static const uint dialogMaxWidth = 450;
|
||||
static const uint dialogHorizontalMargin = 10;
|
||||
static const uint dialogVerticalMargin = 20;
|
||||
static const uint buttonHorizontalMargin = 25;
|
||||
static const uint buttonVerticalMargin = 5;
|
||||
|
||||
DialogBox::DialogBox(StarkEngine *vm, Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Window(gfx, cursor),
|
||||
_background(nullptr),
|
||||
_foreground(nullptr),
|
||||
_confirmCallback(nullptr) {
|
||||
_vm = vm;
|
||||
_surfaceRenderer = gfx->createSurfaceRenderer();
|
||||
|
||||
_background = loadBackground(gfx);
|
||||
if (_background) {
|
||||
_background->setSamplingFilter(Gfx::Bitmap::kLinear);
|
||||
}
|
||||
|
||||
_messageVisual = new VisualText(gfx);
|
||||
_messageVisual->setColor(_textColor);
|
||||
_messageVisual->setTargetWidth(dialogMaxWidth - 2 * dialogHorizontalMargin);
|
||||
_messageVisual->setAlign(Graphics::kTextAlignCenter);
|
||||
|
||||
_confirmLabelVisual = new VisualText(gfx);
|
||||
_confirmLabelVisual->setColor(_textColor);
|
||||
_confirmLabelVisual->setTargetWidth(96);
|
||||
|
||||
_cancelLabelVisual = new VisualText(gfx);
|
||||
_cancelLabelVisual->setColor(_textColor);
|
||||
_cancelLabelVisual->setTargetWidth(96);
|
||||
}
|
||||
|
||||
DialogBox::~DialogBox() {
|
||||
close();
|
||||
|
||||
delete _messageVisual;
|
||||
delete _confirmLabelVisual;
|
||||
delete _cancelLabelVisual;
|
||||
|
||||
delete _background;
|
||||
|
||||
delete _surfaceRenderer;
|
||||
}
|
||||
|
||||
void DialogBox::open(const Common::String &message, ConfirmCallback *confirmCallback,
|
||||
const Common::String &confirmLabel, const Common::String &cancelLabel) {
|
||||
assert(confirmCallback);
|
||||
|
||||
_visible = true;
|
||||
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
_cursor->setMouseHint("");
|
||||
|
||||
_messageVisual->setText(message);
|
||||
_confirmLabelVisual->setText(confirmLabel);
|
||||
_cancelLabelVisual->setText(cancelLabel);
|
||||
|
||||
_confirmCallback = confirmCallback;
|
||||
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
void DialogBox::close() {
|
||||
freeForeground();
|
||||
|
||||
delete _confirmCallback;
|
||||
_confirmCallback = nullptr;
|
||||
|
||||
_visible = false;
|
||||
}
|
||||
|
||||
void DialogBox::recomputeLayout() {
|
||||
freeForeground();
|
||||
|
||||
_messageRect = _messageVisual->getRect();
|
||||
|
||||
uint buttonY = dialogVerticalMargin * 2 + _messageRect.height();
|
||||
uint32 dialogWidth = _messageRect.width() + 2 * dialogHorizontalMargin;
|
||||
|
||||
// The button size is computed based on the largest label
|
||||
Common::Rect labelSize = _confirmLabelVisual->getRect();
|
||||
labelSize.extend(_cancelLabelVisual->getRect());
|
||||
|
||||
_confirmButtonRect = Common::Rect(
|
||||
labelSize.width() + buttonHorizontalMargin * 2,
|
||||
labelSize.height() + buttonVerticalMargin * 2);
|
||||
|
||||
_cancelButtonRect = Common::Rect(
|
||||
labelSize.width() + buttonHorizontalMargin * 2,
|
||||
labelSize.height() + buttonVerticalMargin * 2);
|
||||
|
||||
uint buttonSpacing;
|
||||
if (dialogWidth > (uint32)(_confirmButtonRect.width() + _cancelButtonRect.width())) {
|
||||
buttonSpacing = (dialogWidth - _confirmButtonRect.width() - _cancelButtonRect.width()) / 3;
|
||||
} else {
|
||||
buttonSpacing = buttonHorizontalMargin;
|
||||
dialogWidth = buttonSpacing * 3 + _confirmButtonRect.width() + _cancelButtonRect.width();
|
||||
}
|
||||
|
||||
_messageRect.translate((dialogWidth - _messageRect.width()) / 2, dialogVerticalMargin);
|
||||
|
||||
_confirmButtonRect.translate(buttonSpacing, buttonY);
|
||||
_cancelButtonRect.translate(buttonSpacing * 2 + _confirmButtonRect.width(), buttonY);
|
||||
|
||||
uint32 dialogHeight = _confirmButtonRect.bottom + dialogVerticalMargin;
|
||||
|
||||
Graphics::Surface foreground;
|
||||
foreground.create(dialogWidth, dialogHeight, Gfx::Driver::getRGBAPixelFormat());
|
||||
|
||||
drawBevel(&foreground, Common::Rect(0, 0, foreground.w, foreground.h));
|
||||
|
||||
uint32 buttonColor = foreground.format.RGBToColor(0, 0, 0);
|
||||
foreground.fillRect(_confirmButtonRect, buttonColor);
|
||||
foreground.fillRect(_cancelButtonRect, buttonColor);
|
||||
|
||||
drawBevel(&foreground, _confirmButtonRect);
|
||||
drawBevel(&foreground, _cancelButtonRect);
|
||||
|
||||
_foreground = _gfx->createBitmap(&foreground);
|
||||
_foreground->setSamplingFilter(Gfx::Bitmap::kLinear);
|
||||
|
||||
foreground.free();
|
||||
|
||||
Common::Point screenCenter(Gfx::Driver::kOriginalWidth / 2, Gfx::Driver::kOriginalHeight / 2);
|
||||
_position = Common::Rect::center(screenCenter.x, screenCenter.y,
|
||||
_foreground->width(), _foreground->height());
|
||||
}
|
||||
|
||||
void DialogBox::freeForeground() {
|
||||
delete _foreground;
|
||||
_foreground = nullptr;
|
||||
|
||||
if (_messageVisual) {
|
||||
_messageVisual->reset();
|
||||
}
|
||||
|
||||
if (_confirmLabelVisual) {
|
||||
_confirmLabelVisual->reset();
|
||||
}
|
||||
|
||||
if (_cancelLabelVisual) {
|
||||
_cancelLabelVisual->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogBox::onScreenChanged() {
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
void DialogBox::onClick(const Common::Point &pos) {
|
||||
if (_cancelButtonRect.contains(pos)) {
|
||||
close();
|
||||
} else if (_confirmButtonRect.contains(pos)) {
|
||||
assert(_confirmCallback);
|
||||
|
||||
(*_confirmCallback)();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogBox::onKeyPress(const Common::CustomEventType customType) {
|
||||
if (customType == kActionSkip) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::Bitmap *DialogBox::loadBackground(Gfx::Driver *gfx) {
|
||||
Common::PEResources *executable = new Common::PEResources();
|
||||
if (!executable->loadFromEXE("game.exe") && !executable->loadFromEXE("game.dll")) {
|
||||
warning("Unable to load 'game.exe' to read the modal dialog background image");
|
||||
delete executable;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// As of Aug 2021:
|
||||
// Steam version of The Longest Journey is 1.0.0.161 "Enno's two-CD Version"
|
||||
// GOG version of The Longest Journey is 1.0.0.142 "RC1" (Special Build: "Paper Sun")
|
||||
// Steam's game.exe does not contain a valid resource for the background bitmap id 147
|
||||
// so we skip trying to retrieve it.
|
||||
if (_vm->getGameFlags() & GF_MISSING_EXE_RESOURCES) {
|
||||
warning("Steam version does not contain the modal dialog background bitmap in 'game.exe'. Using fallback color for dialog background...");
|
||||
delete executable;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = executable->getResource(Common::kWinBitmap, 147);
|
||||
if (!stream) {
|
||||
warning("Unable to find the modal dialog background bitmap in 'game.exe'");
|
||||
delete executable;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint32 bitmapWithHeaderLen = stream->size() + 14;
|
||||
byte *bitmapWithHeader = new byte[bitmapWithHeaderLen];
|
||||
|
||||
Common::MemoryWriteStream bitmapWithHeaderWriteStream(bitmapWithHeader, bitmapWithHeaderLen);
|
||||
bitmapWithHeaderWriteStream.write("BM", 2);
|
||||
bitmapWithHeaderWriteStream.writeUint32LE(bitmapWithHeaderLen); // Filesize
|
||||
bitmapWithHeaderWriteStream.writeUint32LE(0); // res1 & res2
|
||||
bitmapWithHeaderWriteStream.writeUint32LE(0x436); // image offset
|
||||
|
||||
stream->read(bitmapWithHeader + 14, stream->size());
|
||||
delete stream;
|
||||
delete executable;
|
||||
|
||||
Common::MemoryReadStream bitmapWithHeaderReadStream(bitmapWithHeader, bitmapWithHeaderLen);
|
||||
|
||||
Image::BitmapDecoder decoder;
|
||||
if (!decoder.loadStream(bitmapWithHeaderReadStream)) {
|
||||
warning("Unable decode the modal dialog background bitmap from 'game.exe'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
delete[] bitmapWithHeader;
|
||||
|
||||
return gfx->createBitmap(decoder.getSurface(), decoder.getPalette().data());
|
||||
}
|
||||
|
||||
void DialogBox::onRender() {
|
||||
if (_background) {
|
||||
uint32 backgroundRepeatX = ceil(_foreground->width() / (float)_background->width());
|
||||
for (uint i = 0; i < backgroundRepeatX; i++) {
|
||||
_surfaceRenderer->render(_background, Common::Point(i * _background->width(), 0));
|
||||
}
|
||||
} else {
|
||||
_surfaceRenderer->fill(_backgroundColor, Common::Point(0, 0), _foreground->width(), _foreground->height());
|
||||
}
|
||||
|
||||
_surfaceRenderer->render(_foreground, Common::Point(0, 0));
|
||||
|
||||
_messageVisual->render(Common::Point(_messageRect.left, _messageRect.top));
|
||||
|
||||
Common::Rect confirmLabelRect = centerRect(_confirmButtonRect, _confirmLabelVisual->getRect());
|
||||
Common::Rect cancelLabelRect = centerRect(_cancelButtonRect, _cancelLabelVisual->getRect());
|
||||
|
||||
_confirmLabelVisual->render(Common::Point(confirmLabelRect.left, confirmLabelRect.top));
|
||||
_cancelLabelVisual->render(Common::Point(cancelLabelRect.left, cancelLabelRect.top));
|
||||
}
|
||||
|
||||
void DialogBox::drawBevel(Graphics::Surface *surface, const Common::Rect &rect) {
|
||||
uint32 topColor1 = surface->format.RGBToColor(191, 191, 191);
|
||||
uint32 topColor2 = surface->format.RGBToColor(159, 159, 159);
|
||||
uint32 bottomColor1 = surface->format.RGBToColor(64, 64, 64);
|
||||
uint32 bottomColor2 = surface->format.RGBToColor(96, 96, 96);
|
||||
|
||||
// Top
|
||||
surface->drawLine(rect.left, rect.top, rect.right - 1, rect.top, topColor1);
|
||||
surface->drawLine(rect.left + 1, rect.top + 1, rect.right - 2, rect.top + 1, topColor2);
|
||||
|
||||
// Left
|
||||
surface->drawLine(rect.left, rect.top, rect.left, rect.bottom - 1, topColor1);
|
||||
surface->drawLine(rect.left + 1, rect.top + 1, rect.left + 1, rect.bottom - 2, topColor2);
|
||||
|
||||
// Right
|
||||
surface->drawLine(rect.right - 1, rect.top, rect.right - 1, rect.bottom - 1, bottomColor2);
|
||||
surface->drawLine(rect.right - 2, rect.top + 1, rect.right - 2, rect.bottom - 2, bottomColor1);
|
||||
|
||||
// Bottom
|
||||
surface->drawLine(rect.left, rect.bottom - 1, rect.right - 1, rect.bottom - 1, bottomColor2);
|
||||
surface->drawLine(rect.left + 1, rect.bottom - 2, rect.right - 2, rect.bottom - 2, bottomColor1);
|
||||
}
|
||||
|
||||
Common::Rect DialogBox::centerRect(const Common::Rect &container, const Common::Rect &size) {
|
||||
Common::Point center(
|
||||
(container.left + container.right) / 2,
|
||||
(container.top + container.bottom) / 2);
|
||||
|
||||
return Common::Rect::center(
|
||||
center.x, center.y,
|
||||
size.width(), size.height());
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
101
engines/stark/ui/dialogbox.h
Normal file
101
engines/stark/ui/dialogbox.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 STARK_UI_DIALOG_BOX_H
|
||||
#define STARK_UI_DIALOG_BOX_H
|
||||
|
||||
#include "engines/stark/stark.h"
|
||||
#include "engines/stark/ui/window.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#include "common/keyboard.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class SurfaceRenderer;
|
||||
class Bitmap;
|
||||
}
|
||||
|
||||
typedef Common::Functor0<void> ConfirmCallback;
|
||||
|
||||
class VisualText;
|
||||
|
||||
/**
|
||||
* A confirmation dialog with two buttons
|
||||
*
|
||||
* The cancel button closes the dialog without action.
|
||||
* The confirm button executes a callback.
|
||||
*/
|
||||
class DialogBox : public Window {
|
||||
public:
|
||||
DialogBox(StarkEngine *vm, Gfx::Driver *gfx, Cursor *cursor);
|
||||
~DialogBox() override;
|
||||
|
||||
/** Make the dialog visible with the specified message */
|
||||
void open(const Common::String &message, ConfirmCallback *confirmCallback,
|
||||
const Common::String &confirmLabel, const Common::String &cancelLabel);
|
||||
|
||||
/** Hide the dialog performing no action */
|
||||
void close();
|
||||
|
||||
/** Called when the screen resolution changes */
|
||||
void onScreenChanged();
|
||||
|
||||
/** Called when a keyboard key is pressed and the dialog is active */
|
||||
void onKeyPress(const Common::CustomEventType customType);
|
||||
|
||||
protected:
|
||||
void onRender() override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
|
||||
private:
|
||||
Gfx::Bitmap *loadBackground(Gfx::Driver *gfx);
|
||||
static void drawBevel(Graphics::Surface *surface, const Common::Rect &rect);
|
||||
static Common::Rect centerRect(const Common::Rect &container, const Common::Rect &size);
|
||||
|
||||
void freeForeground();
|
||||
void recomputeLayout();
|
||||
|
||||
StarkEngine *_vm;
|
||||
|
||||
Gfx::SurfaceRenderer *_surfaceRenderer;
|
||||
Gfx::Bitmap *_background;
|
||||
Gfx::Bitmap *_foreground;
|
||||
|
||||
VisualText *_messageVisual;
|
||||
VisualText *_confirmLabelVisual;
|
||||
VisualText *_cancelLabelVisual;
|
||||
|
||||
Common::Rect _confirmButtonRect;
|
||||
Common::Rect _cancelButtonRect;
|
||||
Common::Rect _messageRect;
|
||||
|
||||
const Gfx::Color _textColor = Gfx::Color(0xFF, 0xFF, 0xFF);
|
||||
const Gfx::Color _backgroundColor = Gfx::Color(26, 28, 57);
|
||||
|
||||
ConfirmCallback *_confirmCallback;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_DIALOG_BOX_H
|
||||
390
engines/stark/ui/menu/dialogmenu.cpp
Normal file
390
engines/stark/ui/menu/dialogmenu.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/* 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 "engines/stark/ui/menu/dialogmenu.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/diary.h"
|
||||
#include "engines/stark/services/gamechapter.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
DialogScreen::DialogScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "DiaryLog", Screen::kScreenDialog),
|
||||
_startTitleIndex(0), _nextTitleIndex(0),
|
||||
_startLineIndex(0), _nextLineIndex(0),
|
||||
_curMaxChapter(0), _curLogIndex(0),
|
||||
_indexFrame(nullptr),
|
||||
_logFrame(nullptr),
|
||||
_chapterTitleTexts(),
|
||||
_prevTitleIndexStack(),
|
||||
_prevLineIndexStack() {
|
||||
}
|
||||
|
||||
DialogScreen::~DialogScreen() {
|
||||
freeResources();
|
||||
}
|
||||
|
||||
void DialogScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BGImage",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Return",
|
||||
CLICK_HANDLER(DialogScreen, backHandler),
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(DialogScreen, backHandler),
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"IndexBack",
|
||||
CLICK_HANDLER(DialogScreen, indexBackHandler),
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"IndexNext",
|
||||
CLICK_HANDLER(DialogScreen, indexNextHandler),
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"LogBack",
|
||||
CLICK_HANDLER(DialogScreen, logBackHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Index",
|
||||
CLICK_HANDLER(DialogScreen, backIndexHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"LogNext",
|
||||
CLICK_HANDLER(DialogScreen, logNextHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
for (uint i = 1; i < _widgets.size(); ++i) {
|
||||
_widgets[i]->setupSounds(0, 1);
|
||||
}
|
||||
|
||||
Resources::Location *location = StarkStaticProvider->getLocation();
|
||||
_indexFrame = location->getRenderEntryByName("IndexFrame");
|
||||
_logFrame = location->getRenderEntryByName("LogFrame");
|
||||
|
||||
_nextTitleIndex = 0;
|
||||
loadIndex();
|
||||
}
|
||||
|
||||
void DialogScreen::close() {
|
||||
freeResources();
|
||||
StaticLocationScreen::close();
|
||||
}
|
||||
|
||||
void DialogScreen::freeResources() {
|
||||
freeChapterTitleTexts();
|
||||
freeDialogLineTexts();
|
||||
_prevTitleIndexStack.clear();
|
||||
_prevLineIndexStack.clear();
|
||||
}
|
||||
|
||||
void DialogScreen::onScreenChanged() {
|
||||
StaticLocationScreen::onScreenChanged();
|
||||
for (uint i = 0; i < _chapterTitleTexts.size(); ++i) {
|
||||
_chapterTitleTexts[i]->onScreenChanged();
|
||||
}
|
||||
for (uint i = 0; i < _dialogLineTexts.size(); ++i) {
|
||||
_dialogLineTexts[i]->onScreenChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogScreen::onDialogClick(uint logIndex) {
|
||||
freeLogTitleWidgets();
|
||||
freeChapterTitleTexts();
|
||||
|
||||
_widgets[kWidgetIndexBack]->setVisible(false);
|
||||
_widgets[kWidgetIndexNext]->setVisible(false);
|
||||
_widgets[kWidgetIndex]->setVisible(true);
|
||||
|
||||
_nextLineIndex = 0;
|
||||
_curLogIndex = logIndex;
|
||||
loadDialog();
|
||||
}
|
||||
|
||||
void DialogScreen::onRender() {
|
||||
StaticLocationScreen::onRender();
|
||||
for (uint i = 0; i < _chapterTitleTexts.size(); ++i) {
|
||||
_chapterTitleTexts[i]->render();
|
||||
}
|
||||
for (uint i = 0; i < _dialogLineTexts.size(); ++i) {
|
||||
_dialogLineTexts[i]->render();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogScreen::loadIndex() {
|
||||
static const int space = 4;
|
||||
|
||||
freeLogTitleWidgets();
|
||||
freeChapterTitleTexts();
|
||||
|
||||
_startTitleIndex = _nextTitleIndex;
|
||||
|
||||
Common::Point pos = _indexFrame->getPosition();
|
||||
int bottom = _indexFrame->getText()->getTargetHeight() + pos.y;
|
||||
int height;
|
||||
ChapterTitleText *chapterTitleText;
|
||||
DialogTitleWidget *dialogTitleWidget;
|
||||
|
||||
_curMaxChapter = 99;
|
||||
while (_nextTitleIndex < StarkDiary->countDialog()) {
|
||||
height = 0;
|
||||
chapterTitleText = nullptr;
|
||||
|
||||
dialogTitleWidget = new DialogTitleWidget(this, _gfx, _nextTitleIndex);
|
||||
height += dialogTitleWidget->getHeight();
|
||||
|
||||
if (dialogTitleWidget->getChapter() != _curMaxChapter) {
|
||||
_curMaxChapter = dialogTitleWidget->getChapter();
|
||||
chapterTitleText = new ChapterTitleText(_gfx, _curMaxChapter);
|
||||
height += chapterTitleText->getHeight() + 2 * space;
|
||||
}
|
||||
|
||||
if (pos.y + height > bottom) {
|
||||
delete dialogTitleWidget;
|
||||
delete chapterTitleText;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chapterTitleText) {
|
||||
pos.y += space;
|
||||
chapterTitleText->setPosition(pos);
|
||||
pos.y += chapterTitleText->getHeight() + space;
|
||||
}
|
||||
dialogTitleWidget->setPosition(pos);
|
||||
pos.y += dialogTitleWidget->getHeight() + space;
|
||||
|
||||
_widgets.push_back(dialogTitleWidget);
|
||||
if (chapterTitleText) {
|
||||
_chapterTitleTexts.push_back(chapterTitleText);
|
||||
}
|
||||
|
||||
++_nextTitleIndex;
|
||||
|
||||
if (pos.y > bottom) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_widgets[kWidgetIndexBack]->setVisible(_startTitleIndex > 0);
|
||||
_widgets[kWidgetIndexNext]->setVisible(_nextTitleIndex < StarkDiary->countDialog());
|
||||
}
|
||||
|
||||
void DialogScreen::loadDialog() {
|
||||
static const int space = 16;
|
||||
|
||||
freeDialogLineTexts();
|
||||
|
||||
_startLineIndex = _nextLineIndex;
|
||||
|
||||
Common::Point pos = _logFrame->getPosition();
|
||||
int boxWidth = _logFrame->getText()->getTargetWidth();
|
||||
int bottom = _logFrame->getText()->getTargetHeight() + pos.y;
|
||||
int height;
|
||||
DialogLineText *dialogLineText;
|
||||
|
||||
Diary::ConversationLog dialog = StarkDiary->getDialog(_curLogIndex);
|
||||
|
||||
while (_nextLineIndex < dialog.lines.size()) {
|
||||
height = 0;
|
||||
|
||||
dialogLineText = new DialogLineText(_gfx, _curLogIndex, _nextLineIndex, boxWidth);
|
||||
height = dialogLineText->getHeight();
|
||||
|
||||
if (pos.y + height + space > bottom) {
|
||||
delete dialogLineText;
|
||||
break;
|
||||
}
|
||||
|
||||
dialogLineText->setPosition(pos);
|
||||
_dialogLineTexts.push_back(dialogLineText);
|
||||
|
||||
pos.y += height + space;
|
||||
|
||||
++_nextLineIndex;
|
||||
}
|
||||
|
||||
_widgets[kWidgetLogBack]->setVisible(_startLineIndex > 0);
|
||||
_widgets[kWidgetLogNext]->setVisible(_nextLineIndex < dialog.lines.size());
|
||||
}
|
||||
|
||||
void DialogScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void DialogScreen::indexBackHandler() {
|
||||
_nextTitleIndex = _prevTitleIndexStack.back();
|
||||
_prevTitleIndexStack.pop_back();
|
||||
loadIndex();
|
||||
}
|
||||
|
||||
void DialogScreen::indexNextHandler() {
|
||||
_prevTitleIndexStack.push_back(_startTitleIndex);
|
||||
loadIndex();
|
||||
}
|
||||
|
||||
void DialogScreen::logBackHandler() {
|
||||
_nextLineIndex = _prevLineIndexStack.back();
|
||||
_prevLineIndexStack.pop_back();
|
||||
loadDialog();
|
||||
}
|
||||
|
||||
void DialogScreen::backIndexHandler() {
|
||||
freeDialogLineTexts();
|
||||
_prevLineIndexStack.clear();
|
||||
|
||||
_widgets[kWidgetLogBack]->setVisible(false);
|
||||
_widgets[kWidgetLogNext]->setVisible(false);
|
||||
_widgets[kWidgetIndex]->setVisible(false);
|
||||
|
||||
_nextTitleIndex = _startTitleIndex;
|
||||
loadIndex();
|
||||
}
|
||||
|
||||
void DialogScreen::logNextHandler() {
|
||||
_prevLineIndexStack.push_back(_startLineIndex);
|
||||
loadDialog();
|
||||
}
|
||||
|
||||
void DialogScreen::freeLogTitleWidgets() {
|
||||
uint size = _widgets.size();
|
||||
for (uint i = 0; i < size - _dialogTitleWidgetOffset; ++i) {
|
||||
delete _widgets.back();
|
||||
_widgets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogScreen::freeChapterTitleTexts() {
|
||||
for (uint i = 0; i < _chapterTitleTexts.size(); ++i) {
|
||||
delete _chapterTitleTexts[i];
|
||||
}
|
||||
_chapterTitleTexts.clear();
|
||||
}
|
||||
|
||||
void DialogScreen::freeDialogLineTexts() {
|
||||
for (uint i = 0; i < _dialogLineTexts.size(); ++i) {
|
||||
delete _dialogLineTexts[i];
|
||||
}
|
||||
_dialogLineTexts.clear();
|
||||
}
|
||||
|
||||
ChapterTitleText::ChapterTitleText(Gfx::Driver *gfx, uint chapter) :
|
||||
_pos(), _text(gfx) {
|
||||
Common::String text = Common::String::format(
|
||||
"%s: %s",
|
||||
StarkGameChapter->getChapterTitle(chapter).c_str(),
|
||||
StarkGameChapter->getChapterSubtitle(chapter).c_str());
|
||||
text.toUppercase();
|
||||
|
||||
_text.setText(text);
|
||||
_text.setColor(_color);
|
||||
_text.setFont(FontProvider::kCustomFont, 5);
|
||||
}
|
||||
|
||||
DialogLineText::DialogLineText(Gfx::Driver *gfx, uint logIndex, uint lineIndex, uint boxWidth) :
|
||||
_namePos(), _linePos(), _boxWidth(boxWidth),
|
||||
_nameText(gfx), _lineText(gfx) {
|
||||
Diary::ConversationLogLine logLine = StarkDiary->getDialog(logIndex).lines[lineIndex];
|
||||
|
||||
Common::String name = StarkGlobal->getCharacterName(logLine.characterId);
|
||||
name.toUppercase();
|
||||
|
||||
Gfx::Color color = logLine.characterId == StarkGlobal->getApril()->getCharacterIndex() ? _textColorApril : _textColorNormal;
|
||||
|
||||
_nameText.setText(name);
|
||||
_nameText.setColor(color);
|
||||
_nameText.setFont(FontProvider::kCustomFont, 5);
|
||||
|
||||
_lineText.setTargetWidth(_boxWidth);
|
||||
_lineText.setText(logLine.line);
|
||||
_lineText.setColor(color);
|
||||
_lineText.setFont(FontProvider::kCustomFont, 3);
|
||||
|
||||
Common::Rect rect = _nameText.getRect();
|
||||
_nameWidth = rect.right - rect.left;
|
||||
_nameHeight = rect.bottom - rect.top;
|
||||
|
||||
rect = _lineText.getRect();
|
||||
_lineHeight = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
void DialogLineText::setPosition(const Common::Point &pos) {
|
||||
static const uint space = 4;
|
||||
|
||||
_namePos.x = pos.x + (_boxWidth - _nameWidth) / 2;
|
||||
_namePos.y = pos.y;
|
||||
|
||||
_linePos.x = pos.x;
|
||||
_linePos.y = pos.y + _nameHeight + space;
|
||||
}
|
||||
|
||||
DialogTitleWidget::DialogTitleWidget(DialogScreen *screen, Gfx::Driver *gfx, uint logIndex) :
|
||||
StaticLocationWidget(nullptr, nullptr, nullptr),
|
||||
_logIndex(logIndex), _pos(), _text(gfx),
|
||||
_screen(screen) {
|
||||
const Diary::ConversationLog &dialog = StarkDiary->getDialog(_logIndex);
|
||||
|
||||
_chapter = dialog.chapter;
|
||||
|
||||
_text.setText(dialog.title);
|
||||
_text.setColor(_textColorDefault);
|
||||
_text.setFont(FontProvider::kCustomFont, 3);
|
||||
|
||||
Common::Rect rect = _text.getRect();
|
||||
_width = rect.right - rect.left;
|
||||
_height = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
bool DialogTitleWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
return mousePos.x >= _pos.x && mousePos.x <= _pos.x + _width &&
|
||||
mousePos.y >= _pos.y && mousePos.y <= _pos.y + _height;
|
||||
}
|
||||
|
||||
void DialogTitleWidget::onClick() {
|
||||
_screen->onDialogClick(_logIndex);
|
||||
}
|
||||
|
||||
void DialogTitleWidget::onScreenChanged() {
|
||||
_text.reset();
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
174
engines/stark/ui/menu/dialogmenu.h
Normal file
174
engines/stark/ui/menu/dialogmenu.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/* 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 STARK_UI_MENU_DIALOG_H
|
||||
#define STARK_UI_MENU_DIALOG_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ChapterTitleText;
|
||||
class DialogLineText;
|
||||
|
||||
/**
|
||||
* The conversation log menu
|
||||
*/
|
||||
class DialogScreen : public StaticLocationScreen {
|
||||
public:
|
||||
DialogScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~DialogScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
void onScreenChanged() override;
|
||||
|
||||
void onDialogClick(uint logIndex);
|
||||
|
||||
protected:
|
||||
// Window API
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
static const uint _dialogTitleWidgetOffset = 8;
|
||||
|
||||
enum WidgetIndex {
|
||||
kWidgetIndexBack = 3,
|
||||
kWidgetIndexNext = 4,
|
||||
kWidgetLogBack = 5,
|
||||
kWidgetIndex = 6,
|
||||
kWidgetLogNext = 7
|
||||
};
|
||||
|
||||
Gfx::RenderEntry *_indexFrame, *_logFrame;
|
||||
uint _startTitleIndex, _nextTitleIndex, _startLineIndex, _nextLineIndex;
|
||||
uint _curMaxChapter, _curLogIndex;
|
||||
Common::Array<ChapterTitleText *> _chapterTitleTexts;
|
||||
Common::Array<uint> _prevTitleIndexStack;
|
||||
Common::Array<DialogLineText *> _dialogLineTexts;
|
||||
Common::Array<uint> _prevLineIndexStack;
|
||||
|
||||
void loadIndex();
|
||||
void loadDialog();
|
||||
|
||||
void backHandler();
|
||||
void indexBackHandler();
|
||||
void indexNextHandler();
|
||||
void logBackHandler();
|
||||
void backIndexHandler();
|
||||
void logNextHandler();
|
||||
|
||||
void freeLogTitleWidgets();
|
||||
void freeChapterTitleTexts();
|
||||
void freeDialogLineTexts();
|
||||
void freeResources();
|
||||
};
|
||||
|
||||
/**
|
||||
* The chapter title displayed on the menu
|
||||
*/
|
||||
class ChapterTitleText {
|
||||
public:
|
||||
ChapterTitleText(Gfx::Driver *gfx, uint chapter);
|
||||
~ChapterTitleText() {}
|
||||
|
||||
void setPosition(const Common::Point &pos) { _pos = pos; }
|
||||
uint getHeight() { return _text.getRect().bottom - _text.getRect().top; }
|
||||
|
||||
void render() { _text.render(_pos); }
|
||||
void onScreenChanged() { _text.reset(); }
|
||||
|
||||
private:
|
||||
const Gfx::Color _color = Gfx::Color(0x68, 0x05, 0x04);
|
||||
|
||||
Common::Point _pos;
|
||||
VisualText _text;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dialog text displayed
|
||||
*/
|
||||
class DialogLineText {
|
||||
public:
|
||||
DialogLineText(Gfx::Driver *gfx, uint logIndex, uint lineIndex, uint boxWidth);
|
||||
~DialogLineText() {}
|
||||
|
||||
void setPosition(const Common::Point &pos);
|
||||
uint getHeight() { return _nameHeight + _lineHeight + 4; }
|
||||
|
||||
void render() {
|
||||
_nameText.render(_namePos);
|
||||
_lineText.render(_linePos);
|
||||
}
|
||||
|
||||
void onScreenChanged() {
|
||||
_nameText.reset();
|
||||
_lineText.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
const Gfx::Color _textColorApril = Gfx::Color(0x68, 0x05, 0x04);
|
||||
const Gfx::Color _textColorNormal = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
|
||||
Common::Point _namePos, _linePos;
|
||||
VisualText _nameText, _lineText;
|
||||
|
||||
uint _nameWidth, _nameHeight, _lineHeight, _boxWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dialog widget
|
||||
*/
|
||||
class DialogTitleWidget : public StaticLocationWidget {
|
||||
public:
|
||||
DialogTitleWidget(DialogScreen *screen, Gfx::Driver *gfx, uint logIndex);
|
||||
virtual ~DialogTitleWidget() {}
|
||||
|
||||
void setPosition(const Common::Point &pos) { _pos = pos; }
|
||||
uint getHeight() { return _height; }
|
||||
uint getChapter() { return _chapter; }
|
||||
|
||||
// StaticLocationWidget API
|
||||
void render() override { _text.render(_pos); }
|
||||
bool isMouseInside(const Common::Point &mousePos) const override;
|
||||
void onClick() override;
|
||||
void onMouseMove(const Common::Point &mousePos) override {
|
||||
_text.setColor(isMouseInside(mousePos) ? _textColorHovered : _textColorDefault);
|
||||
}
|
||||
void onScreenChanged() override;
|
||||
|
||||
private:
|
||||
const Gfx::Color _textColorHovered = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
const Gfx::Color _textColorDefault = Gfx::Color(0x00, 0x00, 0x00);
|
||||
|
||||
uint _logIndex, _chapter;
|
||||
int _width, _height;
|
||||
Common::Point _pos;
|
||||
VisualText _text;
|
||||
|
||||
DialogScreen *_screen;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_DIALOG_H
|
||||
145
engines/stark/ui/menu/diaryindex.cpp
Normal file
145
engines/stark/ui/menu/diaryindex.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/* 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 "engines/stark/ui/menu/diaryindex.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/diary.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/gamemessage.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
#include "gui/saveload.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
DiaryIndexScreen::DiaryIndexScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "DiaryIndexLocation", Screen::kScreenDiaryIndex) {
|
||||
}
|
||||
|
||||
DiaryIndexScreen::~DiaryIndexScreen() {
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BGImage",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"SaveGame",
|
||||
CLICK_HANDLER(DiaryIndexScreen, saveHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
_widgets.back()->setVisible(g_engine->canSaveGameStateCurrently());
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Continue",
|
||||
CLICK_HANDLER(DiaryIndexScreen, loadHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Options",
|
||||
CLICK_HANDLER(DiaryIndexScreen, settingsHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Log",
|
||||
CLICK_HANDLER(DiaryIndexScreen, dialogHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Fmv",
|
||||
CLICK_HANDLER(DiaryIndexScreen, fmvHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Diary",
|
||||
CLICK_HANDLER(DiaryIndexScreen, diaryHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
_widgets.back()->setVisible(StarkDiary->isEnabled());
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Return",
|
||||
CLICK_HANDLER(DiaryIndexScreen, backHandler),
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Quit",
|
||||
CLICK_HANDLER(DiaryIndexScreen, quitHandler),
|
||||
MOVE_HANDLER(DiaryIndexScreen, widgetTextColorHandler)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(DiaryIndexScreen, backHandler),
|
||||
nullptr));
|
||||
|
||||
for (uint i = 1; i < _widgets.size(); i++) {
|
||||
// The background image is intentionally ignored
|
||||
_widgets[i]->setupSounds(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::widgetTextColorHandler(StaticLocationWidget &widget, const Common::Point &mousePos) {
|
||||
if (widget.isVisible()) {
|
||||
Gfx::Color textColor = widget.isMouseInside(mousePos) ? _textColorHovered : _textColorDefault;
|
||||
widget.setTextColor(textColor);
|
||||
}
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::settingsHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenSettingsMenu);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::fmvHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenFMVMenu);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::quitHandler() {
|
||||
StarkUserInterface->confirm(GameMessage::kQuitGamePrompt, StarkUserInterface,
|
||||
&UserInterface::requestQuitToMainMenu);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::loadHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenLoadMenu);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::saveHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenSaveMenu);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::diaryHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenDiaryPages);
|
||||
}
|
||||
|
||||
void DiaryIndexScreen::dialogHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenDialog);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
57
engines/stark/ui/menu/diaryindex.h
Normal file
57
engines/stark/ui/menu/diaryindex.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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 STARK_UI_MENU_DIARY_INDEX_H
|
||||
#define STARK_UI_MENU_DIARY_INDEX_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* The diary index is the in-game menu
|
||||
*/
|
||||
class DiaryIndexScreen : public StaticLocationScreen {
|
||||
public:
|
||||
DiaryIndexScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~DiaryIndexScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
|
||||
private:
|
||||
void widgetTextColorHandler(StaticLocationWidget &widget, const Common::Point &mousePos);
|
||||
void backHandler();
|
||||
void settingsHandler();
|
||||
void fmvHandler();
|
||||
void loadHandler();
|
||||
void saveHandler();
|
||||
void diaryHandler();
|
||||
void dialogHandler();
|
||||
void quitHandler();
|
||||
|
||||
const Gfx::Color _textColorHovered = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
const Gfx::Color _textColorDefault = Gfx::Color(0x00, 0x00, 0x00);
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_DIARY_INDEX_H
|
||||
122
engines/stark/ui/menu/diarypages.cpp
Normal file
122
engines/stark/ui/menu/diarypages.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/* 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 "engines/stark/ui/menu/diarypages.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/diary.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/layer.h"
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
DiaryPagesScreen::DiaryPagesScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "DiaryPages", Screen::kScreenDiaryPages),
|
||||
_page(0) {
|
||||
}
|
||||
|
||||
DiaryPagesScreen::~DiaryPagesScreen() {
|
||||
}
|
||||
|
||||
void DiaryPagesScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BGImage",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Return",
|
||||
CLICK_HANDLER(DiaryPagesScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"GoBack",
|
||||
CLICK_HANDLER(DiaryPagesScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(DiaryPagesScreen, prevPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Next",
|
||||
CLICK_HANDLER(DiaryPagesScreen, nextPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_page = StarkDiary->getPageIndex();
|
||||
|
||||
if (StarkDiary->countDiary() > 0) {
|
||||
_widgets.push_back(new DiaryWidget(_page));
|
||||
_widgets[kWidgetBack]->setVisible(_page > 0);
|
||||
_widgets[kWidgetNext]->setVisible(_page < StarkDiary->countDiary() - 1);
|
||||
} else {
|
||||
_widgets[kWidgetBack]->setVisible(false);
|
||||
_widgets[kWidgetNext]->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DiaryPagesScreen::close() {
|
||||
StarkDiary->setPageIndex(_page);
|
||||
StarkDiary->setDiaryAllRead();
|
||||
StaticLocationScreen::close();
|
||||
}
|
||||
|
||||
void DiaryPagesScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void DiaryPagesScreen::changePage(uint page) {
|
||||
assert(page < StarkDiary->countDiary());
|
||||
|
||||
delete _widgets.back();
|
||||
_widgets.pop_back();
|
||||
_widgets.push_back(new DiaryWidget(page));
|
||||
|
||||
_widgets[kWidgetBack]->setVisible(page > 0);
|
||||
_widgets[kWidgetNext]->setVisible(page < StarkDiary->countDiary() - 1);
|
||||
|
||||
_page = page;
|
||||
}
|
||||
|
||||
DiaryWidget::DiaryWidget(uint diaryIndex):
|
||||
StaticLocationWidget(nullptr, nullptr, nullptr) {
|
||||
Resources::Location *location = StarkStaticProvider->getLocation();
|
||||
Resources::Layer *layer = location->getLayerByName(StarkDiary->getDiary(diaryIndex));
|
||||
|
||||
if (!layer) {
|
||||
debug("Unable to retrieve diary in layer %s.", StarkDiary->getDiary(diaryIndex).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Diary page layer contains only one item, the text
|
||||
_renderEntry = layer->listRenderEntries()[0];
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
67
engines/stark/ui/menu/diarypages.h
Normal file
67
engines/stark/ui/menu/diarypages.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 STARK_UI_MENU_DIARY_PAGES_H
|
||||
#define STARK_UI_MENU_DIARY_PAGES_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* The screen where diary pages are shown in the game
|
||||
*/
|
||||
class DiaryPagesScreen : public StaticLocationScreen {
|
||||
public:
|
||||
DiaryPagesScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~DiaryPagesScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
|
||||
private:
|
||||
enum WidgetIndex {
|
||||
kWidgetBack = 3,
|
||||
kWidgetNext = 4
|
||||
};
|
||||
|
||||
uint _page;
|
||||
|
||||
void backHandler();
|
||||
|
||||
void prevPageHandler() { changePage(_page - 1); }
|
||||
void nextPageHandler() { changePage(_page + 1); }
|
||||
void changePage(uint page);
|
||||
};
|
||||
|
||||
/**
|
||||
* The widget displaying diary text
|
||||
*/
|
||||
class DiaryWidget : public StaticLocationWidget {
|
||||
public:
|
||||
DiaryWidget(uint diaryIndex);
|
||||
~DiaryWidget() {}
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_DIARY_PAGES_H
|
||||
197
engines/stark/ui/menu/fmvmenu.cpp
Normal file
197
engines/stark/ui/menu/fmvmenu.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 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 "engines/stark/ui/menu/fmvmenu.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/diary.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FMVMenuScreen::FMVMenuScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "DiaryFMV", Screen::kScreenFMVMenu),
|
||||
_fmvWidgets(),
|
||||
_page(0),
|
||||
_maxPage(0) {
|
||||
_formatRectPos = Common::Point(202, 61);
|
||||
_fontHeight = 16;
|
||||
_fmvPerPage = 18;
|
||||
}
|
||||
|
||||
FMVMenuScreen::~FMVMenuScreen() {
|
||||
freeFMVWidgets();
|
||||
}
|
||||
|
||||
void FMVMenuScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BGImage",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Return",
|
||||
CLICK_HANDLER(FMVMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(FMVMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"PreviousPage",
|
||||
CLICK_HANDLER(FMVMenuScreen, prevPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"NextPage",
|
||||
CLICK_HANDLER(FMVMenuScreen, nextPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
// Acquire data for FMVWidget from the format rectangle
|
||||
|
||||
int formatRectHeight = 379;
|
||||
|
||||
Resources::Location *location = StarkStaticProvider->getLocation();
|
||||
Gfx::RenderEntry *formatRect = location->getRenderEntryByName("FormatRectangle");
|
||||
|
||||
if (formatRect) {
|
||||
_formatRectPos = formatRect->getPosition();
|
||||
formatRectHeight = formatRect->getText()->getTargetHeight();
|
||||
|
||||
// The format rectangle contains one line,
|
||||
// which can be used to retrieve the font's height
|
||||
Common::Rect textRect = formatRect->getText()->getRect();
|
||||
_fontHeight = textRect.bottom - textRect.top;
|
||||
|
||||
_fmvPerPage = formatRectHeight / (_fontHeight + 4);
|
||||
}
|
||||
|
||||
_maxPage = StarkDiary->countFMV() / _fmvPerPage;
|
||||
changePage(0);
|
||||
}
|
||||
|
||||
void FMVMenuScreen::close() {
|
||||
freeFMVWidgets();
|
||||
StaticLocationScreen::close();
|
||||
}
|
||||
|
||||
void FMVMenuScreen::onScreenChanged() {
|
||||
StaticLocationScreen::onScreenChanged();
|
||||
for (uint i = 0; i < _fmvWidgets.size(); ++i) {
|
||||
_fmvWidgets[i]->onScreenChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void FMVMenuScreen::onMouseMove(const Common::Point &pos) {
|
||||
StaticLocationScreen::onMouseMove(pos);
|
||||
for (uint i = 0; i < _fmvWidgets.size(); ++i) {
|
||||
_fmvWidgets[i]->onMouseMove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void FMVMenuScreen::onClick(const Common::Point &pos) {
|
||||
StaticLocationScreen::onClick(pos);
|
||||
for (uint i = 0; i < _fmvWidgets.size(); ++i) {
|
||||
if (_fmvWidgets[i]->isMouseInside(pos)) {
|
||||
_fmvWidgets[i]->onClick();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FMVMenuScreen::onRender() {
|
||||
StaticLocationScreen::onRender();
|
||||
for (uint i = 0; i < _fmvWidgets.size(); ++i) {
|
||||
_fmvWidgets[i]->render();
|
||||
}
|
||||
}
|
||||
|
||||
void FMVMenuScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void FMVMenuScreen::freeFMVWidgets() {
|
||||
for (uint i = 0; i < _fmvWidgets.size(); ++i) {
|
||||
delete _fmvWidgets[i];
|
||||
}
|
||||
_fmvWidgets.clear();
|
||||
}
|
||||
|
||||
void FMVMenuScreen::loadFMVWidgets(uint page) {
|
||||
uint start = page * _fmvPerPage;
|
||||
uint end = start + _fmvPerPage;
|
||||
end = end < StarkDiary->countFMV() ? end : StarkDiary->countFMV();
|
||||
|
||||
for (uint i = start; i < end; ++i) {
|
||||
_fmvWidgets.push_back(new FMVWidget(_gfx, i));
|
||||
}
|
||||
}
|
||||
|
||||
void FMVMenuScreen::changePage(uint page) {
|
||||
assert(page <= _maxPage);
|
||||
|
||||
freeFMVWidgets();
|
||||
loadFMVWidgets(page);
|
||||
|
||||
_widgets[kWidgetPrevious]->setVisible(page > 0);
|
||||
_widgets[kWidgetNext]->setVisible(page < _maxPage);
|
||||
|
||||
_page = page;
|
||||
}
|
||||
|
||||
FMVWidget::FMVWidget(Gfx::Driver *gfx, uint fmvIndex) :
|
||||
_filename(StarkDiary->getFMVFilename(fmvIndex)),
|
||||
_title(gfx) {
|
||||
_title.setText(StarkDiary->getFMVTitle(fmvIndex));
|
||||
_title.setColor(_textColorDefault);
|
||||
_title.setFont(FontProvider::kCustomFont, 3);
|
||||
|
||||
Common::Rect rect = _title.getRect();
|
||||
_width = rect.right - rect.left;
|
||||
|
||||
_formatRectPos = Common::Point(202, 61);
|
||||
_fontHeight = 16;
|
||||
_fmvPerPage = 18;
|
||||
|
||||
_position.x = _formatRectPos.x;
|
||||
_position.y = _formatRectPos.y + (fmvIndex % _fmvPerPage) * (_fontHeight + 4);
|
||||
}
|
||||
|
||||
void FMVWidget::onClick() {
|
||||
StarkUserInterface->requestFMVPlayback(_filename);
|
||||
}
|
||||
|
||||
bool FMVWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
return mousePos.x >= _position.x && mousePos.x <= _position.x + _width &&
|
||||
mousePos.y >= _position.y && mousePos.y <= _position.y + _fontHeight;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
116
engines/stark/ui/menu/fmvmenu.h
Normal file
116
engines/stark/ui/menu/fmvmenu.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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 STARK_UI_MENU_FMV_MENU_H
|
||||
#define STARK_UI_MENU_FMV_MENU_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
#include "common/path.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class FMVWidget;
|
||||
|
||||
/**
|
||||
* The video replay menu
|
||||
*/
|
||||
class FMVMenuScreen : public StaticLocationScreen {
|
||||
public:
|
||||
FMVMenuScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~FMVMenuScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
void onScreenChanged() override;
|
||||
|
||||
protected:
|
||||
// Window API
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
enum WidgetIndex {
|
||||
kWidgetPrevious = 3,
|
||||
kWidgetNext = 4
|
||||
};
|
||||
|
||||
Common::Array<FMVWidget *> _fmvWidgets;
|
||||
|
||||
Common::Point _formatRectPos;
|
||||
int _fontHeight;
|
||||
uint _fmvPerPage;
|
||||
|
||||
// Count from zero
|
||||
uint _page, _maxPage;
|
||||
|
||||
void backHandler();
|
||||
void prevPageHandler() { changePage(_page - 1); }
|
||||
void nextPageHandler() { changePage(_page + 1); }
|
||||
|
||||
void freeFMVWidgets();
|
||||
void loadFMVWidgets(uint page);
|
||||
void changePage(uint page);
|
||||
};
|
||||
|
||||
/**
|
||||
* The widget for FMV entry, specifically built for FMVMenuScreen
|
||||
*/
|
||||
class FMVWidget {
|
||||
public:
|
||||
FMVWidget(Gfx::Driver *gfx, uint fmvIndex);
|
||||
~FMVWidget() {}
|
||||
|
||||
void render() { _title.render(_position); }
|
||||
|
||||
bool isMouseInside(const Common::Point &mousePos) const;
|
||||
|
||||
void onMouseMove(const Common::Point &mousePos) {
|
||||
setTextColor(isMouseInside(mousePos) ? _textColorHovered : _textColorDefault);
|
||||
}
|
||||
|
||||
void onClick();
|
||||
|
||||
void setTextColor(const Gfx::Color &color) { _title.setColor(color); }
|
||||
|
||||
void onScreenChanged() { _title.reset(); }
|
||||
|
||||
private:
|
||||
const Gfx::Color _textColorHovered = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
const Gfx::Color _textColorDefault = Gfx::Color(0x00, 0x00, 0x00);
|
||||
|
||||
Common::Point _formatRectPos;
|
||||
int _fontHeight;
|
||||
uint _fmvPerPage;
|
||||
|
||||
const Common::Path &_filename;
|
||||
VisualText _title;
|
||||
|
||||
int _width;
|
||||
Common::Point _position;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_FMV_MENU_H
|
||||
268
engines/stark/ui/menu/locationscreen.cpp
Normal file
268
engines/stark/ui/menu/locationscreen.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/* 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 "engines/stark/ui/menu/locationscreen.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
StaticLocationScreen::StaticLocationScreen(Gfx::Driver *gfx, Cursor *cursor,
|
||||
const char *locationName, Screen::Name screenName) :
|
||||
SingleWindowScreen(screenName, gfx, cursor),
|
||||
_locationName(locationName),
|
||||
_location(nullptr),
|
||||
_hoveredWidgetIndex(-1) {
|
||||
_position = Common::Rect(Gfx::Driver::kOriginalWidth, Gfx::Driver::kOriginalHeight);
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
StaticLocationScreen::~StaticLocationScreen() {
|
||||
freeWidgets();
|
||||
}
|
||||
|
||||
void StaticLocationScreen::open() {
|
||||
_location = StarkStaticProvider->loadLocation(_locationName);
|
||||
}
|
||||
|
||||
void StaticLocationScreen::close() {
|
||||
freeWidgets();
|
||||
|
||||
StarkStaticProvider->unloadLocation(_location);
|
||||
_location = nullptr;
|
||||
}
|
||||
|
||||
void StaticLocationScreen::freeWidgets() {
|
||||
for (uint i = 0; i < _widgets.size(); i++) {
|
||||
delete _widgets[i];
|
||||
}
|
||||
|
||||
_widgets.clear();
|
||||
_hoveredWidgetIndex = -1;
|
||||
}
|
||||
|
||||
void StaticLocationScreen::onGameLoop() {
|
||||
for (uint i = 0; i < _widgets.size(); i++) {
|
||||
StaticLocationWidget *widget = _widgets[i];
|
||||
if (widget->isVisible()) {
|
||||
widget->onGameLoop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationScreen::onMouseMove(const Common::Point &pos) {
|
||||
int newHoveredWidget = -1;
|
||||
|
||||
for (uint i = 0; i < _widgets.size(); i++) {
|
||||
StaticLocationWidget *widget = _widgets[i];
|
||||
widget->onMouseMove(pos);
|
||||
|
||||
if (widget->isVisible() && widget->isMouseInside(pos)) {
|
||||
newHoveredWidget = i;
|
||||
}
|
||||
}
|
||||
|
||||
// The first widget is always the background. It is ignored below.
|
||||
|
||||
if (newHoveredWidget != _hoveredWidgetIndex) {
|
||||
if (_hoveredWidgetIndex > 0 && uint(_hoveredWidgetIndex) < _widgets.size()) {
|
||||
_widgets[_hoveredWidgetIndex]->onMouseLeave();
|
||||
}
|
||||
|
||||
if (newHoveredWidget > 0) {
|
||||
_widgets[newHoveredWidget]->onMouseEnter();
|
||||
}
|
||||
|
||||
_hoveredWidgetIndex = newHoveredWidget;
|
||||
}
|
||||
|
||||
_cursor->setCursorType(_hoveredWidgetIndex > 0 ? Cursor::kActive : Cursor::kDefault);
|
||||
}
|
||||
|
||||
void StaticLocationScreen::onClick(const Common::Point &pos) {
|
||||
for (uint i = 1; i < _widgets.size(); i++) {
|
||||
StaticLocationWidget *widget = _widgets[i];
|
||||
if (widget->isVisible() && widget->isMouseInside(pos)) {
|
||||
widget->onClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationScreen::onRender() {
|
||||
for (uint i = 0; i < _widgets.size(); i++) {
|
||||
if (_widgets[i]->isVisible()) {
|
||||
_widgets[i]->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationScreen::onScreenChanged() {
|
||||
for (uint i = 0; i < _widgets.size(); i++) {
|
||||
_widgets[i]->onScreenChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationScreen::waitForSoundsToComplete() {
|
||||
while (g_system->getMixer()->hasActiveChannelOfType(Audio::Mixer::kSFXSoundType)) {
|
||||
StarkGfx->clearScreen();
|
||||
g_system->delayMillis(10);
|
||||
StarkGfx->flipBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
StaticLocationWidget::StaticLocationWidget(const char *renderEntryName, WidgetOnClickCallback *onClickCallback,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback):
|
||||
_onClick(onClickCallback),
|
||||
_onMouseMove(onMouseMoveCallback),
|
||||
_renderEntry(nullptr),
|
||||
_item(nullptr),
|
||||
_soundMouseEnter(nullptr),
|
||||
_soundMouseClick(nullptr),
|
||||
_visible(true) {
|
||||
if (renderEntryName) {
|
||||
Resources::Location *location = StarkStaticProvider->getLocation();
|
||||
_renderEntry = location->getRenderEntryByName(renderEntryName);
|
||||
|
||||
if (_renderEntry == nullptr) {
|
||||
debug("Widget disabled: unable to find render entry with name '%s' in location '%s'",
|
||||
renderEntryName, location->getName().c_str());
|
||||
setVisible(false);
|
||||
} else {
|
||||
_item = _renderEntry->getOwner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::render() {
|
||||
if (_renderEntry) {
|
||||
_renderEntry->render();
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticLocationWidget::isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
|
||||
void StaticLocationWidget::setVisible(bool visible) {
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
bool StaticLocationWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
if (!_renderEntry) return false;
|
||||
|
||||
Common::Point relativePosition;
|
||||
return _renderEntry->containsPoint(mousePos, relativePosition, Common::Rect());
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onClick() {
|
||||
onMouseLeave();
|
||||
|
||||
if (_soundMouseClick) {
|
||||
_soundMouseClick->play();
|
||||
_soundMouseClick->setStopOnDestroy(false);
|
||||
}
|
||||
|
||||
if (_onClick) {
|
||||
(*_onClick)();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onGameLoop() {
|
||||
if (_item) {
|
||||
_item->onGameLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onMouseEnter() {
|
||||
if (_soundMouseEnter && !_soundMouseEnter->isPlaying()) {
|
||||
_soundMouseEnter->play();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onMouseLeave() {
|
||||
if (_soundMouseEnter) {
|
||||
_soundMouseEnter->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::setupSounds(int16 enterSound, int16 clickSound) {
|
||||
if (enterSound != -1) {
|
||||
_soundMouseEnter = StarkStaticProvider->getLocationSound(enterSound);
|
||||
}
|
||||
|
||||
if (clickSound != -1) {
|
||||
_soundMouseClick = StarkStaticProvider->getLocationSound(clickSound);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::setTextColor(const Gfx::Color &textColor) {
|
||||
if (!_renderEntry) return;
|
||||
|
||||
VisualText *text = _renderEntry->getText();
|
||||
assert(text);
|
||||
|
||||
text->setColor(textColor);
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onScreenChanged() {
|
||||
if (!_renderEntry) return;
|
||||
|
||||
VisualText *text = _renderEntry->getText();
|
||||
if (text) {
|
||||
text->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void StaticLocationWidget::onMouseMove(const Common::Point &mousePos) {
|
||||
if (_onMouseMove) {
|
||||
(*_onMouseMove)(*this, mousePos);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point StaticLocationWidget::getPosition() const {
|
||||
if (_renderEntry) {
|
||||
return _renderEntry->getPosition();
|
||||
}
|
||||
|
||||
return Common::Point(0, 0);
|
||||
}
|
||||
|
||||
StaticLocationWidget::~StaticLocationWidget() {
|
||||
delete _onClick;
|
||||
delete _onMouseMove;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
156
engines/stark/ui/menu/locationscreen.h
Normal file
156
engines/stark/ui/menu/locationscreen.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* 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 STARK_UI_MENU_LOCATION_SCREEN_H
|
||||
#define STARK_UI_MENU_LOCATION_SCREEN_H
|
||||
|
||||
#include "engines/stark/ui/screen.h"
|
||||
#include "engines/stark/ui/window.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class RenderEntry;
|
||||
}
|
||||
|
||||
namespace Resources {
|
||||
class ItemVisual;
|
||||
class Location;
|
||||
class Sound;
|
||||
}
|
||||
|
||||
class StaticLocationWidget;
|
||||
|
||||
/**
|
||||
* Abstract user interface screen using resources from a static Location sub-tree
|
||||
*/
|
||||
class StaticLocationScreen : public SingleWindowScreen {
|
||||
public:
|
||||
StaticLocationScreen(Gfx::Driver *gfx, Cursor *cursor, const char *locationName, Screen::Name screenName);
|
||||
~StaticLocationScreen() override;
|
||||
|
||||
// Screen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
void onScreenChanged() override;
|
||||
|
||||
/**
|
||||
* Wait for all effect sounds to complete
|
||||
*
|
||||
* Used to ensure the button press sounds are no longer
|
||||
* playing before performing the next action that
|
||||
* would produce a sound.
|
||||
*/
|
||||
void waitForSoundsToComplete();
|
||||
|
||||
protected:
|
||||
// Window API
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
|
||||
Common::Array<StaticLocationWidget *> _widgets;
|
||||
|
||||
private:
|
||||
const char *_locationName;
|
||||
Resources::Location *_location;
|
||||
int _hoveredWidgetIndex;
|
||||
|
||||
void freeWidgets();
|
||||
};
|
||||
|
||||
typedef Common::Functor0<void> WidgetOnClickCallback;
|
||||
typedef Common::Functor2<StaticLocationWidget &, const Common::Point &, void> WidgetOnMouseMoveCallback;
|
||||
#define CLICK_HANDLER(cls, method) new Common::Functor0Mem<void, cls>(this, &cls::method)
|
||||
#define MOVE_HANDLER(cls, method) new Common::Functor2Mem<StaticLocationWidget &, const Common::Point &, void, cls>(this, &cls::method)
|
||||
|
||||
/**
|
||||
* User interface widget bound to a Location RenderEntry
|
||||
*
|
||||
* Also used without bounding the RenderEntry, as a base class
|
||||
*/
|
||||
class StaticLocationWidget {
|
||||
public:
|
||||
StaticLocationWidget(const char *renderEntryName, WidgetOnClickCallback *onClickCallback,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback);
|
||||
virtual ~StaticLocationWidget();
|
||||
|
||||
/** Draw the widget */
|
||||
virtual void render();
|
||||
|
||||
/** Is the specified point inside the widget? */
|
||||
virtual bool isMouseInside(const Common::Point &mousePos) const;
|
||||
|
||||
/** Called when the widget is clicked */
|
||||
virtual void onClick();
|
||||
|
||||
/** Called when the mouse hovers the widget */
|
||||
virtual void onMouseMove(const Common::Point &mousePos);
|
||||
|
||||
/** Called when the mouse's left button just gets up */
|
||||
virtual void onMouseUp() {}
|
||||
|
||||
/** Called when the screen's resolution is changed */
|
||||
virtual void onScreenChanged();
|
||||
|
||||
/** Lookup sounds in the static location for use when hovering / clicking the widget */
|
||||
void setupSounds(int16 enterSound, int16 clickSound);
|
||||
|
||||
/**
|
||||
* Override the text color
|
||||
*
|
||||
* Only applies for widget referring to a RenderEntry for a text visual
|
||||
*/
|
||||
void setTextColor(const Gfx::Color &textColor);
|
||||
|
||||
/** Widgets must be visible to be rendered and interactive */
|
||||
bool isVisible() const;
|
||||
void setVisible(bool visible);
|
||||
|
||||
/** Per frame widget state update callback */
|
||||
void onGameLoop();
|
||||
|
||||
/** Called when the mouse enters the widget */
|
||||
void onMouseEnter();
|
||||
|
||||
/** Called when the mouse leaves the widget */
|
||||
void onMouseLeave();
|
||||
|
||||
protected:
|
||||
Common::Point getPosition() const;
|
||||
Gfx::RenderEntry *_renderEntry;
|
||||
|
||||
private:
|
||||
Resources::ItemVisual *_item;
|
||||
bool _visible;
|
||||
|
||||
Resources::Sound *_soundMouseEnter;
|
||||
Resources::Sound *_soundMouseClick;
|
||||
|
||||
WidgetOnClickCallback *_onClick;
|
||||
WidgetOnMouseMoveCallback *_onMouseMove;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_LOCATION_SCREEN_H
|
||||
197
engines/stark/ui/menu/mainmenu.cpp
Normal file
197
engines/stark/ui/menu/mainmenu.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 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 "engines/stark/ui/menu/mainmenu.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/resourceprovider.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/services/gamemessage.h"
|
||||
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
#include "engines/stark/scene.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
MainMenuScreen::MainMenuScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "MainMenuLocation", Screen::kScreenMainMenu) {
|
||||
}
|
||||
|
||||
MainMenuScreen::~MainMenuScreen() {
|
||||
}
|
||||
|
||||
void MainMenuScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BGImage",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"NewGame",
|
||||
CLICK_HANDLER(MainMenuScreen, newGameHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kNewGame>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Continue",
|
||||
CLICK_HANDLER(MainMenuScreen, loadHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kContinue>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Options",
|
||||
CLICK_HANDLER(MainMenuScreen, settingsHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kOption>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Box",
|
||||
CLICK_HANDLER(MainMenuScreen, boxHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kBox>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Quit",
|
||||
CLICK_HANDLER(MainMenuScreen, quitHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kQuit>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"OptionHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BeginHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"ContinueHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"BoxHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"QuitHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Credits",
|
||||
CLICK_HANDLER(MainMenuScreen, creditsHandler),
|
||||
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kCredits>)));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"CreditHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new VersionInfoText());
|
||||
}
|
||||
|
||||
template<MainMenuScreen::HelpTextIndex N>
|
||||
void MainMenuScreen::helpTextHandler(StaticLocationWidget &widget, const Common::Point &mousePos) {
|
||||
if (widget.isVisible()) {
|
||||
_widgets[N]->setVisible(widget.isMouseInside(mousePos));
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuScreen::creditsHandler() {
|
||||
if (!StarkSettings->isDemo()) {
|
||||
waitForSoundsToComplete();
|
||||
StarkUserInterface->requestFMVPlayback("0e02.bbb");
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuScreen::newGameHandler() {
|
||||
waitForSoundsToComplete();
|
||||
|
||||
StarkUserInterface->changeScreen(kScreenGame);
|
||||
|
||||
StarkResourceProvider->initGlobal();
|
||||
|
||||
if (ConfMan.hasKey("startup_chapter")) {
|
||||
StarkGlobal->setCurrentChapter(ConfMan.getInt("startup_chapter"));
|
||||
} else {
|
||||
StarkGlobal->setCurrentChapter(0);
|
||||
}
|
||||
|
||||
if (ConfMan.hasKey("startup_level") && ConfMan.hasKey("startup_location")) {
|
||||
uint levelIndex = strtol(ConfMan.get("startup_level").c_str(), nullptr, 16);
|
||||
uint locationIndex = strtol(ConfMan.get("startup_location").c_str(), nullptr, 16);
|
||||
StarkResourceProvider->requestLocationChange(levelIndex, locationIndex);
|
||||
} else {
|
||||
if (StarkSettings->isDemo()) {
|
||||
StarkResourceProvider->requestLocationChange(0x4f, 0x00);
|
||||
} else {
|
||||
// Start us up at the house of all worlds
|
||||
StarkResourceProvider->requestLocationChange(0x45, 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuScreen::loadHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenLoadMenu);
|
||||
}
|
||||
|
||||
void MainMenuScreen::settingsHandler() {
|
||||
StarkUserInterface->changeScreen(Screen::kScreenSettingsMenu);
|
||||
}
|
||||
|
||||
void MainMenuScreen::boxHandler() {
|
||||
if (!StarkSettings->isDemo() && StarkSettings->hasBookOfSecrets()) {
|
||||
StarkUserInterface->changeScreen(kScreenGame);
|
||||
StarkResourceProvider->initGlobal();
|
||||
StarkResourceProvider->requestLocationChange(0x7c, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenuScreen::quitHandler() {
|
||||
StarkUserInterface->confirm(GameMessage::kQuitGamePrompt, StarkUserInterface,
|
||||
&UserInterface::notifyShouldExit);
|
||||
}
|
||||
|
||||
VersionInfoText::VersionInfoText() :
|
||||
StaticLocationWidget("VERSION INFO", nullptr, nullptr) {
|
||||
Common::String text = _copyrightSymbol + Common::String("1999 Funcom");
|
||||
_renderEntry->getText()->setText(text);
|
||||
_renderEntry->setPosition(Common::Point(_posX, _posY));
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
76
engines/stark/ui/menu/mainmenu.h
Normal file
76
engines/stark/ui/menu/mainmenu.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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 STARK_UI_MENU_MAIN_MENU_H
|
||||
#define STARK_UI_MENU_MAIN_MENU_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* The main menu of the game when it is opened
|
||||
*/
|
||||
class MainMenuScreen : public StaticLocationScreen {
|
||||
public:
|
||||
MainMenuScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~MainMenuScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
|
||||
private:
|
||||
enum HelpTextIndex {
|
||||
kNewGame = 7,
|
||||
kContinue = 8,
|
||||
kOption = 6,
|
||||
kBox = 9,
|
||||
kQuit = 10,
|
||||
kCredits = 12
|
||||
};
|
||||
|
||||
template<HelpTextIndex N>
|
||||
void helpTextHandler(StaticLocationWidget &widget, const Common::Point &mousePos);
|
||||
|
||||
void newGameHandler();
|
||||
void loadHandler();
|
||||
void creditsHandler();
|
||||
void settingsHandler();
|
||||
void boxHandler();
|
||||
void quitHandler();
|
||||
};
|
||||
|
||||
/**
|
||||
* The version info text
|
||||
*/
|
||||
class VersionInfoText : public StaticLocationWidget {
|
||||
public:
|
||||
VersionInfoText();
|
||||
virtual ~VersionInfoText() {}
|
||||
|
||||
private:
|
||||
static const char _copyrightSymbol = char(0xA9);
|
||||
static const int _posX = 16, _posY = 419;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_MAIN_MENU_H
|
||||
360
engines/stark/ui/menu/saveloadmenu.cpp
Normal file
360
engines/stark/ui/menu/saveloadmenu.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/* 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 "engines/stark/ui/menu/saveloadmenu.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/services/gamechapter.h"
|
||||
#include "engines/stark/services/gamemessage.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
#include "engines/stark/stark.h"
|
||||
#include "engines/stark/savemetadata.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
SaveLoadMenuScreen::SaveLoadMenuScreen(Gfx::Driver *gfx, Cursor *cursor, Screen::Name screenName) :
|
||||
StaticLocationScreen(gfx, cursor, "LoadSaveLocation", screenName),
|
||||
_page(0),
|
||||
_maxPage(10) {
|
||||
}
|
||||
|
||||
SaveLoadMenuScreen::~SaveLoadMenuScreen() {
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_maxPage = computeMaxPage();
|
||||
|
||||
_page = StarkSettings->getIntSetting(Settings::kSaveLoadPage);
|
||||
if (_page > _maxPage) {
|
||||
_page = _maxPage;
|
||||
}
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"loadsavebg",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"back to index",
|
||||
CLICK_HANDLER(SaveLoadMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Cancel",
|
||||
CLICK_HANDLER(SaveLoadMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"SaveText",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"LoadText",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(SaveLoadMenuScreen, prevPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
_widgets.back()->setTextColor(Gfx::Color(0, 0, 0));
|
||||
_widgets.back()->setVisible(_page > 0);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Next",
|
||||
CLICK_HANDLER(SaveLoadMenuScreen, nextPageHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(0, 1);
|
||||
_widgets.back()->setTextColor(Gfx::Color(0, 0, 0));
|
||||
_widgets.back()->setVisible(_page < _maxPage);
|
||||
|
||||
loadSaveData(_page);
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::close() {
|
||||
ConfMan.flushToDisk();
|
||||
StaticLocationScreen::close();
|
||||
}
|
||||
|
||||
int SaveLoadMenuScreen::computeMaxPage() {
|
||||
const char *target = ConfMan.getActiveDomainName().c_str();
|
||||
|
||||
int maxSlot = 0;
|
||||
Common::StringArray saves = StarkEngine::listSaveNames(target);
|
||||
for (Common::StringArray::const_iterator filename = saves.begin(); filename != saves.end(); filename++) {
|
||||
int slot = StarkEngine::getSaveNameSlot(target, *filename);
|
||||
|
||||
if (slot > maxSlot) {
|
||||
maxSlot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow using one more page than the last page with saves
|
||||
int maxPage = CLIP((maxSlot / _slotPerPage) + 1, 0, 110);
|
||||
if (maxPage < 10) {
|
||||
maxPage = 10;
|
||||
}
|
||||
|
||||
return maxPage;
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::checkError(Common::Error error) {
|
||||
if (error.getCode() != Common::kNoError) {
|
||||
GUI::MessageDialog dialog(error.getDesc());
|
||||
dialog.runModal();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::removeSaveDataWidgets() {
|
||||
assert(_widgets.size() == 7 + _slotPerPage);
|
||||
|
||||
for (int i = 0; i < _slotPerPage; ++i) {
|
||||
delete _widgets.back();
|
||||
_widgets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::loadSaveData(int page) {
|
||||
for (int i = 0; i < _slotPerPage; ++i) {
|
||||
_widgets.push_back(new SaveDataWidget(i + page * _slotPerPage, _gfx, this));
|
||||
}
|
||||
}
|
||||
|
||||
void SaveLoadMenuScreen::changePage(int page) {
|
||||
assert(page >= 0 && page <= _maxPage);
|
||||
|
||||
removeSaveDataWidgets();
|
||||
loadSaveData(page);
|
||||
|
||||
_widgets[kWidgetBack]->setVisible(page > 0);
|
||||
_widgets[kWidgetNext]->setVisible(page < _maxPage);
|
||||
|
||||
StarkSettings->setIntSetting(Settings::kSaveLoadPage, page);
|
||||
_page = page;
|
||||
}
|
||||
|
||||
void SaveMenuScreen::open() {
|
||||
SaveLoadMenuScreen::open();
|
||||
_widgets[kWidgetLoadText]->setVisible(false);
|
||||
}
|
||||
|
||||
void SaveMenuScreen::onWidgetSelected(SaveDataWidget *widget) {
|
||||
if (widget->hasSave()) {
|
||||
_slotToSaveAfterConfirm = widget;
|
||||
|
||||
Common::String format = StarkGameMessage->getTextByKey(GameMessage::kOverwriteSave);
|
||||
Common::String prompt = Common::String::format(format.c_str(), widget->getName().c_str());
|
||||
|
||||
StarkUserInterface->confirm(prompt, this, &SaveMenuScreen::saveConfirmSlot);
|
||||
} else {
|
||||
saveGameToSlot(widget);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveMenuScreen::saveConfirmSlot() {
|
||||
assert(_slotToSaveAfterConfirm);
|
||||
|
||||
saveGameToSlot(_slotToSaveAfterConfirm);
|
||||
_slotToSaveAfterConfirm = nullptr;
|
||||
}
|
||||
|
||||
void SaveMenuScreen::saveGameToSlot(SaveDataWidget *widget) {
|
||||
checkError(g_engine->saveGameState(widget->getSlot(), StarkGameChapter->getCurrentChapterTitle()));
|
||||
|
||||
// Freeze the screen for a while to let the user notice the change
|
||||
widget->loadSaveDataElements();
|
||||
render();
|
||||
StarkGfx->flipBuffer();
|
||||
g_system->delayMillis(100);
|
||||
render();
|
||||
StarkGfx->flipBuffer();
|
||||
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
void LoadMenuScreen::open() {
|
||||
SaveLoadMenuScreen::open();
|
||||
_widgets[kWidgetSaveText]->setVisible(false);
|
||||
}
|
||||
|
||||
void LoadMenuScreen::onWidgetSelected(SaveDataWidget *widget) {
|
||||
if (!StarkGlobal->getCurrent()) {
|
||||
checkError(g_engine->loadGameState(widget->getSlot()));
|
||||
} else {
|
||||
_slotToLoadAfterConfirm = widget->getSlot();
|
||||
StarkUserInterface->confirm(GameMessage::kEndAndLoad, this, &LoadMenuScreen::loadConfirmSlot);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadMenuScreen::loadConfirmSlot() {
|
||||
assert(_slotToLoadAfterConfirm >= 0);
|
||||
|
||||
checkError(g_engine->loadGameState(_slotToLoadAfterConfirm));
|
||||
_slotToLoadAfterConfirm = -1;
|
||||
}
|
||||
|
||||
SaveDataWidget::SaveDataWidget(int slot, Gfx::Driver *gfx, SaveLoadMenuScreen *screen) :
|
||||
StaticLocationWidget(nullptr, nullptr, nullptr),
|
||||
_slot(slot),
|
||||
_screen(screen),
|
||||
_thumbWidth(kThumbnailWidth),
|
||||
_thumbHeight(kThumbnailHeight),
|
||||
_bitmap(gfx->createBitmap()),
|
||||
_outline(gfx->createBitmap()),
|
||||
_surfaceRenderer(gfx->createSurfaceRenderer()),
|
||||
_textDesc(gfx),
|
||||
_textTime(gfx),
|
||||
_isMouseHovered(false),
|
||||
_hasSave(false),
|
||||
_name() {
|
||||
// Load from the save data
|
||||
loadSaveDataElements();
|
||||
|
||||
_textDesc.setColor(_textColor);
|
||||
_textDesc.setFont(FontProvider::kCustomFont, 3);
|
||||
|
||||
_textTime.setColor(_textColor);
|
||||
_textTime.setFont(FontProvider::kCustomFont, 3);
|
||||
|
||||
Graphics::PixelFormat pixelFormat = Gfx::Driver::getRGBAPixelFormat();
|
||||
uint32 outlineColor = pixelFormat.ARGBToColor(
|
||||
_outlineColor.a, _outlineColor.r, _outlineColor.g, _outlineColor.b
|
||||
);
|
||||
|
||||
// Create the outline bitmap
|
||||
Graphics::Surface lineSurface;
|
||||
lineSurface.create(_thumbWidth, _thumbHeight, pixelFormat);
|
||||
lineSurface.drawThickLine(0, 0, _thumbWidth - 1, 0, 2, 2, outlineColor);
|
||||
lineSurface.drawThickLine(0, 0, 0, _thumbHeight - 1, 2, 2, outlineColor);
|
||||
lineSurface.drawThickLine(_thumbWidth - 2, 0, _thumbWidth - 2, _thumbHeight - 2, 2, 2, outlineColor);
|
||||
lineSurface.drawThickLine(0, _thumbHeight - 2, _thumbWidth - 2, _thumbHeight - 2, 2, 2, outlineColor);
|
||||
|
||||
_outline->update(&lineSurface);
|
||||
lineSurface.free();
|
||||
|
||||
// Set the position
|
||||
_thumbPos.x = 41 + (_slot % SaveLoadMenuScreen::_slotPerRow) * (_thumbWidth + 39);
|
||||
_thumbPos.y = 61 + (_slot % SaveLoadMenuScreen::_slotPerPage / SaveLoadMenuScreen::_slotPerColumn) * (_thumbHeight + 38);
|
||||
|
||||
_textDescPos.x = _thumbPos.x;
|
||||
_textDescPos.y = _thumbPos.y + _thumbHeight + 2;
|
||||
|
||||
_textTimePos.x = _thumbPos.x;
|
||||
_textTimePos.y = _textDescPos.y + 12;
|
||||
}
|
||||
|
||||
SaveDataWidget::~SaveDataWidget() {
|
||||
delete _bitmap;
|
||||
delete _outline;
|
||||
delete _surfaceRenderer;
|
||||
}
|
||||
|
||||
void SaveDataWidget::render() {
|
||||
_surfaceRenderer->render(_bitmap, _thumbPos);
|
||||
_textDesc.render(_textDescPos);
|
||||
_textTime.render(_textTimePos);
|
||||
if (_isMouseHovered) {
|
||||
_surfaceRenderer->render(_outline, _thumbPos);
|
||||
}
|
||||
}
|
||||
|
||||
bool SaveDataWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
return mousePos.x >= _thumbPos.x && mousePos.x <= _thumbPos.x + _thumbWidth &&
|
||||
mousePos.y >= _thumbPos.y && mousePos.y <= _thumbPos.y + _thumbHeight;
|
||||
}
|
||||
|
||||
void SaveDataWidget::onClick() {
|
||||
StaticLocationWidget::onClick();
|
||||
_screen->onWidgetSelected(this);
|
||||
}
|
||||
|
||||
void SaveDataWidget::onMouseMove(const Common::Point &mousePos) {
|
||||
StaticLocationWidget::onMouseMove(mousePos);
|
||||
_isMouseHovered = isMouseInside(mousePos);
|
||||
}
|
||||
|
||||
void SaveDataWidget::onScreenChanged() {
|
||||
StaticLocationWidget::onScreenChanged();
|
||||
_textDesc.reset();
|
||||
_textTime.reset();
|
||||
}
|
||||
|
||||
void SaveDataWidget::loadSaveDataElements() {
|
||||
Common::String filename = StarkEngine::formatSaveName(ConfMan.getActiveDomainName().c_str(), _slot);
|
||||
Common::InSaveFile *save = g_system->getSavefileManager()->openForLoading(filename);
|
||||
if (save) {
|
||||
_hasSave = true;
|
||||
|
||||
SaveMetadata metadata;
|
||||
StateReadStream stream(save);
|
||||
Common::ErrorCode metadataErrorCode = metadata.read(&stream, filename);
|
||||
if (metadataErrorCode != Common::kNoError) {
|
||||
error("Unable to read save metadata with error code %d.", metadataErrorCode);
|
||||
}
|
||||
|
||||
// Obtain the thumbnail
|
||||
if (metadata.version >= 9) {
|
||||
Graphics::Surface *thumb = metadata.readGameScreenThumbnail(&stream);
|
||||
_bitmap->update(thumb);
|
||||
_bitmap->setSamplingFilter(StarkSettings->getImageSamplingFilter());
|
||||
|
||||
thumb->free();
|
||||
delete thumb;
|
||||
}
|
||||
|
||||
// Obtain the text
|
||||
Common::String desc = metadata.description;
|
||||
Common::String time = Common::String::format("%02d:%02d:%02d %02d/%02d/%02d",
|
||||
metadata.saveHour, metadata.saveMinute, metadata.saveSecond,
|
||||
metadata.saveMonth, metadata.saveDay, metadata.saveYear % 100);
|
||||
|
||||
_textDesc.setText(desc);
|
||||
_textTime.setText(time);
|
||||
_name = desc + " " + time;
|
||||
} else {
|
||||
_hasSave = false;
|
||||
setVisible(_screen->isSaveMenu());
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
182
engines/stark/ui/menu/saveloadmenu.h
Normal file
182
engines/stark/ui/menu/saveloadmenu.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/* 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 STARK_UI_MENU_SAVELOAD_MENU_H
|
||||
#define STARK_UI_MENU_SAVELOAD_MENU_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class Bitmap;
|
||||
class SurfaceRenderer;
|
||||
}
|
||||
|
||||
class SaveDataWidget;
|
||||
|
||||
/**
|
||||
* The base class of the save and load menu of the game
|
||||
*/
|
||||
class SaveLoadMenuScreen : public StaticLocationScreen {
|
||||
public:
|
||||
static const int _slotPerRow = 3;
|
||||
static const int _slotPerColumn = 3;
|
||||
static const int _slotPerPage = 9;
|
||||
|
||||
SaveLoadMenuScreen(Gfx::Driver *gfx, Cursor *cursor, Screen::Name screenName);
|
||||
virtual ~SaveLoadMenuScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
|
||||
/** Called when a SaveDataWidget is selected */
|
||||
virtual void onWidgetSelected(SaveDataWidget *widget) = 0;
|
||||
|
||||
/** Check whether the menu is used for saving */
|
||||
virtual bool isSaveMenu() = 0;
|
||||
|
||||
protected:
|
||||
static void checkError(Common::Error error);
|
||||
|
||||
enum WidgetIndex {
|
||||
kWidgetSaveText = 3,
|
||||
kWidgetLoadText = 4,
|
||||
kWidgetBack = 5,
|
||||
kWidgetNext = 6
|
||||
};
|
||||
|
||||
private:
|
||||
// Start from zero
|
||||
int _page;
|
||||
int _maxPage;
|
||||
|
||||
void backHandler();
|
||||
|
||||
void prevPageHandler() { changePage(_page - 1); }
|
||||
void nextPageHandler() { changePage(_page + 1); }
|
||||
|
||||
void removeSaveDataWidgets();
|
||||
void loadSaveData(int page);
|
||||
void changePage(int page);
|
||||
int computeMaxPage();
|
||||
};
|
||||
|
||||
/**
|
||||
* The save menu of the game
|
||||
*/
|
||||
class SaveMenuScreen : public SaveLoadMenuScreen {
|
||||
public:
|
||||
SaveMenuScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
SaveLoadMenuScreen(gfx, cursor, Screen::kScreenSaveMenu),
|
||||
_slotToSaveAfterConfirm(nullptr) {}
|
||||
virtual ~SaveMenuScreen() {}
|
||||
|
||||
// SaveLoadMenuScreen API
|
||||
void open() override;
|
||||
|
||||
void onWidgetSelected(SaveDataWidget *widget) override;
|
||||
|
||||
bool isSaveMenu() override { return true; }
|
||||
|
||||
private:
|
||||
void saveGameToSlot(SaveDataWidget *widget);
|
||||
void saveConfirmSlot();
|
||||
|
||||
SaveDataWidget *_slotToSaveAfterConfirm;
|
||||
};
|
||||
|
||||
/**
|
||||
* The load menu of the game
|
||||
*/
|
||||
class LoadMenuScreen : public SaveLoadMenuScreen {
|
||||
public:
|
||||
LoadMenuScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
SaveLoadMenuScreen(gfx, cursor, Screen::kScreenLoadMenu),
|
||||
_slotToLoadAfterConfirm(-1) {}
|
||||
~LoadMenuScreen() override {}
|
||||
|
||||
// SaveLoadMenuScreen API
|
||||
void open() override;
|
||||
|
||||
void onWidgetSelected(SaveDataWidget *widget) override;
|
||||
|
||||
bool isSaveMenu() override { return false; }
|
||||
|
||||
private:
|
||||
void loadConfirmSlot();
|
||||
|
||||
int _slotToLoadAfterConfirm;
|
||||
};
|
||||
|
||||
/**
|
||||
* The widget of save data
|
||||
*/
|
||||
class SaveDataWidget : public StaticLocationWidget {
|
||||
public:
|
||||
SaveDataWidget(int slot, Gfx::Driver *gfx, SaveLoadMenuScreen *screen);
|
||||
~SaveDataWidget();
|
||||
|
||||
// StaticLocationWidget API
|
||||
void render() override;
|
||||
bool isMouseInside(const Common::Point &mousePos) const override;
|
||||
void onClick() override;
|
||||
void onMouseMove(const Common::Point &mousePos) override;
|
||||
void onScreenChanged() override;
|
||||
|
||||
int getSlot() { return _slot; }
|
||||
Common::String getName() { return _name; }
|
||||
|
||||
/** Load the thumbnail and info from the save data */
|
||||
void loadSaveDataElements();
|
||||
|
||||
/** Check whether the save slot has data */
|
||||
bool hasSave() { return _hasSave; }
|
||||
|
||||
private:
|
||||
const Gfx::Color _outlineColor = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
const Gfx::Color _textColor = Gfx::Color(0x5C, 0x48, 0x3D);
|
||||
|
||||
int _slot;
|
||||
SaveLoadMenuScreen *_screen;
|
||||
|
||||
Common::Point _thumbPos, _textDescPos, _textTimePos;
|
||||
int _thumbWidth, _thumbHeight;
|
||||
|
||||
Gfx::Bitmap *_bitmap;
|
||||
Gfx::Bitmap *_outline;
|
||||
Gfx::SurfaceRenderer *_surfaceRenderer;
|
||||
|
||||
VisualText _textDesc, _textTime;
|
||||
|
||||
bool _isMouseHovered;
|
||||
bool _hasSave;
|
||||
|
||||
Common::String _name;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_SETTING_MENU_H
|
||||
399
engines/stark/ui/menu/settingsmenu.cpp
Normal file
399
engines/stark/ui/menu/settingsmenu.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/* 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 "engines/stark/ui/menu/settingsmenu.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
SettingsMenuScreen::SettingsMenuScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
StaticLocationScreen(gfx, cursor, "OptionLocation", Screen::kScreenSettingsMenu),
|
||||
_soundManager() {
|
||||
}
|
||||
|
||||
SettingsMenuScreen::~SettingsMenuScreen() {
|
||||
}
|
||||
|
||||
void SettingsMenuScreen::open() {
|
||||
StaticLocationScreen::open();
|
||||
|
||||
_soundManager.load();
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
// This is the background image
|
||||
"The Longest Journey",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Return",
|
||||
CLICK_HANDLER(SettingsMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"Back",
|
||||
CLICK_HANDLER(SettingsMenuScreen, backHandler),
|
||||
nullptr));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"GSettings",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"AprilHighRes",
|
||||
StarkSettings->getBoolSetting(Settings::kHighModel),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kHighModel>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kHighRes>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"HighResHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"Subtitles",
|
||||
StarkSettings->getBoolSetting(Settings::kSubtitle),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kSubtitle>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kSubtitles>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"SubtitleHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"SpecialFX",
|
||||
StarkSettings->getBoolSetting(Settings::kSpecialFX),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kSpecialFX>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kSpecialFX>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"SpecialFXHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"Shadows",
|
||||
StarkSettings->getBoolSetting(Settings::kShadow),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kShadow>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kShadows>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"ShadowsHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"HighResFMV",
|
||||
StarkSettings->getBoolSetting(Settings::kHighFMV),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kHighFMV>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kHighResFMV>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
_widgets.back()->setVisible(StarkSettings->hasLowResFMV());
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"FMVHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"VSettings",
|
||||
nullptr,
|
||||
nullptr));
|
||||
|
||||
_widgets.push_back(new VolumeWidget(
|
||||
"Voice",
|
||||
_cursor,
|
||||
_soundManager, 0,
|
||||
Settings::kVoice,
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kVoice>)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"VoiceHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new VolumeWidget(
|
||||
"Music",
|
||||
_cursor,
|
||||
_soundManager, 2,
|
||||
Settings::kMusic,
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kMusic>)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"MusicHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new VolumeWidget(
|
||||
"Sfx",
|
||||
_cursor,
|
||||
_soundManager, 1,
|
||||
Settings::kSfx,
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kSfx>)));
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"SfxHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
|
||||
_widgets.push_back(new CheckboxWidget(
|
||||
"AllowFF",
|
||||
StarkSettings->getBoolSetting(Settings::kTimeSkip),
|
||||
CLICK_HANDLER(SettingsMenuScreen, flipSettingHandler<Settings::kTimeSkip>),
|
||||
MOVE_HANDLER(SettingsMenuScreen, textHandler<kAllowFF>)));
|
||||
_widgets.back()->setupSounds(3, 4);
|
||||
|
||||
_widgets.push_back(new StaticLocationWidget(
|
||||
"AllowFFHelp",
|
||||
nullptr,
|
||||
nullptr));
|
||||
_widgets.back()->setVisible(false);
|
||||
}
|
||||
|
||||
void SettingsMenuScreen::close() {
|
||||
_soundManager.close();
|
||||
ConfMan.flushToDisk();
|
||||
StaticLocationScreen::close();
|
||||
}
|
||||
|
||||
void SettingsMenuScreen::onGameLoop() {
|
||||
_soundManager.update();
|
||||
}
|
||||
|
||||
void SettingsMenuScreen::handleMouseUp() {
|
||||
if (!_widgets.empty()) {
|
||||
_soundManager.endLoop();
|
||||
_widgets[kWidgetVoice]->onMouseUp();
|
||||
_widgets[kWidgetMusic]->onMouseUp();
|
||||
_widgets[kWidgetSfx]->onMouseUp();
|
||||
}
|
||||
}
|
||||
|
||||
template<SettingsMenuScreen::HelpTextIndex N>
|
||||
void SettingsMenuScreen::textHandler(StaticLocationWidget &widget, const Common::Point &mousePos) {
|
||||
if (widget.isVisible()) {
|
||||
if (widget.isMouseInside(mousePos)) {
|
||||
widget.setTextColor(_textColorHovered);
|
||||
_widgets[N]->setVisible(true);
|
||||
} else {
|
||||
widget.setTextColor(_textColorDefault);
|
||||
_widgets[N]->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<Settings::BoolSettingIndex N>
|
||||
void SettingsMenuScreen::flipSettingHandler() {
|
||||
StarkSettings->flipSetting(N);
|
||||
}
|
||||
|
||||
void SettingsMenuScreen::backHandler() {
|
||||
StarkUserInterface->backPrevScreen();
|
||||
}
|
||||
|
||||
CheckboxWidget::CheckboxWidget(const char *renderEntryName, bool isChecked,
|
||||
WidgetOnClickCallback *onClickCallback,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback) :
|
||||
StaticLocationWidget(renderEntryName, onClickCallback, onMouseMoveCallback),
|
||||
_isChecked(isChecked) {
|
||||
// Load images
|
||||
_checkBoxImage[0] = StarkStaticProvider->getUIElement(StaticProvider::kCheckMark, 0);
|
||||
_checkBoxImage[1] = StarkStaticProvider->getUIElement(StaticProvider::kCheckMark, 1);
|
||||
_checkboxWidth = _checkBoxImage[0]->getWidth();
|
||||
_checkboxHeight = _checkBoxImage[0]->getHeight();
|
||||
_currentImage = _checkBoxImage[_isChecked];
|
||||
|
||||
// Set positions
|
||||
Common::Point textPosition = getPosition();
|
||||
_position.x = textPosition.x - _checkboxWidth - 8;
|
||||
_position.y = textPosition.y - 4;
|
||||
}
|
||||
|
||||
void CheckboxWidget::render() {
|
||||
StaticLocationWidget::render();
|
||||
_currentImage->render(_position, true);
|
||||
}
|
||||
|
||||
bool CheckboxWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
return StaticLocationWidget::isMouseInside(mousePos) || isMouseInsideCheckbox(mousePos);
|
||||
}
|
||||
|
||||
void CheckboxWidget::onClick() {
|
||||
StaticLocationWidget::onClick();
|
||||
_isChecked = !_isChecked;
|
||||
_currentImage = _checkBoxImage[_isChecked];
|
||||
}
|
||||
|
||||
bool CheckboxWidget::isMouseInsideCheckbox(const Common::Point &mousePos) const {
|
||||
return mousePos.x >= _position.x && mousePos.x <= _position.x + _checkboxWidth &&
|
||||
mousePos.y >= _position.y && mousePos.y <= _position.y + _checkboxHeight;
|
||||
}
|
||||
|
||||
VolumeWidget::VolumeWidget(const char *renderEntryName, Cursor *cursor,
|
||||
TestSoundManager &soundManager, int soundIndex,
|
||||
Settings::IntSettingIndex settingIndex,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback) :
|
||||
StaticLocationWidget(renderEntryName, nullptr, onMouseMoveCallback),
|
||||
_cursor(cursor),
|
||||
_soundManager(soundManager),
|
||||
_soundIndex(soundIndex),
|
||||
_settingIndex(settingIndex),
|
||||
_isDragged(false) {
|
||||
// Load images
|
||||
_sliderImage = StarkStaticProvider->getUIElement(StaticProvider::kVolume, 0);
|
||||
_bgImage = StarkStaticProvider->getUIElement(StaticProvider::kVolume, 1);
|
||||
_bgWidth = _bgImage->getWidth();
|
||||
_bgHeight = _bgImage->getHeight();
|
||||
_sliderWidth = _sliderImage->getWidth();
|
||||
|
||||
// Set positions
|
||||
_bgPosition.x = 313;
|
||||
_bgPosition.y = 303 + _settingIndex * 51;
|
||||
_sliderPosition.y = _bgPosition.y;
|
||||
|
||||
_minX = _bgPosition.x;
|
||||
_maxX = _bgPosition.x + _bgWidth - _sliderWidth;
|
||||
}
|
||||
|
||||
void VolumeWidget::render() {
|
||||
StaticLocationWidget::render();
|
||||
|
||||
_sliderPosition.x = volumeToX(StarkSettings->getIntSetting(_settingIndex));
|
||||
|
||||
_sliderImage->render(_sliderPosition, false);
|
||||
_bgImage->render(_bgPosition, false);
|
||||
}
|
||||
|
||||
bool VolumeWidget::isMouseInside(const Common::Point &mousePos) const {
|
||||
return StaticLocationWidget::isMouseInside(mousePos) || isMouseInsideBg(mousePos);
|
||||
}
|
||||
|
||||
void VolumeWidget::onClick() {
|
||||
if (isMouseInsideBg(_cursor->getMousePosition())) {
|
||||
_isDragged = true;
|
||||
_soundManager.play(_soundIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeWidget::onMouseMove(const Common::Point &mousePos) {
|
||||
if (isMouseInsideBg(mousePos)) {
|
||||
setTextColor(_textColorBgHovered);
|
||||
} else {
|
||||
StaticLocationWidget::onMouseMove(mousePos);
|
||||
}
|
||||
|
||||
if (_isDragged) {
|
||||
int posX = mousePos.x - _sliderWidth / 2;
|
||||
if (posX < _minX) {
|
||||
posX = _minX;
|
||||
}
|
||||
if (posX > _maxX) {
|
||||
posX = _maxX;
|
||||
}
|
||||
StarkSettings->setIntSetting(_settingIndex, xToVolume(posX));
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeWidget::onMouseUp() {
|
||||
_isDragged = false;
|
||||
}
|
||||
|
||||
bool VolumeWidget::isMouseInsideBg(const Common::Point &mousePos) const {
|
||||
return mousePos.x >= _bgPosition.x && mousePos.x <= _bgPosition.x + _bgWidth &&
|
||||
mousePos.y >= _bgPosition.y && mousePos.y <= _bgPosition.y + _bgHeight;
|
||||
}
|
||||
|
||||
TestSoundManager::TestSoundManager() :
|
||||
_currentSound(nullptr),
|
||||
_isLopping(false) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sounds[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TestSoundManager::load() {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sounds[i] = StarkStaticProvider->getLocationSound(i);
|
||||
_sounds[i]->setLooping(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TestSoundManager::close() {
|
||||
stop();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
_sounds[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TestSoundManager::play(int index) {
|
||||
stop();
|
||||
_currentSound = _sounds[index];
|
||||
if (_currentSound) {
|
||||
_currentSound->play();
|
||||
_isLopping = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TestSoundManager::endLoop() {
|
||||
_isLopping = false;
|
||||
}
|
||||
|
||||
void TestSoundManager::stop() {
|
||||
if (_currentSound) {
|
||||
_currentSound->stop();
|
||||
_currentSound = nullptr;
|
||||
}
|
||||
_isLopping = false;
|
||||
}
|
||||
|
||||
void TestSoundManager::update() {
|
||||
if (_currentSound && !_currentSound->isPlaying()) {
|
||||
if (_isLopping) {
|
||||
_currentSound->play();
|
||||
} else {
|
||||
_currentSound->stop();
|
||||
_currentSound = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
188
engines/stark/ui/menu/settingsmenu.h
Normal file
188
engines/stark/ui/menu/settingsmenu.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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 STARK_UI_MENU_SETTINGS_MENU_H
|
||||
#define STARK_UI_MENU_SETTINGS_MENU_H
|
||||
|
||||
#include "engines/stark/ui/menu/locationscreen.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualImageXMG;
|
||||
|
||||
/**
|
||||
* Manager of test sound
|
||||
*/
|
||||
class TestSoundManager {
|
||||
public:
|
||||
TestSoundManager();
|
||||
~TestSoundManager() {}
|
||||
|
||||
/** Load sounds **/
|
||||
void load();
|
||||
|
||||
/** Close the sound manager and reset pointers **/
|
||||
void close();
|
||||
|
||||
/** play a specific sound in a loop */
|
||||
void play(int index);
|
||||
|
||||
/** request to end the playing loop */
|
||||
void endLoop();
|
||||
|
||||
/** stop any currently playing sound */
|
||||
void stop();
|
||||
|
||||
/** update on game frame */
|
||||
void update();
|
||||
|
||||
private:
|
||||
Resources::Sound *_currentSound;
|
||||
Resources::Sound *_sounds[3];
|
||||
bool _isLopping;
|
||||
};
|
||||
|
||||
/**
|
||||
* The setting menu of the game
|
||||
*/
|
||||
class SettingsMenuScreen : public StaticLocationScreen {
|
||||
public:
|
||||
SettingsMenuScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~SettingsMenuScreen();
|
||||
|
||||
// StaticLocationScreen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
void onGameLoop() override;
|
||||
|
||||
void handleMouseUp();
|
||||
|
||||
private:
|
||||
enum HelpTextIndex {
|
||||
kHighRes = 5,
|
||||
kSubtitles = 7,
|
||||
kSpecialFX = 9,
|
||||
kShadows = 11,
|
||||
kHighResFMV = 13,
|
||||
kVoice = 16,
|
||||
kMusic = 18,
|
||||
kSfx = 20,
|
||||
kAllowFF = 22
|
||||
};
|
||||
|
||||
enum WidgetIndex {
|
||||
kWidgetVoice = 15,
|
||||
kWidgetMusic = 17,
|
||||
kWidgetSfx = 19
|
||||
};
|
||||
|
||||
template<HelpTextIndex N>
|
||||
void textHandler(StaticLocationWidget &widget, const Common::Point &mousePos);
|
||||
|
||||
template<Settings::BoolSettingIndex N>
|
||||
void flipSettingHandler();
|
||||
|
||||
void backHandler();
|
||||
|
||||
private:
|
||||
const Gfx::Color _textColorHovered = Gfx::Color(0x1E, 0x1E, 0x96);
|
||||
const Gfx::Color _textColorDefault = Gfx::Color(0x00, 0x00, 0x00);
|
||||
|
||||
TestSoundManager _soundManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Widget with a checkbox
|
||||
*/
|
||||
class CheckboxWidget : public StaticLocationWidget {
|
||||
public:
|
||||
CheckboxWidget(const char *renderEntryName, bool isChecked,
|
||||
WidgetOnClickCallback *onClickCallback,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback);
|
||||
virtual ~CheckboxWidget() {};
|
||||
|
||||
// StaticLocationWidget API
|
||||
void render() override;
|
||||
bool isMouseInside(const Common::Point &mousePos) const override;
|
||||
void onClick() override;
|
||||
|
||||
private:
|
||||
VisualImageXMG *_currentImage;
|
||||
VisualImageXMG *_checkBoxImage[2];
|
||||
Common::Point _position;
|
||||
int _checkboxWidth, _checkboxHeight;
|
||||
bool _isChecked;
|
||||
|
||||
bool isMouseInsideCheckbox(const Common::Point &mousePos) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Widget with a dragged slider for twisting the volume
|
||||
*/
|
||||
class VolumeWidget : public StaticLocationWidget {
|
||||
public:
|
||||
VolumeWidget(const char *renderEntryName, Cursor *cursor,
|
||||
TestSoundManager &soundManager, int soundIndex,
|
||||
Settings::IntSettingIndex settingIndex,
|
||||
WidgetOnMouseMoveCallback *onMouseMoveCallback);
|
||||
virtual ~VolumeWidget() {};
|
||||
|
||||
// StaticLocationWidget API
|
||||
void render() override;
|
||||
bool isMouseInside(const Common::Point &mousePos) const override;
|
||||
void onClick() override;
|
||||
void onMouseMove(const Common::Point &mousePos) override;
|
||||
void onMouseUp() override;
|
||||
|
||||
private:
|
||||
const Gfx::Color _textColorBgHovered = Gfx::Color(0xFF, 0xFF, 0xFF);
|
||||
static const int _maxVolume = 256;
|
||||
|
||||
VisualImageXMG *_sliderImage;
|
||||
VisualImageXMG *_bgImage;
|
||||
|
||||
Cursor *_cursor;
|
||||
|
||||
TestSoundManager &_soundManager;
|
||||
const int _soundIndex;
|
||||
|
||||
Common::Point _sliderPosition, _bgPosition;
|
||||
int _bgWidth, _bgHeight, _sliderWidth, _minX, _maxX;
|
||||
|
||||
bool _isDragged;
|
||||
const Settings::IntSettingIndex _settingIndex;
|
||||
|
||||
bool isMouseInsideBg(const Common::Point &mousePos) const;
|
||||
|
||||
int volumeToX(int volume) {
|
||||
return volume * (_maxX - _minX) / _maxVolume + _minX;
|
||||
}
|
||||
|
||||
int xToVolume(int x) {
|
||||
return (x - _minX) * _maxVolume / (_maxX - _minX);
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_MENU_SETTING_MENU_H
|
||||
96
engines/stark/ui/screen.h
Normal file
96
engines/stark/ui/screen.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* 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 STARK_UI_SCREEN_H
|
||||
#define STARK_UI_SCREEN_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* Screens are individual pages of the user interface
|
||||
*/
|
||||
class Screen {
|
||||
public:
|
||||
enum Name {
|
||||
kScreenMainMenu,
|
||||
kScreenGame,
|
||||
kScreenFMV,
|
||||
kScreenDiaryIndex,
|
||||
kScreenSettingsMenu,
|
||||
kScreenSaveMenu,
|
||||
kScreenLoadMenu,
|
||||
kScreenFMVMenu,
|
||||
kScreenDiaryPages,
|
||||
kScreenDialog
|
||||
};
|
||||
|
||||
explicit Screen(Name name) : _name(name) {};
|
||||
virtual ~Screen() {};
|
||||
|
||||
/** Obtain the name of the screen */
|
||||
Name getName() const { return _name; }
|
||||
|
||||
/** The screen is being made active, prepare it to be drawn */
|
||||
virtual void open() {}
|
||||
|
||||
/** The screen is no longer active, release resources to free memory */
|
||||
virtual void close() {}
|
||||
|
||||
/** Draw the screen */
|
||||
virtual void render() = 0;
|
||||
|
||||
/** Called once per game loop when the screen is active. */
|
||||
virtual void handleGameLoop() {}
|
||||
|
||||
/** Called when the screen resolution changes */
|
||||
virtual void onScreenChanged() {}
|
||||
|
||||
virtual void handleMouseMove() = 0;
|
||||
virtual void handleClick() = 0;
|
||||
virtual void handleRightClick() = 0;
|
||||
virtual void handleDoubleClick() = 0;
|
||||
|
||||
private:
|
||||
Name _name;
|
||||
};
|
||||
|
||||
class SingleWindowScreen : public Screen, public Window {
|
||||
public:
|
||||
SingleWindowScreen(Name name, Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Screen(name), Window(gfx, cursor) {}
|
||||
|
||||
void handleGameLoop() override { Window::handleGameLoop(); }
|
||||
void render() override { Window::render(); }
|
||||
|
||||
void handleMouseMove() override { Window::handleMouseMove(); }
|
||||
void handleClick() override { Window::handleClick(); }
|
||||
void handleRightClick() override { Window::handleRightClick(); }
|
||||
void handleDoubleClick() override { Window::handleDoubleClick(); }
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_SCREEN_H
|
||||
123
engines/stark/ui/window.cpp
Normal file
123
engines/stark/ui/window.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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 "engines/stark/ui/window.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Window::Window(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
_gfx(gfx),
|
||||
_cursor(cursor),
|
||||
_visible(false) {
|
||||
}
|
||||
|
||||
Window::~Window() {
|
||||
}
|
||||
|
||||
void Window::handleGameLoop() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
onGameLoop();
|
||||
}
|
||||
|
||||
void Window::render() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gfx->setViewport(_position);
|
||||
|
||||
onRender();
|
||||
}
|
||||
|
||||
Graphics::Surface *Window::getScreenshot() const {
|
||||
if (!_visible) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_gfx->setViewport(_position);
|
||||
return _gfx->getViewportScreenshot();
|
||||
}
|
||||
|
||||
bool Window::isMouseInside() const {
|
||||
if (!_visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Point mousePos = _cursor->getMousePosition();
|
||||
return _position.contains(mousePos);
|
||||
}
|
||||
|
||||
bool Window::isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
|
||||
Common::Point Window::getRelativeMousePosition() const {
|
||||
Common::Point mousePos = _cursor->getMousePosition();
|
||||
return mousePos - Common::Point(_position.left, _position.top);
|
||||
}
|
||||
|
||||
void Window::handleMouseMove() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMouseInside()) {
|
||||
onMouseMove(getRelativeMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::handleClick() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMouseInside()) {
|
||||
onClick(getRelativeMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::handleRightClick() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMouseInside()) {
|
||||
onRightClick(getRelativeMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::handleDoubleClick() {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMouseInside()) {
|
||||
onDoubleClick(getRelativeMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
105
engines/stark/ui/window.h
Normal file
105
engines/stark/ui/window.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* 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 STARK_UI_WINDOW_H
|
||||
#define STARK_UI_WINDOW_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class Cursor;
|
||||
|
||||
namespace Gfx {
|
||||
class Driver;
|
||||
}
|
||||
|
||||
namespace Resources {
|
||||
typedef Common::Array<uint32> ActionArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* A window is a portion of the game screen.
|
||||
*
|
||||
* A window can handle events happening in its screen portion,
|
||||
* as well as render graphics to that same screen portion.
|
||||
*
|
||||
* Coordinates inside a window are relative to its top left corner.
|
||||
* Rendering happens in a viewport matching the window's screen portion.
|
||||
*
|
||||
*/
|
||||
class Window {
|
||||
public:
|
||||
Window(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~Window();
|
||||
|
||||
/** Called by the user interface when the mouse moves inside the window */
|
||||
void handleMouseMove();
|
||||
|
||||
/** Called by the user interface when the mouse is clicked inside the window */
|
||||
void handleClick();
|
||||
|
||||
/** Called by the user interface when the mouse is right clicked inside the window */
|
||||
void handleRightClick();
|
||||
|
||||
/** Called by the user interface when the mouse is double clicked inside the window */
|
||||
void handleDoubleClick();
|
||||
|
||||
/** Called once per game loop when the screen is active. */
|
||||
void handleGameLoop();
|
||||
|
||||
/** Called by the user interface in the render phase of the game loop */
|
||||
void render();
|
||||
|
||||
/** Is the mouse inside the window? */
|
||||
bool isMouseInside() const;
|
||||
|
||||
/** Is the window visible */
|
||||
bool isVisible() const;
|
||||
|
||||
/** Grab a screenshot of the window if it is visible */
|
||||
Graphics::Surface *getScreenshot() const;
|
||||
|
||||
protected:
|
||||
virtual void onMouseMove(const Common::Point &pos) {}
|
||||
virtual void onClick(const Common::Point &pos) {}
|
||||
virtual void onRightClick(const Common::Point &pos) {}
|
||||
virtual void onDoubleClick(const Common::Point &pos) {}
|
||||
virtual void onGameLoop() {};
|
||||
virtual void onRender() = 0;
|
||||
|
||||
Common::Point getRelativeMousePosition() const;
|
||||
|
||||
Gfx::Driver *_gfx;
|
||||
Cursor *_cursor;
|
||||
|
||||
Common::Rect _position;
|
||||
bool _visible;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_WINDOW_H
|
||||
250
engines/stark/ui/world/actionmenu.cpp
Normal file
250
engines/stark/ui/world/actionmenu.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/* 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 "engines/stark/ui/world/actionmenu.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/gamewindow.h"
|
||||
#include "engines/stark/ui/world/inventorywindow.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/level.h"
|
||||
#include "engines/stark/resources/pattable.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/gameinterface.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
|
||||
namespace Stark {
|
||||
|
||||
static const int kAutoCloseSuspended = -1;
|
||||
static const int kAutoCloseDisabled = -2;
|
||||
static const int kAutoCloseDelay = 200;
|
||||
|
||||
ActionMenu::ActionMenu(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Window(gfx, cursor),
|
||||
_fromInventory(false),
|
||||
_itemDescription(nullptr),
|
||||
_item(nullptr),
|
||||
_inventory(nullptr),
|
||||
_activeMenuType(kActionNoneM),
|
||||
_autoCloseTimeRemaining(kAutoCloseDisabled) {
|
||||
|
||||
_background = StarkStaticProvider->getUIElement(StaticProvider::kActionMenuBg);
|
||||
|
||||
_itemDescription = new VisualText(gfx);
|
||||
_itemDescription->setColor(Gfx::Color(0xFF, 0xFF, 0xFF));
|
||||
_itemDescription->setBackgroundColor(Gfx::Color(0x00, 0x00, 0x00, 0x80));
|
||||
_itemDescription->setFont(FontProvider::kSmallFont);
|
||||
_itemDescription->setTargetWidth(96);
|
||||
|
||||
_buttons[kActionHand].action = Resources::PATTable::kActionUse;
|
||||
_buttons[kActionHand].rect = Common::Rect(90, 15, 126, 63);
|
||||
_buttons[kActionEye].action = Resources::PATTable::kActionLook;
|
||||
_buttons[kActionEye].rect = Common::Rect(5, 77, 51, 110);
|
||||
_buttons[kActionMouth].action = Resources::PATTable::kActionTalk;
|
||||
_buttons[kActionMouth].rect = Common::Rect(42, 35, 83, 74);
|
||||
|
||||
_actionMouthHoverSound = StarkStaticProvider->getUISound(StaticProvider::kActionMouthHover);
|
||||
_actionMouthHoverSound->setLooping(false);
|
||||
_actionHoverSound = StarkStaticProvider->getUISound(StaticProvider::kActionHover);
|
||||
|
||||
clearActions();
|
||||
}
|
||||
|
||||
ActionMenu::~ActionMenu() {
|
||||
delete _itemDescription;
|
||||
}
|
||||
|
||||
void ActionMenu::open(Resources::ItemVisual *item, const Common::Point &itemRelativePos) {
|
||||
_visible = true;
|
||||
|
||||
Common::Point mousePos = _cursor->getMousePosition();
|
||||
|
||||
_position = computePosition(mousePos);
|
||||
|
||||
_itemRelativePos = itemRelativePos;
|
||||
_item = item;
|
||||
_fromInventory = item->getSubType() == Resources::Item::kItemInventory;
|
||||
|
||||
if (_fromInventory) {
|
||||
_itemDescription->setText(StarkGameInterface->getItemTitle(item));
|
||||
} else {
|
||||
_itemDescription->setText(StarkGameInterface->getItemTitleAt(item, itemRelativePos));
|
||||
}
|
||||
|
||||
_cursor->setMouseHint("");
|
||||
|
||||
clearActions();
|
||||
|
||||
Resources::ActionArray possible;
|
||||
if (_fromInventory) {
|
||||
possible = StarkGameInterface->listActionsPossibleForObject(_item);
|
||||
} else {
|
||||
possible = StarkGameInterface->listActionsPossibleForObjectAt(_item, _itemRelativePos);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < possible.size(); i++) {
|
||||
enableAction(possible[i]);
|
||||
}
|
||||
|
||||
if (_fromInventory) {
|
||||
// All inventory items can be picked up
|
||||
enableAction(Resources::PATTable::kActionUse);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::close() {
|
||||
_visible = false;
|
||||
_item = nullptr;
|
||||
_activeMenuType = kActionNoneM;
|
||||
_actionHoverSound->stop();
|
||||
}
|
||||
|
||||
Common::Rect ActionMenu::computePosition(const Common::Point &mouse) const {
|
||||
Common::Rect position = Common::Rect::center(mouse.x, mouse.y, 160, 111);
|
||||
|
||||
Common::Rect gameWindowRect(Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
|
||||
gameWindowRect.translate(0, Gfx::Driver::kTopBorderHeight);
|
||||
|
||||
if (position.top < gameWindowRect.top) position.translate(0, gameWindowRect.top - position.top);
|
||||
if (position.left < gameWindowRect.left) position.translate(gameWindowRect.left - position.left, 0);
|
||||
if (position.bottom > gameWindowRect.bottom) position.translate(0, gameWindowRect.bottom - position.bottom);
|
||||
if (position.right > gameWindowRect.right) position.translate(gameWindowRect.right - position.right, 0);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
void ActionMenu::onRender() {
|
||||
_background->render(Common::Point(0, 0), false);
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
|
||||
if (_buttons[i].enabled) {
|
||||
VisualImageXMG *visual = StarkGameInterface->getActionImage(_buttons[i].action, (int32)i == _activeMenuType);
|
||||
visual->render(Common::Point(_buttons[i].rect.left, _buttons[i].rect.top), false);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect descriptionSize = _itemDescription->getRect();
|
||||
int descriptionX = 60 + (_itemDescription->getTargetWidth() - descriptionSize.width()) / 2;
|
||||
int descriptionY = _position.height() - descriptionSize.height();
|
||||
|
||||
_itemDescription->render(Common::Point(descriptionX, descriptionY));
|
||||
}
|
||||
|
||||
void ActionMenu::clearActions() {
|
||||
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
|
||||
_buttons[i].enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::enableAction(uint32 action) {
|
||||
for (uint j = 0; j < ARRAYSIZE(_buttons); j++) {
|
||||
if (_buttons[j].action == action) {
|
||||
_buttons[j].enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::updateActionSound() {
|
||||
if (_activeMenuType == kActionNoneM) {
|
||||
_actionHoverSound->stop();
|
||||
return;
|
||||
}
|
||||
_actionHoverSound->play();
|
||||
if (_activeMenuType == kActionMouth) {
|
||||
_actionMouthHoverSound->play();
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::onMouseMove(const Common::Point &pos) {
|
||||
int32 prevActive = _activeMenuType;
|
||||
int32 newActive = kActionNoneM;
|
||||
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
|
||||
if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
|
||||
newActive = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (newActive != prevActive) {
|
||||
_activeMenuType = newActive;
|
||||
if (_activeMenuType == kActionNoneM) {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
}
|
||||
updateActionSound();
|
||||
}
|
||||
|
||||
_autoCloseTimeRemaining = kAutoCloseSuspended;
|
||||
}
|
||||
|
||||
void ActionMenu::onClick(const Common::Point &pos) {
|
||||
assert(_item);
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
|
||||
if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
|
||||
|
||||
if (_fromInventory && i == kActionHand) {
|
||||
_inventory->setSelectedInventoryItem(_item->getIndex());
|
||||
} else {
|
||||
if (_fromInventory) {
|
||||
StarkGameInterface->itemDoAction(_item, _buttons[i].action);
|
||||
} else {
|
||||
StarkGameInterface->itemDoActionAt(_item, _buttons[i].action, _itemRelativePos);
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::setInventory(InventoryWindow *inventory) {
|
||||
_inventory = inventory;
|
||||
}
|
||||
|
||||
void ActionMenu::onGameLoop() {
|
||||
if (!isMouseInside() && _autoCloseTimeRemaining == kAutoCloseSuspended) {
|
||||
_autoCloseTimeRemaining = kAutoCloseDelay;
|
||||
} else if (_autoCloseTimeRemaining >= 0) {
|
||||
_autoCloseTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
if (_autoCloseTimeRemaining <= 0) {
|
||||
_autoCloseTimeRemaining = kAutoCloseSuspended;
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMenu::onScreenChanged() {
|
||||
_itemDescription->reset();
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
99
engines/stark/ui/world/actionmenu.h
Normal file
99
engines/stark/ui/world/actionmenu.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 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 STARK_UI_ACTIONMENU_H
|
||||
#define STARK_UI_ACTIONMENU_H
|
||||
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class Cursor;
|
||||
class VisualImageXMG;
|
||||
class VisualText;
|
||||
class InventoryWindow;
|
||||
class GameWindow;
|
||||
|
||||
namespace Resources {
|
||||
class ItemVisual;
|
||||
class Sound;
|
||||
}
|
||||
|
||||
class ActionMenu : public Window {
|
||||
public:
|
||||
ActionMenu(Gfx::Driver *gfx, Cursor *cursor);
|
||||
~ActionMenu() override;
|
||||
|
||||
void setInventory(InventoryWindow *inventory);
|
||||
|
||||
void open(Resources::ItemVisual *item, const Common::Point &itemRelativePos);
|
||||
void close();
|
||||
|
||||
void onScreenChanged();
|
||||
|
||||
protected:
|
||||
Common::Rect computePosition(const Common::Point &mouse) const;
|
||||
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
void clearActions();
|
||||
void enableAction(uint32 action);
|
||||
void updateActionSound();
|
||||
|
||||
enum ActionMenuType {
|
||||
kActionNoneM = -1,
|
||||
kActionHand = 0,
|
||||
kActionEye = 1,
|
||||
kActionMouth = 2
|
||||
};
|
||||
|
||||
struct ActionButton {
|
||||
bool enabled;
|
||||
uint32 action;
|
||||
Common::Rect rect;
|
||||
};
|
||||
|
||||
bool _fromInventory;
|
||||
ActionButton _buttons[3];
|
||||
|
||||
VisualImageXMG *_background;
|
||||
VisualText *_itemDescription;
|
||||
|
||||
Common::Point _itemRelativePos;
|
||||
Resources::ItemVisual *_item;
|
||||
|
||||
InventoryWindow *_inventory;
|
||||
|
||||
int32 _autoCloseTimeRemaining;
|
||||
|
||||
int32 _activeMenuType;
|
||||
|
||||
Resources::Sound *_actionMouthHoverSound;
|
||||
Resources::Sound *_actionHoverSound;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif
|
||||
129
engines/stark/ui/world/button.cpp
Normal file
129
engines/stark/ui/world/button.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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 "engines/stark/ui/world/button.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/visual/explodingimage.h"
|
||||
#include "engines/stark/visual/flashingimage.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Button::Button(const Common::String &text, StaticProvider::UIElement stockElement, const Common::Point &pos, HintAlign align, const Common::Point &hintPos) :
|
||||
_position(pos),
|
||||
_stockElement(stockElement),
|
||||
_text(text),
|
||||
_hintPosition(hintPos),
|
||||
_align(align),
|
||||
_mouseText(nullptr),
|
||||
_renderHint(false),
|
||||
_explodingImageAnimation(nullptr),
|
||||
_flashingImageAnimation(nullptr) {
|
||||
}
|
||||
|
||||
Button::~Button() {
|
||||
delete _explodingImageAnimation;
|
||||
delete _flashingImageAnimation;
|
||||
delete _mouseText;
|
||||
}
|
||||
|
||||
void Button::render() {
|
||||
VisualImageXMG *image = StarkStaticProvider->getUIElement(_stockElement);
|
||||
image->render(_position, false);
|
||||
|
||||
if (_explodingImageAnimation) {
|
||||
_explodingImageAnimation->render(_position);
|
||||
}
|
||||
|
||||
if (_flashingImageAnimation) {
|
||||
_flashingImageAnimation->render(_position);
|
||||
}
|
||||
|
||||
if (_renderHint) {
|
||||
Common::Point pos(_hintPosition);
|
||||
if (_align == kAlignRight) {
|
||||
pos.x -= _mouseText->getRect().width();
|
||||
}
|
||||
_mouseText->render(pos);
|
||||
_renderHint = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Button::containsPoint(const Common::Point &point) {
|
||||
VisualImageXMG *image = StarkStaticProvider->getUIElement(_stockElement);
|
||||
|
||||
Common::Rect r;
|
||||
r.left = _position.x;
|
||||
r.top = _position.y;
|
||||
r.setWidth(image->getWidth());
|
||||
r.setHeight(image->getHeight());
|
||||
return r.contains(point);
|
||||
}
|
||||
|
||||
void Button::showButtonHint() {
|
||||
if (!_mouseText) {
|
||||
_mouseText = new VisualText(StarkGfx);
|
||||
_mouseText->setText(_text);
|
||||
_mouseText->setColor(Gfx::Color(0xFF, 0xFF, 0xFF));
|
||||
_mouseText->setFont(FontProvider::kSmallFont);
|
||||
_mouseText->setTargetWidth(96);
|
||||
}
|
||||
_renderHint = true;
|
||||
}
|
||||
|
||||
void Button::resetHintVisual() {
|
||||
delete _mouseText;
|
||||
_mouseText = nullptr;
|
||||
}
|
||||
|
||||
void Button::goToAnimStatement(int animScriptItemIndex) {
|
||||
StarkStaticProvider->goToAnimScriptStatement(_stockElement, animScriptItemIndex);
|
||||
}
|
||||
|
||||
void Button::startImageExplosion(VisualImageXMG *image) {
|
||||
assert(image);
|
||||
|
||||
stopImageExplosion();
|
||||
_explodingImageAnimation = new VisualExplodingImage(StarkGfx);
|
||||
_explodingImageAnimation->initFromSurface(image->getSurface(), image->getWidth(), image->getHeight());
|
||||
}
|
||||
|
||||
void Button::stopImageExplosion() {
|
||||
delete _explodingImageAnimation;
|
||||
_explodingImageAnimation = nullptr;
|
||||
}
|
||||
|
||||
void Button::startImageFlashing(VisualImageXMG *image) {
|
||||
assert(image);
|
||||
|
||||
stopImageFlashing();
|
||||
_flashingImageAnimation = new VisualFlashingImage(StarkGfx);
|
||||
_flashingImageAnimation->initFromSurface(image->getSurface(), image->getWidth(), image->getHeight());
|
||||
}
|
||||
|
||||
void Button::stopImageFlashing() {
|
||||
delete _flashingImageAnimation;
|
||||
_flashingImageAnimation = nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
89
engines/stark/ui/world/button.h
Normal file
89
engines/stark/ui/world/button.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 STARK_UI_BUTTON_H
|
||||
#define STARK_UI_BUTTON_H
|
||||
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualExplodingImage;
|
||||
class VisualFlashingImage;
|
||||
class VisualImageXMG;
|
||||
class VisualText;
|
||||
|
||||
namespace Resources {
|
||||
class Anim;
|
||||
}
|
||||
|
||||
class Button {
|
||||
public:
|
||||
enum HintAlign { kAlignLeft, kAlignRight };
|
||||
|
||||
Button(const Common::String &text, StaticProvider::UIElement stockElement, const Common::Point &pos, HintAlign align, const Common::Point &hintPos);
|
||||
~Button();
|
||||
|
||||
void setPosition(const Common::Point &pos) { _position = pos; }
|
||||
void setUIElement(const StaticProvider::UIElement &stockElement) { _stockElement = stockElement; }
|
||||
|
||||
/** Set hint to render for one frame */
|
||||
void showButtonHint();
|
||||
void render();
|
||||
bool containsPoint(const Common::Point &point);
|
||||
|
||||
/** Reset the hint text visual so it is rebuilt with the appropriate size */
|
||||
void resetHintVisual();
|
||||
|
||||
/** Move execution of the button's icon anim script to the specified item */
|
||||
void goToAnimStatement(int animScriptItemIndex);
|
||||
|
||||
/** Start overlaying an explosion animation of an image on top of the button */
|
||||
void startImageExplosion(VisualImageXMG *image);
|
||||
|
||||
/** Remove the currently playing exploding image animation, if any */
|
||||
void stopImageExplosion();
|
||||
|
||||
/** Start a flash animation of an image button */
|
||||
void startImageFlashing(VisualImageXMG *image);
|
||||
|
||||
/** Remove the currently playing flash image animation, if any */
|
||||
void stopImageFlashing();
|
||||
|
||||
private:
|
||||
StaticProvider::UIElement _stockElement;
|
||||
Common::Point _position;
|
||||
Common::Point _hintPosition;
|
||||
Common::String _text;
|
||||
VisualText *_mouseText;
|
||||
VisualExplodingImage *_explodingImageAnimation;
|
||||
VisualFlashingImage *_flashingImageAnimation;
|
||||
const HintAlign _align;
|
||||
bool _renderHint;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_BUTTON_H
|
||||
63
engines/stark/ui/world/clicktext.cpp
Normal file
63
engines/stark/ui/world/clicktext.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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 "engines/stark/ui/world/clicktext.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
ClickText::ClickText(const Common::String &text, const Gfx::Color &color) :
|
||||
_text(text),
|
||||
_color(color) {
|
||||
_visualPassive = new VisualText(StarkGfx);
|
||||
_visualPassive->setText(_text);
|
||||
_visualPassive->setColor(_color);
|
||||
_visualPassive->setFont(FontProvider::kBigFont);
|
||||
_visualPassive->setTargetWidth(600);
|
||||
|
||||
_visualActive = new VisualText(StarkGfx);
|
||||
_visualActive->setText(_text);
|
||||
_visualActive->setColor(Gfx::Color(0x00, 0x00, 0x00));
|
||||
_visualActive->setBackgroundColor(_color);
|
||||
_visualActive->setFont(FontProvider::kBigFont);
|
||||
_visualActive->setTargetWidth(600);
|
||||
|
||||
_curVisual = _visualPassive;
|
||||
_bbox = _curVisual->getRect();
|
||||
}
|
||||
|
||||
ClickText::~ClickText() {
|
||||
delete _visualActive;
|
||||
delete _visualPassive;
|
||||
}
|
||||
|
||||
void ClickText::render() {
|
||||
_curVisual->render(_position);
|
||||
}
|
||||
|
||||
bool ClickText::containsPoint(const Common::Point &point) const {
|
||||
Common::Rect r = _bbox;
|
||||
r.translate(_position.x, _position.y);
|
||||
return r.contains(point);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
60
engines/stark/ui/world/clicktext.h
Normal file
60
engines/stark/ui/world/clicktext.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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 STARK_UI_CLICK_TEXT_H
|
||||
#define STARK_UI_CLICK_TEXT_H
|
||||
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualText;
|
||||
|
||||
class ClickText {
|
||||
public:
|
||||
ClickText(const Common::String &text, const Gfx::Color &color);
|
||||
~ClickText();
|
||||
|
||||
void setPosition(const Common::Point &pos) { _position = pos; }
|
||||
void render();
|
||||
bool containsPoint(const Common::Point &point) const;
|
||||
int getHeight() const { return _bbox.height(); }
|
||||
|
||||
void setActive() { _curVisual = _visualActive; }
|
||||
void setPassive() { _curVisual = _visualPassive; }
|
||||
|
||||
private:
|
||||
VisualText *_visualPassive;
|
||||
VisualText *_visualActive;
|
||||
VisualText *_curVisual;
|
||||
Common::Point _position;
|
||||
Common::String _text;
|
||||
Common::Rect _bbox;
|
||||
Gfx::Color _color;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_CLICK_TEXT_H
|
||||
382
engines/stark/ui/world/dialogpanel.cpp
Normal file
382
engines/stark/ui/world/dialogpanel.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/* 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 "engines/stark/ui/world/dialogpanel.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/speech.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/dialogplayer.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/clicktext.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
DialogPanel::DialogPanel(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Window(gfx, cursor),
|
||||
_subtitleVisual(nullptr),
|
||||
_currentSpeech(nullptr),
|
||||
_scrollUpArrowVisible(false),
|
||||
_scrollDownArrowVisible(false),
|
||||
_firstVisibleOption(0),
|
||||
_lastVisibleOption(0),
|
||||
_focusedOption(0),
|
||||
_acceptIdleMousePos(false) {
|
||||
_position = Common::Rect(Gfx::Driver::kOriginalWidth, Gfx::Driver::kBottomBorderHeight);
|
||||
_position.translate(0, Gfx::Driver::kTopBorderHeight + Gfx::Driver::kGameViewportHeight);
|
||||
|
||||
_visible = true;
|
||||
|
||||
_activeBackGroundImage = StarkStaticProvider->getUIElement(StaticProvider::kTextBackgroundActive);
|
||||
_passiveBackGroundImage = StarkStaticProvider->getUIElement(StaticProvider::kTextBackgroundPassive);
|
||||
_scrollUpArrowImage = StarkStaticProvider->getUIElement(StaticProvider::kTextScrollUpArrow);
|
||||
_scrollDownArrowImage = StarkStaticProvider->getUIElement(StaticProvider::kTextScrollDownArrow);
|
||||
_dialogOptionBullet = StarkStaticProvider->getUIImage(StaticProvider::kDialogOptionBullet);
|
||||
|
||||
_scrollUpArrowRect = Common::Rect(_scrollUpArrowImage->getWidth(), _scrollUpArrowImage->getHeight());
|
||||
_scrollUpArrowRect.translate(0, _optionsTop);
|
||||
|
||||
_scrollDownArrowRect = Common::Rect(_scrollDownArrowImage->getWidth(), _scrollDownArrowImage->getHeight());
|
||||
_scrollDownArrowRect.translate(0, _optionsTop + _optionsHeight - _scrollDownArrowImage->getHeight() - 9);
|
||||
}
|
||||
|
||||
DialogPanel::~DialogPanel() {
|
||||
clearOptions();
|
||||
clearSubtitleVisual();
|
||||
}
|
||||
|
||||
void DialogPanel::abortCurrentSpeech() {
|
||||
if (_currentSpeech) {
|
||||
_currentSpeech->stop();
|
||||
_currentSpeech = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::clearSubtitleVisual() {
|
||||
delete _subtitleVisual;
|
||||
_subtitleVisual = nullptr;
|
||||
}
|
||||
|
||||
void DialogPanel::clearOptions() {
|
||||
for (uint i = 0; i < _options.size(); i++) {
|
||||
delete _options[i];
|
||||
}
|
||||
_options.clear();
|
||||
}
|
||||
|
||||
void DialogPanel::renderOptions() {
|
||||
uint32 pos = _optionsTop;
|
||||
for (uint i = _firstVisibleOption; i <= _lastVisibleOption; ++i) {
|
||||
_options[i]->setPosition(Common::Point(_optionsLeft, pos));
|
||||
_options[i]->render();
|
||||
|
||||
_dialogOptionBullet->render(Common::Point(_optionsLeft - 13, pos + 3), false);
|
||||
|
||||
pos += _options[i]->getHeight();
|
||||
}
|
||||
|
||||
_scrollUpArrowVisible = _firstVisibleOption > 0;
|
||||
_scrollDownArrowVisible = _lastVisibleOption < _options.size() - 1;
|
||||
}
|
||||
|
||||
void DialogPanel::renderScrollArrows() const {
|
||||
if (_scrollUpArrowVisible) {
|
||||
_scrollUpArrowImage->render(Common::Point(_scrollUpArrowRect.left, _scrollUpArrowRect.top), true);
|
||||
}
|
||||
|
||||
if (_scrollDownArrowVisible) {
|
||||
_scrollDownArrowImage->render(Common::Point(_scrollDownArrowRect.left, _scrollDownArrowRect.top), true);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::onGameLoop() {
|
||||
// Clear completed speeches
|
||||
if (!_currentSpeech || !_currentSpeech->isPlaying()) {
|
||||
_currentSpeech = nullptr;
|
||||
|
||||
clearSubtitleVisual();
|
||||
|
||||
// Toggle subtitles on and off when requested
|
||||
if (StarkUserInterface->hasToggleSubtitleRequest()) {
|
||||
StarkUserInterface->performToggleSubtitle();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the dialog engine
|
||||
StarkDialogPlayer->update();
|
||||
|
||||
// Check if a new speech can be played
|
||||
if (StarkDialogPlayer->isSpeechReady()) {
|
||||
_currentSpeech = StarkDialogPlayer->acquireReadySpeech();
|
||||
_currentSpeech->playSound();
|
||||
updateSubtitleVisual();
|
||||
}
|
||||
|
||||
if (_options.empty() && StarkDialogPlayer->areOptionsAvailable()) {
|
||||
updateDialogOptions();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::onRender() {
|
||||
// Draw options if available
|
||||
if (!_options.empty()) {
|
||||
_activeBackGroundImage->render(Common::Point(0, 0), false);
|
||||
|
||||
renderOptions();
|
||||
renderScrollArrows();
|
||||
} else {
|
||||
_passiveBackGroundImage->render(Common::Point(0, 0), false);
|
||||
|
||||
// Draw subtitle if available
|
||||
if (_subtitleVisual && StarkSettings->getBoolSetting(Settings::kSubtitle)) {
|
||||
_subtitleVisual->render(Common::Point(_optionsLeft, _optionsTop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::updateSubtitleVisual() {
|
||||
clearSubtitleVisual();
|
||||
|
||||
Gfx::Color color = _otherColor;
|
||||
if (_currentSpeech->characterIsApril())
|
||||
color = _aprilColor;
|
||||
|
||||
_subtitleVisual = new VisualText(_gfx);
|
||||
_subtitleVisual->setText(_currentSpeech->getPhrase());
|
||||
_subtitleVisual->setAlign(Graphics::TextAlign::kTextAlignStart);
|
||||
_subtitleVisual->setColor(color);
|
||||
_subtitleVisual->setFont(FontProvider::kBigFont);
|
||||
_subtitleVisual->setTargetWidth(600);
|
||||
}
|
||||
|
||||
void DialogPanel::updateDialogOptions() {
|
||||
clearOptions();
|
||||
|
||||
_firstVisibleOption = 0;
|
||||
_lastVisibleOption = 0;
|
||||
_focusedOption = 0;
|
||||
Common::Array<DialogPlayer::Option> options = StarkDialogPlayer->listOptions();
|
||||
|
||||
for (uint i = 0; i < options.size(); i++) {
|
||||
_options.push_back(new ClickText(options[i]._caption, _aprilColor));
|
||||
}
|
||||
|
||||
if (!_options.empty()) {
|
||||
updateLastVisibleOption();
|
||||
_options[_focusedOption]->setActive();
|
||||
_acceptIdleMousePos = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::onMouseMove(const Common::Point &pos) {
|
||||
static Common::Point prevPos;
|
||||
|
||||
if (_subtitleVisual) {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
} else if (!_options.empty()) {
|
||||
if (pos != prevPos || _acceptIdleMousePos) {
|
||||
for (uint i = _firstVisibleOption; i <= _lastVisibleOption; ++i) {
|
||||
if (_options[i]->containsPoint(pos)) {
|
||||
_options[_focusedOption]->setPassive();
|
||||
_focusedOption = i;
|
||||
_options[_focusedOption]->setActive();
|
||||
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
_acceptIdleMousePos = false;
|
||||
|
||||
prevPos = pos;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_options[_focusedOption]->containsPoint(pos)) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
} else if (_scrollUpArrowVisible && _scrollUpArrowRect.contains(pos)) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
} else if (_scrollDownArrowVisible && _scrollDownArrowRect.contains(pos)) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
}
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
}
|
||||
|
||||
prevPos = pos;
|
||||
}
|
||||
|
||||
void DialogPanel::onClick(const Common::Point &pos) {
|
||||
if (!_options.empty()) {
|
||||
if (_options[_focusedOption]->containsPoint(pos)) {
|
||||
selectFocusedOption();
|
||||
}
|
||||
|
||||
if (_scrollUpArrowVisible && _scrollUpArrowRect.contains(pos)) {
|
||||
scrollUp();
|
||||
}
|
||||
|
||||
if (_scrollDownArrowVisible && _scrollDownArrowRect.contains(pos)) {
|
||||
scrollDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::onRightClick(const Common::Point &pos) {
|
||||
if (_currentSpeech && _currentSpeech->isPlaying()) {
|
||||
abortCurrentSpeech();
|
||||
clearSubtitleVisual();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::reset() {
|
||||
abortCurrentSpeech();
|
||||
clearSubtitleVisual();
|
||||
clearOptions();
|
||||
|
||||
StarkDialogPlayer->reset();
|
||||
}
|
||||
|
||||
void DialogPanel::scrollUp() {
|
||||
if (!_scrollUpArrowVisible) return;
|
||||
|
||||
_lastVisibleOption = _firstVisibleOption;
|
||||
updateFirstVisibleOption();
|
||||
|
||||
_options[_focusedOption]->setPassive();
|
||||
_focusedOption = _lastVisibleOption;
|
||||
_options[_focusedOption]->setActive();
|
||||
}
|
||||
|
||||
void DialogPanel::scrollDown() {
|
||||
if (!_scrollDownArrowVisible) return;
|
||||
|
||||
_firstVisibleOption = _lastVisibleOption;
|
||||
updateLastVisibleOption();
|
||||
|
||||
_options[_focusedOption]->setPassive();
|
||||
_focusedOption = _firstVisibleOption;
|
||||
_options[_focusedOption]->setActive();
|
||||
}
|
||||
|
||||
void DialogPanel::focusNextOption() {
|
||||
if (_options.empty() || _focusedOption == _options.size() - 1) return;
|
||||
|
||||
_options[_focusedOption]->setPassive();
|
||||
++_focusedOption;
|
||||
_options[_focusedOption]->setActive();
|
||||
|
||||
if (_focusedOption > _lastVisibleOption) {
|
||||
_lastVisibleOption = _focusedOption;
|
||||
updateFirstVisibleOption();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::focusPrevOption() {
|
||||
if (_options.empty() || _focusedOption == 0) return;
|
||||
|
||||
_options[_focusedOption]->setPassive();
|
||||
--_focusedOption;
|
||||
_options[_focusedOption]->setActive();
|
||||
|
||||
if (_focusedOption < _firstVisibleOption) {
|
||||
_firstVisibleOption = _focusedOption;
|
||||
updateLastVisibleOption();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::selectFocusedOption() {
|
||||
if (_options.size() > 0) {
|
||||
StarkDialogPlayer->selectOption(_focusedOption);
|
||||
clearOptions();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::selectOption(uint index) {
|
||||
if (_options.size() <= index) return;
|
||||
|
||||
StarkDialogPlayer->selectOption(index);
|
||||
clearOptions();
|
||||
}
|
||||
|
||||
void DialogPanel::onScreenChanged() {
|
||||
if (_currentSpeech) {
|
||||
updateSubtitleVisual();
|
||||
} else {
|
||||
updateDialogOptions();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::updateFirstVisibleOption() {
|
||||
_firstVisibleOption = _lastVisibleOption;
|
||||
uint32 height = _optionsTop + _options[_lastVisibleOption]->getHeight();
|
||||
|
||||
while (_firstVisibleOption > 0) {
|
||||
height += _options[_firstVisibleOption - 1]->getHeight();
|
||||
if (height <= _optionsHeight) {
|
||||
--_firstVisibleOption;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_firstVisibleOption == 0) {
|
||||
while (_lastVisibleOption < _options.size() - 1) {
|
||||
height += _options[_lastVisibleOption + 1]->getHeight();
|
||||
if (height <= _optionsHeight) {
|
||||
++_lastVisibleOption;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPanel::updateLastVisibleOption() {
|
||||
_lastVisibleOption = _firstVisibleOption;
|
||||
uint32 height = _optionsTop + _options[_firstVisibleOption]->getHeight();
|
||||
|
||||
while (_lastVisibleOption < _options.size() - 1) {
|
||||
height += _options[_lastVisibleOption + 1]->getHeight();
|
||||
if (height <= _optionsHeight) {
|
||||
++_lastVisibleOption;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_lastVisibleOption == _options.size() - 1) {
|
||||
while (_firstVisibleOption > 0) {
|
||||
height += _options[_firstVisibleOption - 1]->getHeight();
|
||||
if (height <= _optionsHeight) {
|
||||
--_firstVisibleOption;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
116
engines/stark/ui/world/dialogpanel.h
Normal file
116
engines/stark/ui/world/dialogpanel.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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 STARK_UI_DIALOG_PANEL_H
|
||||
#define STARK_UI_DIALOG_PANEL_H
|
||||
|
||||
#include "engines/stark/ui/window.h"
|
||||
#include "engines/stark/gfx/color.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualImageXMG;
|
||||
class VisualText;
|
||||
class ClickText;
|
||||
|
||||
namespace Resources {
|
||||
class Speech;
|
||||
}
|
||||
|
||||
class DialogPanel : public Window {
|
||||
public:
|
||||
DialogPanel(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~DialogPanel();
|
||||
|
||||
/** Abort the currently playing dialog */
|
||||
void reset();
|
||||
|
||||
/** The screen resolution changed, rebuild the text accordingly */
|
||||
void onScreenChanged();
|
||||
|
||||
/** Scroll up and down the panel */
|
||||
void scrollUp();
|
||||
void scrollDown();
|
||||
|
||||
/** Select the next or previous option */
|
||||
void focusNextOption();
|
||||
void focusPrevOption();
|
||||
|
||||
/** Select the focused option */
|
||||
void selectFocusedOption();
|
||||
|
||||
/** Select an option by index */
|
||||
void selectOption(uint index);
|
||||
|
||||
protected:
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onRightClick(const Common::Point &pos) override;
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
void updateSubtitleVisual();
|
||||
void clearSubtitleVisual();
|
||||
void updateDialogOptions();
|
||||
void clearOptions();
|
||||
void renderOptions();
|
||||
void renderScrollArrows() const;
|
||||
|
||||
void updateFirstVisibleOption();
|
||||
void updateLastVisibleOption();
|
||||
|
||||
VisualImageXMG *_passiveBackGroundImage;
|
||||
VisualImageXMG *_activeBackGroundImage;
|
||||
VisualImageXMG *_scrollUpArrowImage;
|
||||
VisualImageXMG *_scrollDownArrowImage;
|
||||
VisualImageXMG *_dialogOptionBullet;
|
||||
VisualText *_subtitleVisual;
|
||||
|
||||
bool _scrollUpArrowVisible;
|
||||
bool _scrollDownArrowVisible;
|
||||
Common::Rect _scrollUpArrowRect;
|
||||
Common::Rect _scrollDownArrowRect;
|
||||
|
||||
Resources::Speech *_currentSpeech;
|
||||
void abortCurrentSpeech();
|
||||
|
||||
uint32 _firstVisibleOption, _lastVisibleOption;
|
||||
uint32 _focusedOption;
|
||||
Common::Array<ClickText*> _options;
|
||||
bool _acceptIdleMousePos;
|
||||
|
||||
const Gfx::Color _aprilColor = Gfx::Color(0xFF, 0xC0, 0x00);
|
||||
const Gfx::Color _otherColor = Gfx::Color(0xFF, 0x40, 0x40);
|
||||
static const uint32 _optionsTop = 4;
|
||||
static const uint32 _optionsLeft = 30;
|
||||
static const uint32 _optionsHeight = 80;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_DIALOG_PANEL_H
|
||||
116
engines/stark/ui/world/fmvscreen.cpp
Normal file
116
engines/stark/ui/world/fmvscreen.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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 "common/rect.h"
|
||||
|
||||
#include "engines/stark/ui/world/fmvscreen.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/surfacerenderer.h"
|
||||
#include "engines/stark/gfx/bitmap.h"
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FMVScreen::FMVScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
SingleWindowScreen(Screen::kScreenFMV, gfx, cursor) {
|
||||
_position = Common::Rect(Gfx::Driver::kOriginalWidth, Gfx::Driver::kOriginalHeight);
|
||||
_visible = true;
|
||||
|
||||
_bitmap = _gfx->createBitmap();
|
||||
_bitmap->setSamplingFilter(StarkSettings->getImageSamplingFilter());
|
||||
|
||||
_decoder = new Video::BinkDecoder();
|
||||
_decoder->setSoundType(Audio::Mixer::kSFXSoundType);
|
||||
|
||||
_surfaceRenderer = _gfx->createSurfaceRenderer();
|
||||
}
|
||||
|
||||
FMVScreen::~FMVScreen() {
|
||||
delete _decoder;
|
||||
delete _bitmap;
|
||||
delete _surfaceRenderer;
|
||||
}
|
||||
|
||||
void FMVScreen::play(const Common::Path &name) {
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
|
||||
// Play the low-resolution video, if possible
|
||||
if (!StarkSettings->getBoolSetting(Settings::kHighFMV) && StarkSettings->hasLowResFMV()) {
|
||||
Common::String lowResBaseName = name.baseName();
|
||||
lowResBaseName.erase(lowResBaseName.size() - 4);
|
||||
lowResBaseName += "_lo_res.bbb";
|
||||
|
||||
Common::Path lowResName(name.getParent().appendComponent(lowResBaseName));
|
||||
|
||||
stream = StarkArchiveLoader->getExternalFile(lowResName, "Global/");
|
||||
if (!stream) {
|
||||
debug("Could not open %s", lowResName.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Play the original video
|
||||
if (!stream) {
|
||||
stream = StarkArchiveLoader->getExternalFile(name, "Global/");
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
warning("Could not open %s", name.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_decoder->loadStream(stream);
|
||||
if (!_decoder->isVideoLoaded()) {
|
||||
error("Could not open %s", name.toString().c_str());
|
||||
}
|
||||
_decoder->setOutputPixelFormat(_bitmap->getBestPixelFormat());
|
||||
_decoder->start();
|
||||
}
|
||||
|
||||
void FMVScreen::onGameLoop() {
|
||||
if (isPlaying()) {
|
||||
if (_decoder->needsUpdate()) {
|
||||
const Graphics::Surface *decodedSurface = _decoder->decodeNextFrame();
|
||||
_bitmap->update(decodedSurface);
|
||||
}
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void FMVScreen::onRender() {
|
||||
_surfaceRenderer->render(_bitmap, Common::Point(0, Gfx::Driver::kTopBorderHeight),
|
||||
Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
|
||||
}
|
||||
|
||||
bool FMVScreen::isPlaying() {
|
||||
return _decoder->isPlaying() && !_decoder->endOfVideo();
|
||||
}
|
||||
|
||||
void FMVScreen::stop() {
|
||||
_decoder->stop();
|
||||
StarkUserInterface->onFMVStopped();
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Stark
|
||||
67
engines/stark/ui/world/fmvscreen.h
Normal file
67
engines/stark/ui/world/fmvscreen.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 STARK_UI_FMV_PLAYER_H
|
||||
#define STARK_UI_FMV_PLAYER_H
|
||||
|
||||
#include "engines/stark/ui/screen.h"
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
#include "video/bink_decoder.h"
|
||||
|
||||
namespace Video {
|
||||
class BinkDecoder;
|
||||
}
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class SurfaceRenderer;
|
||||
class Bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* FMV Player
|
||||
*
|
||||
* Handles the state of the currently running FMV.
|
||||
*/
|
||||
class FMVScreen : public SingleWindowScreen {
|
||||
public:
|
||||
FMVScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~FMVScreen();
|
||||
void play(const Common::Path &name);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
|
||||
private:
|
||||
bool isPlaying();
|
||||
|
||||
Video::BinkDecoder *_decoder;
|
||||
Gfx::SurfaceRenderer *_surfaceRenderer;
|
||||
Gfx::Bitmap *_bitmap;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_FMV_PLAYER_H
|
||||
157
engines/stark/ui/world/gamescreen.cpp
Normal file
157
engines/stark/ui/world/gamescreen.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/* 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 "engines/stark/ui/world/gamescreen.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/actionmenu.h"
|
||||
#include "engines/stark/ui/world/dialogpanel.h"
|
||||
#include "engines/stark/ui/world/gamewindow.h"
|
||||
#include "engines/stark/ui/world/inventorywindow.h"
|
||||
#include "engines/stark/ui/world/topmenu.h"
|
||||
#include "engines/stark/resources/level.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
GameScreen::GameScreen(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Screen(Screen::kScreenGame),
|
||||
_gfx(gfx),
|
||||
_cursor(cursor) {
|
||||
|
||||
_topMenu = new TopMenu(_gfx, _cursor);
|
||||
_dialogPanel = new DialogPanel(_gfx, _cursor);
|
||||
_actionMenu = new ActionMenu(_gfx, _cursor);
|
||||
_inventoryWindow = new InventoryWindow(_gfx, _cursor, _actionMenu);
|
||||
_actionMenu->setInventory(_inventoryWindow);
|
||||
_gameWindow = new GameWindow(_gfx, _cursor, _actionMenu, _inventoryWindow);
|
||||
|
||||
_gameScreenWindows.push_back(_actionMenu);
|
||||
_gameScreenWindows.push_back(_inventoryWindow);
|
||||
_gameScreenWindows.push_back(_gameWindow);
|
||||
_gameScreenWindows.push_back(_topMenu);
|
||||
_gameScreenWindows.push_back(_dialogPanel);
|
||||
}
|
||||
|
||||
GameScreen::~GameScreen() {
|
||||
delete _gameWindow;
|
||||
delete _actionMenu;
|
||||
delete _topMenu;
|
||||
delete _dialogPanel;
|
||||
delete _inventoryWindow;
|
||||
}
|
||||
|
||||
void GameScreen::open() {
|
||||
pauseGame(false);
|
||||
StarkUserInterface->freeGameScreenThumbnail();
|
||||
}
|
||||
|
||||
void GameScreen::close() {
|
||||
_cursor->setMouseHint("");
|
||||
pauseGame(true);
|
||||
StarkUserInterface->saveGameScreenThumbnail();
|
||||
}
|
||||
|
||||
void GameScreen::handleGameLoop() {
|
||||
for (int i = _gameScreenWindows.size() - 1; i >= 0; i--) {
|
||||
_gameScreenWindows[i]->handleGameLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameScreen::render() {
|
||||
for (int i = _gameScreenWindows.size() - 1; i >= 0; i--) {
|
||||
_gameScreenWindows[i]->render();
|
||||
}
|
||||
}
|
||||
|
||||
InventoryWindow *GameScreen::getInventoryWindow() const {
|
||||
return _inventoryWindow;
|
||||
}
|
||||
|
||||
void GameScreen::reset() {
|
||||
_dialogPanel->reset();
|
||||
_gameWindow->reset();
|
||||
_inventoryWindow->reset();
|
||||
_actionMenu->close();
|
||||
}
|
||||
|
||||
GameWindow *GameScreen::getGameWindow() const {
|
||||
return _gameWindow;
|
||||
}
|
||||
|
||||
DialogPanel *GameScreen::getDialogPanel() const {
|
||||
return _dialogPanel;
|
||||
}
|
||||
|
||||
void GameScreen::handleMouseMove() {
|
||||
dispatchEvent(&Window::handleMouseMove);
|
||||
}
|
||||
|
||||
void GameScreen::handleClick() {
|
||||
dispatchEvent(&Window::handleClick);
|
||||
}
|
||||
|
||||
void GameScreen::handleRightClick() {
|
||||
dispatchEvent(&Window::handleRightClick);
|
||||
}
|
||||
|
||||
void GameScreen::handleDoubleClick() {
|
||||
dispatchEvent(&Window::handleDoubleClick);
|
||||
}
|
||||
|
||||
void GameScreen::dispatchEvent(WindowHandler handler) {
|
||||
for (uint i = 0; i < _gameScreenWindows.size(); i++) {
|
||||
if (_gameScreenWindows[i]->isMouseInside()) {
|
||||
(*_gameScreenWindows[i].*handler)();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScreen::onScreenChanged() {
|
||||
_cursor->onScreenChanged();
|
||||
_dialogPanel->onScreenChanged();
|
||||
_topMenu->onScreenChanged();
|
||||
_gameWindow->onScreenChanged();
|
||||
_actionMenu->onScreenChanged();
|
||||
}
|
||||
|
||||
void GameScreen::notifyInventoryItemEnabled(uint16 itemIndex) {
|
||||
_topMenu->notifyInventoryItemEnabled(itemIndex);
|
||||
}
|
||||
|
||||
void GameScreen::notifyDiaryEntryEnabled() {
|
||||
_topMenu->notifyDiaryEntryEnabled();
|
||||
}
|
||||
|
||||
void GameScreen::pauseGame(bool pause) {
|
||||
if (StarkGlobal->getLevel()) {
|
||||
StarkGlobal->getLevel()->onEnginePause(pause);
|
||||
}
|
||||
if (StarkGlobal->getCurrent()) {
|
||||
StarkGlobal->getCurrent()->getLevel()->onEnginePause(pause);
|
||||
StarkGlobal->getCurrent()->getLocation()->onEnginePause(pause);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
94
engines/stark/ui/world/gamescreen.h
Normal file
94
engines/stark/ui/world/gamescreen.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* 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 STARK_UI_WORLD_GAME_SCREEN_H
|
||||
#define STARK_UI_WORLD_GAME_SCREEN_H
|
||||
|
||||
#include "engines/stark/ui/screen.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ActionMenu;
|
||||
class Cursor;
|
||||
class DialogPanel;
|
||||
class GameWindow;
|
||||
class InventoryWindow;
|
||||
class TopMenu;
|
||||
class Window;
|
||||
|
||||
/**
|
||||
* Game world screen
|
||||
*
|
||||
* Container for all the game world windows
|
||||
*/
|
||||
class GameScreen : public Screen {
|
||||
public:
|
||||
GameScreen(Gfx::Driver *gfx, Cursor *cursor);
|
||||
virtual ~GameScreen();
|
||||
|
||||
// Screen API
|
||||
void open() override;
|
||||
void close() override;
|
||||
void handleGameLoop() override;
|
||||
void render() override;
|
||||
void onScreenChanged() override;
|
||||
void handleMouseMove() override;
|
||||
void handleClick() override;
|
||||
void handleRightClick() override;
|
||||
void handleDoubleClick() override;
|
||||
|
||||
/** Get individual windows */
|
||||
InventoryWindow *getInventoryWindow() const;
|
||||
GameWindow *getGameWindow() const;
|
||||
DialogPanel *getDialogPanel() const;
|
||||
|
||||
/** Clear any location dependant state */
|
||||
void reset();
|
||||
|
||||
/** A new item has been added to the player's inventory */
|
||||
void notifyInventoryItemEnabled(uint16 itemIndex);
|
||||
|
||||
/** A new entry has been added to the player's diary */
|
||||
void notifyDiaryEntryEnabled();
|
||||
|
||||
|
||||
private:
|
||||
Gfx::Driver *_gfx;
|
||||
Cursor *_cursor;
|
||||
|
||||
// Game Screen windows
|
||||
ActionMenu *_actionMenu;
|
||||
DialogPanel *_dialogPanel;
|
||||
InventoryWindow *_inventoryWindow;
|
||||
TopMenu *_topMenu;
|
||||
GameWindow *_gameWindow;
|
||||
|
||||
// Game screen windows array
|
||||
Common::Array<Window *> _gameScreenWindows;
|
||||
|
||||
typedef void (Window::*WindowHandler)();
|
||||
void dispatchEvent(WindowHandler handler);
|
||||
void pauseGame(bool pause);
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_WORLD_GAME_SCREEN_H
|
||||
290
engines/stark/ui/world/gamewindow.cpp
Normal file
290
engines/stark/ui/world/gamewindow.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/* 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 "engines/stark/ui/world/gamewindow.h"
|
||||
#include "engines/stark/scene.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/image.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
#include "engines/stark/resources/layer.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/gameinterface.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/actionmenu.h"
|
||||
#include "engines/stark/ui/world/inventorywindow.h"
|
||||
#include "engines/stark/visual/text.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
GameWindow::GameWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu, InventoryWindow *inventory) :
|
||||
Window(gfx, cursor),
|
||||
_actionMenu(actionMenu),
|
||||
_inventory(inventory),
|
||||
_objectUnderCursor(nullptr),
|
||||
_displayExit(false) {
|
||||
_position = Common::Rect(Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
|
||||
_position.translate(0, Gfx::Driver::kTopBorderHeight);
|
||||
_visible = true;
|
||||
|
||||
_fadeRenderer = _gfx->createFadeRenderer();
|
||||
|
||||
_exitArrow = StarkStaticProvider->getUIElement(StaticProvider::kExitArrow);
|
||||
_exitArrowLeft = StarkStaticProvider->getUIElement(StaticProvider::kExitArrowLeft);
|
||||
_exitArrowRight = StarkStaticProvider->getUIElement(StaticProvider::kExitArrowRight);
|
||||
|
||||
_exitLeftBoundary = 5;
|
||||
_exitRightBoundary = Gfx::Driver::kGameViewportWidth - _exitArrowRight->getWidth() - 5;
|
||||
}
|
||||
|
||||
GameWindow::~GameWindow() {
|
||||
delete _fadeRenderer;
|
||||
}
|
||||
|
||||
void GameWindow::onRender() {
|
||||
// List the items to render
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
_renderEntries = location->listRenderEntries();
|
||||
Gfx::LightEntryArray lightEntries = location->listLightEntries();
|
||||
|
||||
// Render all the scene items
|
||||
Gfx::RenderEntryArray::iterator element = _renderEntries.begin();
|
||||
while (element != _renderEntries.end()) {
|
||||
// Draw the current element
|
||||
(*element)->render(lightEntries);
|
||||
|
||||
// Go for the next one
|
||||
element++;
|
||||
}
|
||||
|
||||
if (_displayExit) {
|
||||
Common::Array<Common::Point> exitPositions = StarkGameInterface->listExitPositions();
|
||||
|
||||
for (uint i = 0; i < exitPositions.size(); ++i) {
|
||||
Common::Point pos = exitPositions[i];
|
||||
VisualImageXMG *exitImage = nullptr;
|
||||
|
||||
if (pos.x < _exitLeftBoundary) {
|
||||
pos.x = _exitLeftBoundary;
|
||||
exitImage = _exitArrowLeft;
|
||||
} else if (pos.x > _exitRightBoundary) {
|
||||
pos.x = _exitRightBoundary;
|
||||
exitImage = _exitArrowRight;
|
||||
} else {
|
||||
exitImage = _exitArrow;
|
||||
}
|
||||
|
||||
exitImage->render(pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
float fadeLevel = StarkScene->getFadeLevel();
|
||||
if ((1.0f - fadeLevel) > 0.00001f) {
|
||||
_fadeRenderer->render(fadeLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::onMouseMove(const Common::Point &pos) {
|
||||
_renderEntries = StarkGlobal->getCurrent()->getLocation()->listRenderEntries();
|
||||
|
||||
if (!StarkUserInterface->isInteractive()) {
|
||||
_objectUnderCursor = nullptr;
|
||||
_cursor->setCursorType(Cursor::kPassive);
|
||||
_cursor->setMouseHint("");
|
||||
return;
|
||||
}
|
||||
|
||||
int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
|
||||
int16 singlePossibleAction = -1;
|
||||
bool defaultAction = false;
|
||||
bool itemActive = false;
|
||||
|
||||
checkObjectAtPos(pos, selectedInventoryItem, singlePossibleAction, defaultAction);
|
||||
|
||||
if (selectedInventoryItem != -1 && !defaultAction) {
|
||||
VisualImageXMG *cursorImage = StarkGameInterface->getCursorImage(selectedInventoryItem);
|
||||
_cursor->setCursorImage(cursorImage);
|
||||
itemActive = singlePossibleAction == selectedInventoryItem;
|
||||
} else if (_objectUnderCursor) {
|
||||
switch (singlePossibleAction) {
|
||||
case -1:
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
break;
|
||||
case Resources::PATTable::kActionLook:
|
||||
_cursor->setCursorType(Cursor::kEye);
|
||||
break;
|
||||
case Resources::PATTable::kActionTalk:
|
||||
_cursor->setCursorType(Cursor::kMouth);
|
||||
break;
|
||||
case Resources::PATTable::kActionUse:
|
||||
_cursor->setCursorType(Cursor::kHand);
|
||||
break;
|
||||
default:
|
||||
VisualImageXMG *cursorImage = StarkGameInterface->getCursorImage(singlePossibleAction);
|
||||
_cursor->setCursorImage(cursorImage);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Not an object
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
}
|
||||
_cursor->setItemActive(itemActive);
|
||||
|
||||
Common::String mouseHint;
|
||||
if (_objectUnderCursor) {
|
||||
mouseHint = StarkGameInterface->getItemTitleAt(_objectUnderCursor, _objectRelativePosition);
|
||||
}
|
||||
_cursor->setMouseHint(mouseHint);
|
||||
}
|
||||
|
||||
void GameWindow::onClick(const Common::Point &pos) {
|
||||
if (!StarkGlobal->getCurrent()) {
|
||||
return; // No level is loaded yet, interaction is impossible
|
||||
}
|
||||
|
||||
if (!StarkUserInterface->isInteractive()) {
|
||||
StarkUserInterface->markInteractionDenied();
|
||||
return;
|
||||
}
|
||||
|
||||
_actionMenu->close();
|
||||
|
||||
int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
|
||||
int16 singlePossibleAction = -1;
|
||||
bool defaultAction;
|
||||
|
||||
checkObjectAtPos(pos, selectedInventoryItem, singlePossibleAction, defaultAction);
|
||||
|
||||
if (_objectUnderCursor) {
|
||||
if (singlePossibleAction != -1) {
|
||||
StarkGameInterface->itemDoActionAt(_objectUnderCursor, singlePossibleAction, _objectRelativePosition);
|
||||
} else if (selectedInventoryItem == -1) {
|
||||
_actionMenu->open(_objectUnderCursor, _objectRelativePosition);
|
||||
}
|
||||
} else {
|
||||
// The walk code expects unscaled absolute mouse coordinates
|
||||
StarkGameInterface->walkTo(_cursor->getMousePosition(true));
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::onRightClick(const Common::Point &pos) {
|
||||
if (!StarkUserInterface->isInteractive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16 selectedInventoryItem = _inventory->getSelectedInventoryItem();
|
||||
|
||||
if (selectedInventoryItem == -1) {
|
||||
_inventory->open();
|
||||
} else {
|
||||
_inventory->setSelectedInventoryItem(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::onDoubleClick(const Common::Point &pos) {
|
||||
if (!StarkUserInterface->isInteractive()) {
|
||||
StarkUserInterface->markInteractionDenied();
|
||||
return;
|
||||
}
|
||||
|
||||
if (StarkGameInterface->isAprilWalking()) {
|
||||
StarkGameInterface->setAprilRunning();
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::checkObjectAtPos(const Common::Point &pos, int16 selectedInventoryItem, int16 &singlePossibleAction, bool &isDefaultAction) {
|
||||
_objectUnderCursor = nullptr;
|
||||
singlePossibleAction = -1;
|
||||
isDefaultAction = false;
|
||||
|
||||
Math::Ray ray = StarkScene->makeRayFromMouse(_cursor->getMousePosition(true));
|
||||
|
||||
Common::Rect cursorRect;
|
||||
if (selectedInventoryItem != -1) {
|
||||
cursorRect = _cursor->getHotRectangle();
|
||||
cursorRect.translate(pos.x, pos.y);
|
||||
}
|
||||
|
||||
// Render entries are sorted from the farthest to the camera to the nearest
|
||||
// Loop in reverse order
|
||||
for (int i = _renderEntries.size() - 1; i >= 0; i--) {
|
||||
if (_renderEntries[i]->containsPoint(pos, _objectRelativePosition, cursorRect)
|
||||
|| _renderEntries[i]->intersectRay(ray)) {
|
||||
_objectUnderCursor = _renderEntries[i]->getOwner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_objectUnderCursor || !StarkGameInterface->itemHasActionAt(_objectUnderCursor, _objectRelativePosition, -1)) {
|
||||
// Only consider items with runnable scripts
|
||||
_objectUnderCursor = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
int32 defaultAction = StarkGameInterface->itemGetDefaultActionAt(_objectUnderCursor, _objectRelativePosition);
|
||||
if (defaultAction != -1) {
|
||||
// Use the default action if there is one
|
||||
singlePossibleAction = defaultAction;
|
||||
isDefaultAction = true;
|
||||
} else if (selectedInventoryItem != -1) {
|
||||
// Use the selected inventory item if there is one
|
||||
if (StarkGameInterface->itemHasActionAt(_objectUnderCursor, _objectRelativePosition, selectedInventoryItem)) {
|
||||
singlePossibleAction = selectedInventoryItem;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, use stock actions
|
||||
Resources::ActionArray actionsPossible = StarkGameInterface->listStockActionsPossibleForObjectAt(
|
||||
_objectUnderCursor, _objectRelativePosition);
|
||||
|
||||
if (actionsPossible.size() == 1) {
|
||||
singlePossibleAction = actionsPossible[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::reset() {
|
||||
_renderEntries.clear();
|
||||
_objectUnderCursor = nullptr;
|
||||
_objectRelativePosition.x = 0;
|
||||
_objectRelativePosition.y = 0;
|
||||
}
|
||||
|
||||
void GameWindow::onScreenChanged() {
|
||||
// May be called when resources have not been loaded
|
||||
if (!StarkGlobal->getCurrent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
Common::Array<Resources::ImageText *> images = location->listChildrenRecursive<Resources::ImageText>(Resources::Image::kImageText);
|
||||
|
||||
for (uint i = 0; i < images.size(); i++) {
|
||||
images[i]->resetVisual();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
78
engines/stark/ui/world/gamewindow.h
Normal file
78
engines/stark/ui/world/gamewindow.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* 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 STARK_UI_GAME_WINDOW_H
|
||||
#define STARK_UI_GAME_WINDOW_H
|
||||
|
||||
#include "engines/stark/gfx/faderenderer.h"
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ActionMenu;
|
||||
class InventoryWindow;
|
||||
|
||||
class GameWindow : public Window {
|
||||
public:
|
||||
GameWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu, InventoryWindow *inventory);
|
||||
virtual ~GameWindow();
|
||||
|
||||
/** Clear the location dependent state */
|
||||
void reset();
|
||||
|
||||
/** Update when the screen resolution has changed */
|
||||
void onScreenChanged();
|
||||
|
||||
/** Toggle the display of exit locations */
|
||||
void toggleExitDisplay() { _displayExit = !_displayExit; }
|
||||
|
||||
protected:
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onRightClick(const Common::Point &pos) override;
|
||||
void onDoubleClick(const Common::Point &pos) override;
|
||||
void onRender() override;
|
||||
|
||||
void checkObjectAtPos(const Common::Point &pos, int16 selectedInventoryItem, int16 &singlePossibleAction, bool &isDefaultAction);
|
||||
|
||||
ActionMenu *_actionMenu;
|
||||
InventoryWindow *_inventory;
|
||||
|
||||
Gfx::RenderEntryArray _renderEntries;
|
||||
Resources::ItemVisual *_objectUnderCursor;
|
||||
Common::Point _objectRelativePosition;
|
||||
|
||||
Gfx::FadeRenderer *_fadeRenderer;
|
||||
|
||||
VisualImageXMG *_exitArrow, *_exitArrowLeft, *_exitArrowRight;
|
||||
int _exitLeftBoundary, _exitRightBoundary;
|
||||
|
||||
bool _displayExit;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_GAME_WINDOW_H
|
||||
302
engines/stark/ui/world/inventorywindow.cpp
Normal file
302
engines/stark/ui/world/inventorywindow.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/* 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 "engines/stark/ui/world/inventorywindow.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/actionmenu.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/pattable.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/staticprovider.h"
|
||||
#include "engines/stark/services/gameinterface.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
static const int kAutoCloseSuspended = -1;
|
||||
static const int kAutoCloseDisabled = -2;
|
||||
static const int kAutoCloseDelay = 200;
|
||||
|
||||
InventoryWindow::InventoryWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu) :
|
||||
Window(gfx, cursor),
|
||||
_actionMenu(actionMenu),
|
||||
_firstVisibleSlot(0),
|
||||
_selectedInventoryItem(-1),
|
||||
_autoCloseTimeRemaining(kAutoCloseDisabled) {
|
||||
// The window has the same size as the game window
|
||||
_position = Common::Rect(Gfx::Driver::kGameViewportWidth, Gfx::Driver::kGameViewportHeight);
|
||||
_position.translate(0, Gfx::Driver::kTopBorderHeight);
|
||||
|
||||
_backgroundImage = StarkStaticProvider->getUIImage(StaticProvider::kInventoryBg);
|
||||
|
||||
// Center the background in the window
|
||||
_backgroundRect = Common::Rect(_backgroundImage->getWidth(), _backgroundImage->getHeight());
|
||||
_backgroundRect.translate((_position.width() - _backgroundRect.width()) / 2,
|
||||
(_position.height() - _backgroundRect.height()) / 2);
|
||||
|
||||
_scrollUpArrowImage = StarkStaticProvider->getUIElement(StaticProvider::kInventoryScrollUpArrow);
|
||||
_scrollDownArrowImage = StarkStaticProvider->getUIElement(StaticProvider::kInventoryScrollDownArrow);
|
||||
|
||||
_scrollUpArrowRect = Common::Rect(_scrollUpArrowImage->getWidth(), _scrollUpArrowImage->getHeight());
|
||||
_scrollUpArrowRect.translate(_backgroundRect.right - _scrollUpArrowRect.width(),
|
||||
_backgroundRect.top + 2);
|
||||
_scrollDownArrowRect = Common::Rect(_scrollDownArrowImage->getWidth(), _scrollDownArrowImage->getHeight());
|
||||
_scrollDownArrowRect.translate(_backgroundRect.right - _scrollDownArrowRect.width(),
|
||||
_backgroundRect.bottom - _scrollDownArrowRect.height() - 2);
|
||||
}
|
||||
|
||||
void InventoryWindow::open() {
|
||||
if (!_visible) {
|
||||
_actionMenu->close();
|
||||
}
|
||||
|
||||
_visible = true;
|
||||
|
||||
// The user needs to move the mouse over the background at least once
|
||||
// before autoclose is enabled.
|
||||
_autoCloseTimeRemaining = kAutoCloseDisabled;
|
||||
}
|
||||
|
||||
void InventoryWindow::close() {
|
||||
if (_visible) {
|
||||
_actionMenu->close();
|
||||
}
|
||||
|
||||
_visible = false;
|
||||
}
|
||||
|
||||
void InventoryWindow::setSelectedInventoryItem(int16 selectedInventoryItem) {
|
||||
// The first 4 elements are UI elements (Eye, Mouth, Hand, ...)
|
||||
// Scripts pass 0 when they want to clear the selected inventory item
|
||||
if (selectedInventoryItem < 4) {
|
||||
_selectedInventoryItem = -1;
|
||||
} else {
|
||||
_selectedInventoryItem = selectedInventoryItem;
|
||||
}
|
||||
}
|
||||
|
||||
int16 InventoryWindow::getSelectedInventoryItem() const {
|
||||
return _selectedInventoryItem;
|
||||
}
|
||||
|
||||
Common::Rect InventoryWindow::getSlotRect(uint32 slot) const {
|
||||
Common::Rect rect = Common::Rect(64, 64);
|
||||
rect.translate(
|
||||
96 * (slot % 5) + _backgroundRect.left + 24,
|
||||
96 * (slot / 5) + _backgroundRect.left + 8); // The original uses left here as well
|
||||
return rect;
|
||||
}
|
||||
|
||||
Common::Rect InventoryWindow::getItemRect(uint32 slot, VisualImageXMG *image) const {
|
||||
Common::Rect rect = getSlotRect(slot % _visibleSlotsCount);
|
||||
|
||||
// Center the image in the inventory slot
|
||||
rect.translate((rect.width() - image->getWidth()) / 2,
|
||||
(rect.height() - image->getHeight()) / 2);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void InventoryWindow::onRender() {
|
||||
_renderEntries = StarkGlobal->getInventory()->getInventoryRenderEntries();
|
||||
|
||||
_backgroundImage->render(Common::Point(_backgroundRect.left, _backgroundRect.top), false);
|
||||
drawScrollArrows();
|
||||
|
||||
for (uint i = _firstVisibleSlot; i < _renderEntries.size() && isSlotVisible(i); i++) {
|
||||
VisualImageXMG *image = _renderEntries[i]->getImage();
|
||||
|
||||
// Get the item rect
|
||||
Common::Rect pos = getItemRect(i, image);
|
||||
|
||||
image->render(Common::Point(pos.left, pos.top), false);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::drawScrollArrows() const {
|
||||
if (canScrollUp()) {
|
||||
_scrollUpArrowImage->render(Common::Point(_scrollUpArrowRect.left, _scrollUpArrowRect.top), false);
|
||||
}
|
||||
if (canScrollDown()) {
|
||||
_scrollDownArrowImage->render(Common::Point(_scrollDownArrowRect.left, _scrollDownArrowRect.top), false);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::checkObjectAtPos(Common::Point pos, Resources::ItemVisual **item, int16 selectedInventoryItem, int16 &singlePossibleAction) {
|
||||
*item = nullptr;
|
||||
singlePossibleAction = -1;
|
||||
|
||||
// Check for inventory mouse overs
|
||||
for (uint i = _firstVisibleSlot; i < _renderEntries.size() && isSlotVisible(i); i++) {
|
||||
VisualImageXMG *image = _renderEntries[i]->getImage();
|
||||
Common::Rect itemRect = getItemRect(i, image);
|
||||
|
||||
if (itemRect.contains(pos)) {
|
||||
*item = _renderEntries[i]->getOwner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*item) {
|
||||
// No item at specified position
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedInventoryItem == -1) {
|
||||
Resources::ActionArray actionsPossible;
|
||||
actionsPossible = StarkGameInterface->listStockActionsPossibleForObject(*item);
|
||||
|
||||
if (actionsPossible.empty()) {
|
||||
// The item can still be taken
|
||||
singlePossibleAction = Resources::PATTable::kActionUse;
|
||||
}
|
||||
} else {
|
||||
if (StarkGameInterface->itemHasAction(*item, selectedInventoryItem)) {
|
||||
singlePossibleAction = selectedInventoryItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::onMouseMove(const Common::Point &pos) {
|
||||
Resources::ItemVisual *hoveredItem = nullptr;
|
||||
int16 hoveredItemAction = -1;
|
||||
|
||||
checkObjectAtPos(pos, &hoveredItem, _selectedInventoryItem, hoveredItemAction);
|
||||
|
||||
if (_selectedInventoryItem == -1) {
|
||||
if (hoveredItem) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
} else if ((canScrollDown() && _scrollDownArrowRect.contains(pos))
|
||||
|| (canScrollUp() && _scrollUpArrowRect.contains(pos))) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
_cursor->setItemActive(false);
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
}
|
||||
_cursor->setItemActive(false);
|
||||
} else {
|
||||
VisualImageXMG *cursorImage = StarkGameInterface->getCursorImage(_selectedInventoryItem);
|
||||
_cursor->setCursorImage(cursorImage);
|
||||
_cursor->setItemActive(hoveredItemAction == _selectedInventoryItem);
|
||||
}
|
||||
|
||||
if (hoveredItem) {
|
||||
Common::String hint = StarkGameInterface->getItemTitle(hoveredItem);
|
||||
_cursor->setMouseHint(hint);
|
||||
} else {
|
||||
_cursor->setMouseHint("");
|
||||
}
|
||||
|
||||
if (!_backgroundRect.contains(pos)) {
|
||||
if (_autoCloseTimeRemaining == kAutoCloseSuspended) {
|
||||
_autoCloseTimeRemaining = kAutoCloseDelay;
|
||||
}
|
||||
} else {
|
||||
_autoCloseTimeRemaining = kAutoCloseSuspended;
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::onClick(const Common::Point &pos) {
|
||||
_actionMenu->close();
|
||||
|
||||
Resources::ItemVisual *clickedItem = nullptr;
|
||||
int16 clickedItemAction = -1;
|
||||
checkObjectAtPos(pos, &clickedItem, _selectedInventoryItem, clickedItemAction);
|
||||
|
||||
if (clickedItem) {
|
||||
// An item was clicked
|
||||
if (clickedItemAction != -1) {
|
||||
// A single action is possible
|
||||
if (clickedItemAction == Resources::PATTable::kActionUse) {
|
||||
setSelectedInventoryItem(clickedItem->getIndex());
|
||||
} else {
|
||||
StarkGameInterface->itemDoAction(clickedItem, clickedItemAction);
|
||||
}
|
||||
} else {
|
||||
// Multiple actions are possible
|
||||
if (_selectedInventoryItem == -1) {
|
||||
_actionMenu->open(clickedItem, Common::Point());
|
||||
}
|
||||
}
|
||||
} else if (_scrollDownArrowRect.contains(pos)) {
|
||||
if (canScrollDown()) {
|
||||
scrollDown();
|
||||
}
|
||||
} else if (_scrollUpArrowRect.contains(pos)) {
|
||||
if (canScrollUp()) {
|
||||
scrollUp();
|
||||
}
|
||||
} else {
|
||||
// Nothing was under the mouse cursor, close the inventory
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::onRightClick(const Common::Point &pos) {
|
||||
if (_selectedInventoryItem == -1) {
|
||||
close();
|
||||
} else {
|
||||
setSelectedInventoryItem(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::reset() {
|
||||
_renderEntries.clear();
|
||||
}
|
||||
|
||||
bool InventoryWindow::isSlotVisible(uint32 slot) const {
|
||||
return slot < _firstVisibleSlot + _visibleSlotsCount;
|
||||
}
|
||||
|
||||
bool InventoryWindow::canScrollDown() const {
|
||||
return _renderEntries.size() - _firstVisibleSlot > _visibleSlotsCount;
|
||||
}
|
||||
|
||||
bool InventoryWindow::canScrollUp() const {
|
||||
return _firstVisibleSlot > 0;
|
||||
}
|
||||
|
||||
void InventoryWindow::scrollDown() {
|
||||
if (canScrollDown()) {
|
||||
_firstVisibleSlot += _visibleSlotsCount;
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::scrollUp() {
|
||||
if (canScrollUp()) {
|
||||
_firstVisibleSlot -= _visibleSlotsCount;
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryWindow::onGameLoop() {
|
||||
if (_autoCloseTimeRemaining >= 0 && !_actionMenu->isVisible()) {
|
||||
_autoCloseTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
if (_autoCloseTimeRemaining <= 0) {
|
||||
_autoCloseTimeRemaining = kAutoCloseSuspended;
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End of namespace Stark
|
||||
95
engines/stark/ui/world/inventorywindow.h
Normal file
95
engines/stark/ui/world/inventorywindow.h
Normal 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 STARK_UI_INVENTORY_WINDOW_H
|
||||
#define STARK_UI_INVENTORY_WINDOW_H
|
||||
|
||||
#include "engines/stark/gfx/renderentry.h"
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ActionMenu;
|
||||
|
||||
namespace Resources {
|
||||
class Anim;
|
||||
}
|
||||
|
||||
class InventoryWindow : public Window {
|
||||
public:
|
||||
InventoryWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu);
|
||||
~InventoryWindow() override {}
|
||||
|
||||
void open();
|
||||
void close();
|
||||
|
||||
void setSelectedInventoryItem(int16 selectedInventoryItem);
|
||||
int16 getSelectedInventoryItem() const;
|
||||
|
||||
/** Clear the location dependent state */
|
||||
void reset();
|
||||
|
||||
/** Scroll the inventory */
|
||||
void scrollDown();
|
||||
void scrollUp();
|
||||
|
||||
protected:
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
void onRightClick(const Common::Point &pos) override;
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
|
||||
void checkObjectAtPos(Common::Point pos, Resources::ItemVisual **item, int16 selectedInventoryItem, int16 &singlePossibleAction);
|
||||
Common::Rect getSlotRect(uint32 slot) const;
|
||||
Common::Rect getItemRect(uint32 slot, VisualImageXMG *image) const;
|
||||
bool isSlotVisible(uint32 slot) const;
|
||||
bool canScrollUp() const;
|
||||
bool canScrollDown() const;
|
||||
void drawScrollArrows() const;
|
||||
|
||||
private:
|
||||
ActionMenu *_actionMenu;
|
||||
|
||||
VisualImageXMG *_backgroundImage;
|
||||
Common::Rect _backgroundRect;
|
||||
|
||||
VisualImageXMG *_scrollUpArrowImage;
|
||||
VisualImageXMG *_scrollDownArrowImage;
|
||||
Common::Rect _scrollUpArrowRect;
|
||||
Common::Rect _scrollDownArrowRect;
|
||||
|
||||
uint32 _firstVisibleSlot;
|
||||
static const uint32 _visibleSlotsCount = 15;
|
||||
|
||||
Gfx::RenderEntryArray _renderEntries;
|
||||
int16 _selectedInventoryItem;
|
||||
|
||||
int32 _autoCloseTimeRemaining;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_INVENTORY_WINDOW_H
|
||||
191
engines/stark/ui/world/topmenu.cpp
Normal file
191
engines/stark/ui/world/topmenu.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/* 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 "engines/stark/ui/world/topmenu.h"
|
||||
#include "engines/stark/ui/cursor.h"
|
||||
#include "engines/stark/ui/world/button.h"
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/resources/knowledgeset.h"
|
||||
#include "engines/stark/resources/sound.h"
|
||||
#include "engines/stark/services/diary.h"
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/userinterface.h"
|
||||
#include "engines/stark/services/gamemessage.h"
|
||||
#include "engines/stark/visual/image.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
TopMenu::TopMenu(Gfx::Driver *gfx, Cursor *cursor) :
|
||||
Window(gfx, cursor),
|
||||
_widgetsVisible(false),
|
||||
_newInventoryItemExplosionAnimTimeRemaining(0),
|
||||
_newDiaryEntryAnimTimeRemaining(0),
|
||||
_newInventoryItemChestClosingAnimTimeRemaining(0) {
|
||||
|
||||
_position = Common::Rect(Gfx::Driver::kOriginalWidth, Gfx::Driver::kTopBorderHeight);
|
||||
_visible = true;
|
||||
|
||||
Common::String inventoryText = StarkGameMessage->getTextByKey(GameMessage::kInventory);
|
||||
Common::String optionsText = StarkGameMessage->getTextByKey(GameMessage::kOptions);
|
||||
Common::String quitText = StarkGameMessage->getTextByKey(GameMessage::kQuit);
|
||||
|
||||
_inventoryButton = new Button(inventoryText, StaticProvider::kInventory, Common::Point(32, 2), Button::kAlignLeft, Common::Point(64, 20));
|
||||
_optionsButton = new Button(optionsText, StaticProvider::kDiaryNormal, Common::Point(560, 2), Button::kAlignRight, Common::Point(560, 20));
|
||||
_exitButton = new Button(quitText, StaticProvider::kQuit, Common::Point(608, 2), Button::kAlignRight, Common::Point(608, 20));
|
||||
|
||||
_inventoryNewItemSound = StarkStaticProvider->getUISound(StaticProvider::kInventoryNewItem);
|
||||
}
|
||||
|
||||
TopMenu::~TopMenu() {
|
||||
delete _exitButton;
|
||||
delete _inventoryButton;
|
||||
delete _optionsButton;
|
||||
}
|
||||
|
||||
void TopMenu::onGameLoop() {
|
||||
_widgetsVisible = (isMouseInside() && StarkUserInterface->isInteractive()) || isAnimationPlaying();
|
||||
|
||||
if (!_widgetsVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (StarkDiary->hasUnreadEntries()) {
|
||||
_optionsButton->setUIElement(StaticProvider::kDiaryTabbed);
|
||||
} else {
|
||||
_optionsButton->setUIElement(StaticProvider::kDiaryNormal);
|
||||
}
|
||||
|
||||
updateAnimations();
|
||||
}
|
||||
|
||||
void TopMenu::onRender() {
|
||||
if (!_widgetsVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
_inventoryButton->render();
|
||||
_optionsButton->render();
|
||||
_exitButton->render();
|
||||
}
|
||||
|
||||
bool TopMenu::isAnimationPlaying() const {
|
||||
return _newInventoryItemExplosionAnimTimeRemaining > 0
|
||||
|| _newDiaryEntryAnimTimeRemaining > 0
|
||||
|| _newInventoryItemChestClosingAnimTimeRemaining > 0;
|
||||
}
|
||||
|
||||
void TopMenu::updateAnimations() {
|
||||
if (_newInventoryItemExplosionAnimTimeRemaining > 0) {
|
||||
_newInventoryItemExplosionAnimTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
if (_newInventoryItemExplosionAnimTimeRemaining <= 0) {
|
||||
_inventoryButton->stopImageExplosion();
|
||||
|
||||
_newInventoryItemChestClosingAnimTimeRemaining = 20 * 33; // 20 frames at 30 fps
|
||||
_inventoryButton->goToAnimStatement(12);
|
||||
}
|
||||
}
|
||||
|
||||
if (_newInventoryItemChestClosingAnimTimeRemaining > 0) {
|
||||
_newInventoryItemChestClosingAnimTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
|
||||
if (_newDiaryEntryAnimTimeRemaining > 0) {
|
||||
_newDiaryEntryAnimTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
if (_newDiaryEntryAnimTimeRemaining <= 0) {
|
||||
_optionsButton->stopImageFlashing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TopMenu::onMouseMove(const Common::Point &pos) {
|
||||
if (_widgetsVisible && StarkUserInterface->isInteractive()) {
|
||||
Button *hoveredButton = getButtonAtPosition(pos);
|
||||
if (hoveredButton) {
|
||||
_cursor->setCursorType(Cursor::kActive);
|
||||
hoveredButton->showButtonHint();
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kDefault);
|
||||
}
|
||||
} else {
|
||||
_cursor->setCursorType(Cursor::kPassive);
|
||||
}
|
||||
}
|
||||
|
||||
void TopMenu::onClick(const Common::Point &pos) {
|
||||
if (!_widgetsVisible || !StarkUserInterface->isInteractive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_exitButton->containsPoint(pos)) {
|
||||
StarkUserInterface->confirm(GameMessage::kQuitGamePrompt, StarkUserInterface,
|
||||
&UserInterface::requestQuitToMainMenu);
|
||||
}
|
||||
|
||||
if (_inventoryButton->containsPoint(pos)) {
|
||||
StarkUserInterface->inventoryOpen(true);
|
||||
}
|
||||
|
||||
if (_optionsButton->containsPoint(pos)) {
|
||||
StarkUserInterface->optionsOpen();
|
||||
}
|
||||
}
|
||||
|
||||
Button *TopMenu::getButtonAtPosition(const Common::Point &point) const {
|
||||
if (_exitButton->containsPoint(point)) {
|
||||
return _exitButton;
|
||||
} else if (_optionsButton->containsPoint(point)) {
|
||||
return _optionsButton;
|
||||
} else if (_inventoryButton->containsPoint(point)) {
|
||||
return _inventoryButton;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TopMenu::onScreenChanged() {
|
||||
_exitButton->resetHintVisual();
|
||||
_inventoryButton->resetHintVisual();
|
||||
_optionsButton->resetHintVisual();
|
||||
}
|
||||
|
||||
void TopMenu::notifyInventoryItemEnabled(uint16 itemIndex) {
|
||||
_newInventoryItemExplosionAnimTimeRemaining = 128 * 33; // 128 frames at 30 fps
|
||||
_inventoryButton->goToAnimStatement(2);
|
||||
|
||||
Visual *inventoryItemImage = StarkGlobal->getInventory()->getInventoryItemVisual(itemIndex);
|
||||
_inventoryButton->startImageExplosion(inventoryItemImage->get<VisualImageXMG>());
|
||||
|
||||
assert(_inventoryNewItemSound);
|
||||
_inventoryNewItemSound->stop();
|
||||
_inventoryNewItemSound->play();
|
||||
}
|
||||
|
||||
void TopMenu::notifyDiaryEntryEnabled() {
|
||||
if (StarkDiary->isEnabled()) {
|
||||
_newDiaryEntryAnimTimeRemaining = 5000;
|
||||
VisualImageXMG *diaryImage = StarkStaticProvider->getUIElement(StaticProvider::kDiaryTabbed);
|
||||
_optionsButton->startImageFlashing(diaryImage);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
77
engines/stark/ui/world/topmenu.h
Normal file
77
engines/stark/ui/world/topmenu.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 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 STARK_UI_TOPMENU_H
|
||||
#define STARK_UI_TOPMENU_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "engines/stark/ui/window.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class VisualImageXMG;
|
||||
class Button;
|
||||
|
||||
namespace Resources {
|
||||
class Sound;
|
||||
}
|
||||
|
||||
class TopMenu : public Window {
|
||||
public:
|
||||
TopMenu(Gfx::Driver *gfx, Cursor *cursor);
|
||||
~TopMenu() override;
|
||||
|
||||
// Window API
|
||||
void onGameLoop() override;
|
||||
void onRender() override;
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
void onClick(const Common::Point &pos) override;
|
||||
|
||||
/** The screen resolution changed, rebuild the text accordingly */
|
||||
void onScreenChanged();
|
||||
|
||||
/** A new item has been added to the player's inventory. Play relevant animation */
|
||||
void notifyInventoryItemEnabled(uint16 itemIndex);
|
||||
|
||||
/** A new entry has been added to the player's diary. Play relevant animation */
|
||||
void notifyDiaryEntryEnabled();
|
||||
|
||||
private:
|
||||
Button *getButtonAtPosition(const Common::Point &point) const;
|
||||
bool isAnimationPlaying() const;
|
||||
void updateAnimations();
|
||||
|
||||
bool _widgetsVisible;
|
||||
|
||||
Button *_inventoryButton;
|
||||
Button *_exitButton;
|
||||
Button *_optionsButton;
|
||||
int _newInventoryItemExplosionAnimTimeRemaining;
|
||||
int _newInventoryItemChestClosingAnimTimeRemaining;
|
||||
int _newDiaryEntryAnimTimeRemaining;
|
||||
Resources::Sound *_inventoryNewItemSound;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_UI_TOPMENU_H
|
||||
Reference in New Issue
Block a user