Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

STARK: Improve the main menu #1467

Merged
merged 10 commits into from Jun 30, 2018
6 changes: 6 additions & 0 deletions engines/stark/gfx/renderentry.cpp
Expand Up @@ -194,10 +194,16 @@ bool RenderEntry::intersectRay(const Math::Ray &ray) const {
}

VisualImageXMG *RenderEntry::getImage() const {
if (!_visual) {
return nullptr;
}
return _visual->get<VisualImageXMG>();
}

VisualText *RenderEntry::getText() const {
if (!_visual) {
return nullptr;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the implementation of GameWindow::onScreenChanged() I came across with render entries with no Visual. It feels strange to me since a RenderEntry without a Visual seems pretty useless. But anyway it needs to be handled.

}
return _visual->get<VisualText>();
}

Expand Down
6 changes: 3 additions & 3 deletions engines/stark/resources/command.cpp
Expand Up @@ -61,6 +61,7 @@
#include "engines/stark/services/global.h"
#include "engines/stark/services/resourceprovider.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/services/settings.h"

#include "common/random.h"

Expand Down Expand Up @@ -460,8 +461,7 @@ Math::Vector3d Command::getObjectPosition(const ResourceReference &targetRef, in
}

Command *Command::opGameEnd() {
// TODO: Display the main menu instead of exiting
StarkUserInterface->notifyShouldExit();
StarkUserInterface->requestQuitToMainMenu();

return nextCommand();
}
Expand All @@ -483,7 +483,7 @@ Command *Command::opFloatScene(int32 periodMs, int32 amplitudeIn, int32 offsetIn
}

Command *Command::opBookOfSecretsOpen() {
warning("(TODO: Implement) opBookOfSecretsOpen()");
StarkSettings->enableBookOfSecrets();

return nextCommand();
}
Expand Down
3 changes: 3 additions & 0 deletions engines/stark/resources/location.h
Expand Up @@ -139,6 +139,9 @@ class Location : public Object {
/** Get a render entry with a given name, return null when not found */
Gfx::RenderEntry *getRenderEntryByName(const Common::String &name);

/** Obtain the list of all the inner layers */
Common::Array<Layer *> listLayers() { return _layers; }

protected:
void printData() override;

Expand Down
2 changes: 1 addition & 1 deletion engines/stark/scene.cpp
Expand Up @@ -36,7 +36,7 @@ Scene::Scene(Gfx::Driver *gfx) :
_fov(45.0),
_nearClipPlane(100.0),
_farClipPlane(64000.0),
_fadeLevel(0.0),
_fadeLevel(1.0),
_floatOffset(0.0) {
}

Expand Down
9 changes: 9 additions & 0 deletions engines/stark/services/settings.h
Expand Up @@ -85,6 +85,15 @@ class Settings {
/** Check whether low-resolution fmv is available */
bool hasLowResFMV() { return _hasLowRes; }

/** Enable the book of secrets */
void enableBookOfSecrets() {
ConfMan.setBool("xoBfOsterceS", true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just follow the name in "w_world.ini" :p

ConfMan.flushToDisk();
}

/** Check whether the book of secrets is enabled */
bool hasBookOfSecrets() { return ConfMan.hasKey("xoBfOsterceS"); }

private:
Audio::Mixer *_mixer;
bool _hasLowRes;
Expand Down
6 changes: 5 additions & 1 deletion engines/stark/services/userinterface.cpp
Expand Up @@ -61,6 +61,7 @@ UserInterface::UserInterface(Gfx::Driver *gfx) :
_diaryPagesScreen(nullptr),
_dialogScreen(nullptr),
_exitGame(false),
_quitToMainMenu(false),
_fmvScreen(nullptr),
_gameScreen(nullptr),
_interactive(true),
Expand Down Expand Up @@ -186,11 +187,14 @@ void UserInterface::backPrevScreen() {
_prevScreenNameStack.pop();
}

void UserInterface::quitToMainMenu() {
void UserInterface::performQuitToMainMenu() {
assert(_quitToMainMenu);

changeScreen(Screen::kScreenGame);
StarkResourceProvider->shutdown();
changeScreen(Screen::kScreenMainMenu);
_prevScreenNameStack.clear();
_quitToMainMenu = false;
}

void UserInterface::restoreScreenHistory() {
Expand Down
5 changes: 4 additions & 1 deletion engines/stark/services/userinterface.h
Expand Up @@ -96,7 +96,9 @@ class UserInterface {
void backPrevScreen();

/** Back to the main menu screen and rest resources */
void quitToMainMenu();
void requestQuitToMainMenu() { _quitToMainMenu = true; }
bool hasQuitToMainMenuRequest() { return _quitToMainMenu; }
void performQuitToMainMenu();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Script needs to request quitting to the main menu. In the previous implementation, immediately quitting will trigger the deallocation of the Script object and therefore crash the game and it took me an eternity to realize this!!! . So it is cached here and is performed later in the update part of the game's main loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building with Address Sanitizer enabled helps a lot to understand these issues. It tells precisely the cause of the use after free. It's probably a bit of a pain to get it working on Windows, and IMO it's well worth having a Linux install somewhere just to be able to use it when needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah now I know the good of Linux...


/** Restore the screen travelling history to the initial state*/
void restoreScreenHistory();
Expand Down Expand Up @@ -173,6 +175,7 @@ class UserInterface {
Gfx::Driver *_gfx;
Cursor *_cursor;
bool _exitGame;
bool _quitToMainMenu;

bool _interactive;
bool _interactionAttemptDenied;
Expand Down
4 changes: 4 additions & 0 deletions engines/stark/stark.cpp
Expand Up @@ -176,6 +176,10 @@ void StarkEngine::mainLoop() {
break;
}

if (_userInterface->hasQuitToMainMenuRequest()) {
_userInterface->performQuitToMainMenu();
}

if (_resourceProvider->hasLocationChangeRequest()) {
_global->setNormalSpeed();
_resourceProvider->performLocationChange();
Expand Down
6 changes: 6 additions & 0 deletions engines/stark/ui/cursor.cpp
Expand Up @@ -76,6 +76,12 @@ void Cursor::setFading(bool fading) {
_fading = fading;
}

void Cursor::onScreenChanged() {
if (_mouseText) {
_mouseText->resetTexture();
}
}

void Cursor::updateFadeLevel() {
if (_fading) {
if (_fadeLevelIncreasing) {
Expand Down
3 changes: 3 additions & 0 deletions engines/stark/ui/cursor.h
Expand Up @@ -52,6 +52,9 @@ class Cursor {
/** Make cycle the cursor's brightness */
void setFading(bool fading);

/** Update when the screen resolution has changed */
void onScreenChanged();

Common::Point getMousePosition(bool unscaled = false) const;

enum CursorType {
Expand Down
2 changes: 1 addition & 1 deletion engines/stark/ui/menu/diaryindex.cpp
Expand Up @@ -122,7 +122,7 @@ void DiaryIndexScreen::backHandler() {
}

void DiaryIndexScreen::quitHandler() {
StarkUserInterface->quitToMainMenu();
StarkUserInterface->requestQuitToMainMenu();
}

void DiaryIndexScreen::loadHandler() {
Expand Down
38 changes: 22 additions & 16 deletions engines/stark/ui/menu/mainmenu.cpp
Expand Up @@ -26,13 +26,12 @@
#include "engines/stark/services/global.h"
#include "engines/stark/services/settings.h"

#include "common/config-manager.h"
#include "common/translation.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/visual/text.h"

#include "gui/saveload.h"
#include "gui/message.h"
#include "engines/stark/scene.h"

#include "engines/engine.h"
#include "common/config-manager.h"

namespace Stark {

Expand Down Expand Up @@ -72,7 +71,7 @@ void MainMenuScreen::open() {

_widgets.push_back(new StaticLocationWidget(
"Box",
nullptr,
CLICK_HANDLER(MainMenuScreen, boxHandler),
MOVE_HANDLER(MainMenuScreen, helpTextHandler<kBox>)));
_widgets.back()->setupSounds(0, 1);

Expand Down Expand Up @@ -124,15 +123,7 @@ void MainMenuScreen::open() {
nullptr));
_widgets.back()->setVisible(false);

_widgets.push_back(new StaticLocationWidget(
"VERSION INFO",
nullptr,
nullptr));

_widgets.push_back(new StaticLocationWidget(
"VERSION INFO REALLY",
nullptr,
nullptr));
_widgets.push_back(new VersionInfoText());
}

template<MainMenuScreen::HelpTextIndex N>
Expand Down Expand Up @@ -181,8 +172,23 @@ 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->notifyShouldExit();
}

} // End of namespace Stark
VersionInfoText::VersionInfoText() :
StaticLocationWidget("VERSION INFO REALLY", nullptr, nullptr) {
Common::String text = _copyrightSymbol + Common::String("1999 Funcom");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we cannot access the build version of the original executable and we are not using the Audiere library, I chose to just display the Funcom text.

_renderEntry->getText()->setText(text);
_renderEntry->setPosition(Common::Point(_posX, _posY));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default positions of the "VERSION INFO HERE" widget in the released game and the demo are different, and both of them needs to be moved, so I chose to hard-code it.

}

} // End of namespace Stark
14 changes: 14 additions & 0 deletions engines/stark/ui/menu/mainmenu.h
Expand Up @@ -55,9 +55,23 @@ class MainMenuScreen : public StaticLocationScreen {
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
2 changes: 2 additions & 0 deletions engines/stark/ui/world/gamescreen.cpp
Expand Up @@ -122,8 +122,10 @@ void GameScreen::dispatchEvent(WindowHandler handler) {
}

void GameScreen::onScreenChanged() {
_cursor->onScreenChanged();
_dialogPanel->onScreenChanged();
_topMenu->onScreenChanged();
_gameWindow->onScreenChanged();
}

void GameScreen::notifyInventoryItemEnabled(uint16 itemIndex) {
Expand Down
24 changes: 24 additions & 0 deletions engines/stark/ui/world/gamewindow.cpp
Expand Up @@ -30,6 +30,7 @@
#include "engines/stark/resources/knowledgeset.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"
Expand All @@ -41,6 +42,8 @@
#include "engines/stark/ui/world/actionmenu.h"
#include "engines/stark/ui/world/inventorywindow.h"

#include "engines/stark/visual/text.h"

namespace Stark {

GameWindow::GameWindow(Gfx::Driver *gfx, Cursor *cursor, ActionMenu *actionMenu, InventoryWindow *inventory) :
Expand Down Expand Up @@ -238,4 +241,25 @@ void GameWindow::reset() {
_objectRelativePosition.y = 0;
}

void GameWindow::onScreenChanged() {
// May be called when resources have not been loaded
if (!StarkGlobal->getCurrent()) {
return;
}

Common::Array<Resources::Layer *> layers = StarkGlobal->getCurrent()->getLocation()->listLayers();

VisualText *text = nullptr;
Gfx::RenderEntryArray renderEntries;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reduce the scope of these variables. That'd makes the code a bit easier to read, IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean putting the declaration into the loop? I am not sure how compilers can optimize this these days but isn't that may potentially cause unnecessary variable creations and deallocations, especially since we have an array here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ compilers are very good with these little details. For non performance critical code it's better to focus on writing simple and readable code.

In this particular case, constructing RenderEntryArray outside of the loop may prevent Return Value Optimization.

for (uint i = 0; i < layers.size(); ++i) {
renderEntries = layers[i]->listRenderEntries();
for (uint j = 0; j < renderEntries.size(); ++j) {
text = renderEntries[j]->getText();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Texts in not only the current layer but all the layers within the same location need to reset their textures when the resolution has changed, otherwise, the user may change the screen, go back to a previous layer and find size-unmatched texts.

if (text) {
text->resetTexture();
}
}
}
}

} // End of namespace Stark
3 changes: 3 additions & 0 deletions engines/stark/ui/world/gamewindow.h
Expand Up @@ -45,6 +45,9 @@ class GameWindow : public Window {
/** Clear the location dependent state */
void reset();

/** Update when the screen resolution has changed */
void onScreenChanged();

protected:
void onMouseMove(const Common::Point &pos) override;
void onClick(const Common::Point &pos) override;
Expand Down
2 changes: 1 addition & 1 deletion engines/stark/ui/world/topmenu.cpp
Expand Up @@ -133,7 +133,7 @@ void TopMenu::onClick(const Common::Point &pos) {

if (_exitButton->containsPoint(pos)) {
// TODO: Confirmation dialog
StarkUserInterface->quitToMainMenu();
StarkUserInterface->requestQuitToMainMenu();
}

if (_inventoryButton->containsPoint(pos)) {
Expand Down