Initial commit

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

195
engines/stark/ui/cursor.cpp Normal file
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

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

View 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

View 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