[Scummvm-git-logs] scummvm master -> 6d0d9a570407287a9f1d85559d75c4f755488113
Helco
noreply at scummvm.org
Wed May 27 17:12:12 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
6d0d9a5704 ALCACHOFA: Implement V2 menu variants
Commit: 6d0d9a570407287a9f1d85559d75c4f755488113
https://github.com/scummvm/scummvm/commit/6d0d9a570407287a9f1d85559d75c4f755488113
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-05-27T19:11:48+02:00
Commit Message:
ALCACHOFA: Implement V2 menu variants
Changed paths:
engines/alcachofa/alcachofa.cpp
engines/alcachofa/alcachofa.h
engines/alcachofa/graphics.cpp
engines/alcachofa/menu.cpp
engines/alcachofa/menu.h
engines/alcachofa/objects.h
engines/alcachofa/player.cpp
engines/alcachofa/rooms.cpp
engines/alcachofa/rooms.h
engines/alcachofa/ui-objects.cpp
diff --git a/engines/alcachofa/alcachofa.cpp b/engines/alcachofa/alcachofa.cpp
index e3b79fada02..0fb648f95c7 100644
--- a/engines/alcachofa/alcachofa.cpp
+++ b/engines/alcachofa/alcachofa.cpp
@@ -89,7 +89,7 @@ Common::Error AlcachofaEngine::run() {
_script.reset(new Script());
_player.reset(new Player());
_globalUI.reset(isV1() || isV2() ? static_cast<GlobalUI *>(new GlobalUIV1()) : new GlobalUIV3());
- _menu.reset(isV1() ? static_cast<Menu *>(new MenuV1()) : new MenuV3());
+ _menu.reset(Menu::create());
setMillis(0);
game().onLoadedGameFiles();
@@ -454,11 +454,13 @@ void Config::registerDefaults() {
ConfMan.registerDefault("music_volume", c._musicVolume);
ConfMan.registerDefault("speech_volume", c._speechVolume);
ConfMan.registerDefault("sfx_volume", c._speechVolume);
+ ConfMan.registerDefault("cursor", c._cursor);
}
void Config::loadFromScummVM() {
_musicVolume = (uint8)CLIP(ConfMan.getInt("music_volume"), 0, 255);
_speechVolume = (uint8)CLIP(ConfMan.getInt("speech_volume"), 0, 255);
+ _cursor = (uint8)CLIP(ConfMan.getInt("cursor"), 0, (int)kMaxCursor);
_subtitles = ConfMan.getBool("subtitles");
_highQuality = ConfMan.getBool("high_quality");
_bits32 = ConfMan.getBool("32_bits");
@@ -473,6 +475,7 @@ void Config::saveToScummVM() {
ConfMan.setInt("music_volume", _musicVolume);
ConfMan.setInt("speech_volume", _speechVolume);
ConfMan.setInt("sfx_volume", _speechVolume);
+ ConfMan.setInt("cursor", _cursor);
ConfMan.flushToDisk();
// ^ a bit unfortunate, that means if you change in-game it overrides.
// if you set it in ScummVMs dialog it sticks
diff --git a/engines/alcachofa/alcachofa.h b/engines/alcachofa/alcachofa.h
index 91790b776e3..96008e8fc69 100644
--- a/engines/alcachofa/alcachofa.h
+++ b/engines/alcachofa/alcachofa.h
@@ -81,12 +81,15 @@ public:
class Config {
public:
+ static constexpr const uint8 kMaxCursor = 3;
+
inline bool &subtitles() { return _subtitles; }
inline bool &highQuality() { return _highQuality; }
inline bool &bits32() { return _bits32; }
inline bool &texFilter() { return _texFilter; }
inline uint8 &musicVolume() { return _musicVolume; }
inline uint8 &speechVolume() { return _speechVolume; }
+ inline uint8 &cursor() { return _cursor; } // only used in V2
static void registerDefaults();
void loadFromScummVM();
@@ -100,7 +103,8 @@ private:
_texFilter = true;
uint8
_musicVolume = 255,
- _speechVolume = 255;
+ _speechVolume = 255,
+ _cursor = 0;
};
class AlcachofaEngine : public Engine {
diff --git a/engines/alcachofa/graphics.cpp b/engines/alcachofa/graphics.cpp
index d01d4ea91a9..913de0b1d40 100644
--- a/engines/alcachofa/graphics.cpp
+++ b/engines/alcachofa/graphics.cpp
@@ -616,8 +616,8 @@ void Animation::prerenderFrame(int32 frameI) {
struct TexCoords {
TexCoords(const Rect &inner, int16 outerW, int16 outerH) {
- _min = Vector2d(0.5f / outerW, 0.5f / outerH);
- _max = Vector2d((inner.width() - 0.5f) / outerW, (inner.height() - 0.5f) / outerH);
+ _min = Vector2d(0.0f, 0.0f);
+ _max = Vector2d((inner.width()) / (float)outerW, (inner.height()) / (float)outerH);
}
Vector2d _min, _max;
diff --git a/engines/alcachofa/menu.cpp b/engines/alcachofa/menu.cpp
index a211ffdad47..cc000fe77cf 100644
--- a/engines/alcachofa/menu.cpp
+++ b/engines/alcachofa/menu.cpp
@@ -63,6 +63,17 @@ static void convertToGrayscale(ManagedSurface &surface) {
}
}
+Menu *Menu::create() {
+ if (g_engine->isV1())
+ return new MenuV1();
+ else if (g_engine->isV2())
+ return new MenuV2();
+ else if (g_engine->isV3())
+ return new MenuV3();
+ else
+ error("Menu is not implemented for this engine version");
+}
+
Menu::Menu()
: _interactionSemaphore("menu")
, _saveFileMgr(g_system->getSavefileManager()) {}
@@ -110,6 +121,13 @@ void MenuV1::updateOpeningMenu() {
switchToState(MainMenuAction::ConfirmSavestate);
}
+void MenuV2::updateOpeningMenu() {
+ bool willOpen = _openAtNextFrame;
+ Menu::updateOpeningMenu();
+ if (willOpen)
+ toggleMessageBox(false);
+}
+
static int parseSavestateSlot(const String &filename) {
if (filename.size() < 5) // minimal name would be "t.###"
return 1;
@@ -150,18 +168,28 @@ void MenuV1::updateSelectedSavefile(bool hasJustSaved) {
captureObject->toggle(isInCorrectState && !isOnNewSlot());
}
+void MenuV2::updateSelectedSavefile(bool hasJustSaved) {
+ Menu::updateSelectedSavefile(hasJustSaved);
+
+ auto getButton = [ ] (const char *name) -> MenuButton &{
+ return g_engine->player().currentRoom()->getRequiredObjectByName<MenuButton>(name);
+ };
+
+ getButton("CARGAR").isInteractable() = !isOnNewSlot();
+ getButton("ANTERIOR").toggle(_selectedSavefileI > 0);
+ getButton("SIGUIENTE").toggle(!isOnNewSlot());
+}
+
void MenuV3::updateSelectedSavefile(bool hasJustSaved) {
Menu::updateSelectedSavefile(hasJustSaved);
- auto getButton = [ ] (const char *name) {
- MenuButton *button = dynamic_cast<MenuButton *>(g_engine->player().currentRoom()->getObjectByName(name));
- scumm_assert(button != nullptr);
- return button;
+ auto getButton = [ ] (const char *name) -> MenuButton& {
+ return g_engine->player().currentRoom()->getRequiredObjectByName<MenuButton>(name);
};
- getButton("CARGAR")->isInteractable() = !isOnNewSlot();
- getButton("ANTERIOR")->toggle(_selectedSavefileI > 0);
- getButton("SIGUIENTE")->toggle(!isOnNewSlot());
+ getButton("CARGAR").isInteractable() = !isOnNewSlot();
+ getButton("ANTERIOR").toggle(_selectedSavefileI > 0);
+ getButton("SIGUIENTE").toggle(!isOnNewSlot());
}
bool Menu::tryReadOldSavefile() {
@@ -225,8 +253,10 @@ void Menu::triggerMainMenuAction(MainMenuAction action) {
g_engine->fadeExit();
break;
case MainMenuAction::NewGame:
- // this action might be unused just like the only room it would appear: MENUPRINCIPALINICIO
- g_engine->script().createProcess(MainCharacterKind::None, g_engine->world().initScriptName());
+ // this action is unused just like the only room it would appear: MENUPRINCIPALINICIO
+ // it also breaks the engine in very funny ways so let's not do anything instead
+ // g_engine->script().createProcess(MainCharacterKind::None, g_engine->world().initScriptName());
+ warning("MainMenuAction::NewGame triggered!");
break;
default:
g_engine->game().unknownMenuAction((int32)action);
@@ -276,6 +306,35 @@ void MenuV1::triggerMainMenuAction(MainMenuAction action) {
}
}
+void MenuV2::triggerMainMenuAction(MainMenuAction action) {
+ switch (action) {
+ case MainMenuAction::NextSave:
+ if (_selectedSavefileI < _savefiles.size()) {
+ _selectedSavefileI++;
+ updateSelectedSavefile(false);
+ }
+ break;
+ case MainMenuAction::PrevSave:
+ if (_selectedSavefileI > 0) {
+ _selectedSavefileI--;
+ updateSelectedSavefile(false);
+ }
+ break;
+ case MainMenuAction::Exit:
+ toggleMessageBox(true);
+ break;
+ case MainMenuAction::Cancel:
+ toggleMessageBox(false);
+ break;
+ case MainMenuAction::Accept:
+ Menu::triggerMainMenuAction(MainMenuAction::Exit);
+ break;
+ default:
+ Menu::triggerMainMenuAction(action);
+ break;
+ }
+}
+
void MenuV3::triggerMainMenuAction(MainMenuAction action) {
switch (action) {
case MainMenuAction::NextSave:
@@ -337,49 +396,59 @@ void Menu::triggerSave() {
}
void Menu::openOptionsMenu() {
+ _currentSlideButton = nullptr;
setOptionsState();
g_engine->player().changeRoom("MENUOPCIONES", true);
}
void MenuV1::setOptionsState() {}
-void MenuV3::setOptionsState() {
+void MenuV2::setOptionsState() {
Config &config = g_engine->config();
Room *optionsMenu = g_engine->world().getRoomByName("MENUOPCIONES");
scumm_assert(optionsMenu != nullptr);
+ auto getSlideButton = [&] (const char *name) -> SlideButton& {
+ return optionsMenu->getRequiredObjectByName<SlideButton>(name);
+ };
+ auto getPushButton = [&] (const char *name) -> PushButton& {
+ return optionsMenu->getRequiredObjectByName<PushButton>(name);
+ };
- auto getSlideButton = [&] (const char *name) {
- SlideButton *slideButton = dynamic_cast<SlideButton *>(optionsMenu->getObjectByName(name));
- scumm_assert(slideButton != nullptr);
- return slideButton;
+ // there is a mouse sensitivity slider, this does not exist in ScummVM, so we ignore it
+ getSlideButton("VOLUMENCD").value() = config.musicVolume() / 255.0f;
+ getSlideButton("VOLUMENAUDIO").value() = config.speechVolume() / 255.0f;
+
+ getPushButton("CURSOR0").isChecked() = config.cursor() == 0;
+ getPushButton("CURSOR1").isChecked() = config.cursor() == 1;
+ getPushButton("CURSOR2").isChecked() = config.cursor() == 2;
+ getPushButton("CURSOR3").isChecked() = config.cursor() == 3;
+ getPushButton("TEXTOSON").isChecked() = config.subtitles();
+ getPushButton("TEXTOSOFF").isChecked() = !config.subtitles();
+}
+
+void MenuV3::setOptionsState() {
+ Config &config = g_engine->config();
+ Room *optionsMenu = g_engine->world().getRoomByName("MENUOPCIONES");
+ scumm_assert(optionsMenu != nullptr);
+ auto getSlideButton = [&] (const char *name) -> SlideButton& {
+ return optionsMenu->getRequiredObjectByName<SlideButton>(name);
};
- SlideButton
- *slideMusicVolume = getSlideButton("Slider Musica"),
- *slideSpeechVolume = getSlideButton("Slider Sonido");
- slideMusicVolume->value() = config.musicVolume() / 255.0f;
- slideSpeechVolume->value() = config.speechVolume() / 255.0f;
+ auto getCheckBox = [&] (const char *name) -> CheckBox& {
+ return optionsMenu->getRequiredObjectByName<CheckBox>(name);
+ };
+
+ getSlideButton("Slider Musica").value() = config.musicVolume() / 255.0f;
+ getSlideButton("Slider Sonido").value() = config.speechVolume() / 255.0f;
if (!config.bits32())
config.highQuality() = false;
- auto getCheckBox = [&] (const char *name) {
- CheckBox *checkBox = dynamic_cast<CheckBox *>(optionsMenu->getObjectByName(name));
- scumm_assert(checkBox != nullptr);
- return checkBox;
- };
- CheckBox
- *checkSubtitlesOn = getCheckBox("Boton ON"),
- *checkSubtitlesOff = getCheckBox("Boton OFF"),
- *check32Bits = getCheckBox("Boton 32 Bits"),
- *check16Bits = getCheckBox("Boton 16 Bits"),
- *checkHighQuality = getCheckBox("Boton Alta"),
- *checkLowQuality = getCheckBox("Boton Baja");
- checkSubtitlesOn->isChecked() = config.subtitles();
- checkSubtitlesOff->isChecked() = !config.subtitles();
- check32Bits->isChecked() = config.bits32();
- check16Bits->isChecked() = !config.bits32();
- checkHighQuality->isChecked() = config.highQuality();
- checkLowQuality->isChecked() = !config.highQuality();
- checkHighQuality->toggle(config.bits32());
+ getCheckBox("Boton ON").isChecked() = config.subtitles();
+ getCheckBox("Boton OFF").isChecked() = !config.subtitles();
+ getCheckBox("Boton 32 Bits").isChecked() = config.bits32();
+ getCheckBox("Boton 16 Bits").isChecked() = !config.bits32();
+ getCheckBox("Boton Alta").isChecked() = config.highQuality();
+ getCheckBox("Boton Baja").isChecked() = !config.highQuality();
+ getCheckBox("Boton Alta").toggle(config.bits32());
}
void Menu::triggerOptionsAction(OptionsMenuAction action) {
@@ -404,6 +473,18 @@ void Menu::triggerOptionsAction(OptionsMenuAction action) {
case OptionsMenuAction::Bits16:
config.bits32() = false;
break;
+ case OptionsMenuAction::Cursor0:
+ config.cursor() = 0;
+ break;
+ case OptionsMenuAction::Cursor1:
+ config.cursor() = 1;
+ break;
+ case OptionsMenuAction::Cursor2:
+ config.cursor() = 2;
+ break;
+ case OptionsMenuAction::Cursor3:
+ config.cursor() = 3;
+ break;
case OptionsMenuAction::MainMenu:
continueMainMenu();
break;
@@ -423,6 +504,8 @@ void Menu::triggerOptionsValue(OptionsMenuValue valueId, float value) {
case OptionsMenuValue::Speech:
config.speechVolume() = CLIP<uint8>((uint8)(value * 255), 0, 255);
break;
+ case OptionsMenuValue::Sensitivity:
+ break;
default:
warning("Unknown options menu value: %d", (int32)valueId);
break;
@@ -456,4 +539,22 @@ void MenuV1::switchToState(MainMenuAction state) {
}
}
+void MenuV2::toggleMessageBox(bool show) {
+ auto getButton = [&] (const char *name) -> MenuButton& {
+ return g_engine->player().currentRoom()->getRequiredObjectByName<MenuButton>(name);
+ };
+
+ getButton("MBACEPTAR").toggle(show);
+ getButton("MBCANCELAR").toggle(show);
+
+ getButton("ANTERIOR").toggle(!show);
+ getButton("SIGUIENTE").toggle(!show);
+ getButton("CARGAR").toggle(!show);
+ getButton("GRABAR").toggle(!show);
+ getButton("INTERNET").toggle(!show);
+ getButton("OPCIONES").toggle(!show);
+ getButton("JUGAR").toggle(!show);
+ getButton("SALIR").toggle(!show);
+}
+
}
diff --git a/engines/alcachofa/menu.h b/engines/alcachofa/menu.h
index 1194cb42f33..1fa1e395cec 100644
--- a/engines/alcachofa/menu.h
+++ b/engines/alcachofa/menu.h
@@ -27,6 +27,10 @@
namespace Alcachofa {
class Room;
+class SlideButton;
+
+// the order of these enums are compatible to V3 the first to be developed
+// any other version needs some translation
enum class MainMenuAction : int32 {
ContinueGame = 0,
@@ -37,10 +41,12 @@ enum class MainMenuAction : int32 {
Exit,
NextSave,
PrevSave,
- NewGame,
+ NewGame, // unused in any game
AlsoExit, // there seems to be no difference to Exit
ConfirmSavestate, // only used in V1
+ Accept, // only used in V2
+ Cancel, // only used in V2
};
enum class OptionsMenuAction : int32 {
@@ -50,16 +56,23 @@ enum class OptionsMenuAction : int32 {
LowQuality,
Bits32,
Bits16,
- MainMenu
+ MainMenu,
+
+ Cursor0, // only used in V2
+ Cursor1,
+ Cursor2,
+ Cursor3,
};
enum class OptionsMenuValue : int32 {
Music = 0,
- Speech = 1
+ Speech = 1,
+ Sensitivity = 2 // only used in V2 and no effect for ScummVM
};
class Menu {
public:
+ static Menu *create();
Menu();
virtual ~Menu();
@@ -67,6 +80,7 @@ public:
inline uint32 millisBeforeMenu() const { return _millisBeforeMenu; }
inline Room *previousRoom() { return _previousRoom; }
inline FakeSemaphore &interactionSemaphore() { return _interactionSemaphore; }
+ inline SlideButton *¤tSlideButton() { return _currentSlideButton; }
void triggerLoad();
void resetAfterLoad();
@@ -100,6 +114,7 @@ protected:
_selectedSavefileI = 0;
Room *_previousRoom = nullptr;
FakeSemaphore _interactionSemaphore; // to prevent ScummVM loading during button clicks
+ SlideButton *_currentSlideButton = nullptr; // due to V2 we cannot store this in OptionsMenu
Common::String _selectedSavefileDescription = "<unset>";
Common::Array<Common::String> _savefiles;
Graphics::ManagedSurface
@@ -117,6 +132,17 @@ protected:
void setOptionsState() override;
};
+class MenuV2 : public Menu {
+public:
+ void updateOpeningMenu() override;
+ void triggerMainMenuAction(MainMenuAction action) override;
+
+protected:
+ void updateSelectedSavefile(bool hasJustSaved) override;
+ void setOptionsState() override;
+ void toggleMessageBox(bool show);
+};
+
class MenuV1 : public Menu {
public:
void updateOpeningMenu() override;
diff --git a/engines/alcachofa/objects.h b/engines/alcachofa/objects.h
index 04967481b23..33f4dccb95d 100644
--- a/engines/alcachofa/objects.h
+++ b/engines/alcachofa/objects.h
@@ -224,7 +224,7 @@ public:
const char *typeName() const override;
};
-class OptionsMenuButton final : public MenuButton {
+class OptionsMenuButton : public MenuButton {
public:
static constexpr const char *kClassName = "CBotonMenuOpciones";
OptionsMenuButton(Room *room, Common::SeekableReadStream &stream);
@@ -234,7 +234,15 @@ public:
const char *typeName() const override;
};
-class MainMenuButton final : public MenuButton {
+class OptionsMenuButtonV2 : public OptionsMenuButton {
+public:
+ OptionsMenuButtonV2(Room *room, Common::SeekableReadStream &stream);
+
+ void update() override;
+ void trigger() override;
+};
+
+class MainMenuButton : public MenuButton {
public:
static constexpr const char *kClassName = "CBotonMenuPrincipal";
MainMenuButton(Room *room, Common::SeekableReadStream &stream);
@@ -242,19 +250,40 @@ public:
void update() override;
void trigger() override;
const char *typeName() const override;
+protected:
+ virtual MainMenuAction action() const;
+};
+
+class MainMenuButtonV2 final : public MainMenuButton {
+public:
+ MainMenuButtonV2(Room *room, Common::SeekableReadStream &stream);
+
+protected:
+ MainMenuAction action() const override;
};
-class PushButton final : public PhysicalObject {
+// this is a variant of a CheckBox only used in V2
+class PushButton : public PhysicalObject {
public:
static constexpr const char *kClassName = "CPushButton";
PushButton(Room *room, Common::SeekableReadStream &stream);
+ inline bool &isChecked() { return _isChecked; }
+
+ void draw() override;
+ void update() override;
+ void loadResources() override;
+ void freeResources() override;
+ void onHoverUpdate() override;
+ void onClick() override;
+ virtual void trigger();
const char *typeName() const override;
private:
- bool _alwaysVisible;
- Graphic _graphic1, _graphic2;
- int32 _actionId;
+ bool _isChecked = false;
+ Graphic _graphicHovered, _graphicChecked;
+ int32 _actionId = -1;
+ uint32 _clickTime = 0;
};
class EditBox : public PhysicalObject {
diff --git a/engines/alcachofa/player.cpp b/engines/alcachofa/player.cpp
index 00017417cfc..0c77c33c2fd 100644
--- a/engines/alcachofa/player.cpp
+++ b/engines/alcachofa/player.cpp
@@ -58,8 +58,9 @@ void Player::resetCursor() {
}
void Player::updateCursor() {
- // TODO: V2 has additional cursor frames. How are they used?
- if (g_engine->isV1() || g_engine->isV2() || g_engine->menu().isOpen())
+ if (g_engine->isV2())
+ _cursorFrameI = g_engine->config().cursor();
+ else if (g_engine->isV1() || g_engine->menu().isOpen())
_cursorFrameI = 0;
else if (_selectedObject == nullptr)
_cursorFrameI = !g_engine->input().isMouseLeftDown() || _pressedObject != nullptr ? 6 : 7;
diff --git a/engines/alcachofa/rooms.cpp b/engines/alcachofa/rooms.cpp
index 9781d35647c..33bdd3392f7 100644
--- a/engines/alcachofa/rooms.cpp
+++ b/engines/alcachofa/rooms.cpp
@@ -60,12 +60,20 @@ static ObjectBase *readRoomObject(Room *room, const String &type, SeekableReadSt
return new Item(room, stream);
else if (type == PhysicalObject::kClassName)
return new PhysicalObject(room, stream);
- else if (type == MainMenuButton::kClassName)
- return new MainMenuButton(room, stream);
+ else if (type == MainMenuButton::kClassName) {
+ if (g_engine->isV2())
+ return new MainMenuButtonV2(room, stream);
+ else
+ return new MainMenuButton(room, stream);
+ }
else if (type == InternetMenuButton::kClassName)
return new InternetMenuButton(room, stream);
- else if (type == OptionsMenuButton::kClassName)
- return new OptionsMenuButton(room, stream);
+ else if (type == OptionsMenuButton::kClassName) {
+ if (g_engine->isV2())
+ return new OptionsMenuButtonV2(room, stream);
+ else
+ return new OptionsMenuButton(room, stream);
+ }
else if (type == EditBox::kClassName) {
if (g_engine->isV2())
return new EditBoxV2(room, stream);
@@ -396,7 +404,6 @@ bool OptionsMenu::updateInput() {
void OptionsMenu::loadResources() {
Room::loadResources();
_lastSelectedObject = nullptr;
- _currentSlideButton = nullptr;
_idleArm = getObjectByName("Brazo");
}
diff --git a/engines/alcachofa/rooms.h b/engines/alcachofa/rooms.h
index 5d6235c7c82..b8cd56c0b48 100644
--- a/engines/alcachofa/rooms.h
+++ b/engines/alcachofa/rooms.h
@@ -55,6 +55,13 @@ public:
inline ObjectIterator beginObjects() const { return _objects.begin(); }
inline ObjectIterator endObjects() const { return _objects.end(); }
+ template<class TObjectType>
+ TObjectType &getRequiredObjectByName(const char *name) const {
+ TObjectType *obj = dynamic_cast<TObjectType *>(getObjectByName(name));
+ scumm_assert(obj != nullptr);
+ return *obj;
+ }
+
void update();
void draw();
virtual bool updateInput();
@@ -111,12 +118,10 @@ public:
void loadResources() override;
void clearLastSelectedObject(); // to reset arm animation
- inline SlideButton *¤tSlideButton() { return _currentSlideButton; }
private:
ShapeObject *_lastSelectedObject = nullptr;
ObjectBase *_idleArm = nullptr;
- SlideButton *_currentSlideButton = nullptr;
};
class ConnectMenu final : public Room {
diff --git a/engines/alcachofa/ui-objects.cpp b/engines/alcachofa/ui-objects.cpp
index c30c9a40007..67354c72653 100644
--- a/engines/alcachofa/ui-objects.cpp
+++ b/engines/alcachofa/ui-objects.cpp
@@ -192,6 +192,11 @@ void MenuButton::draw() {
: _isClicked ? _graphicClicked
: wasSelected() ? _graphicHovered
: _graphicNormal;
+
+ // In V2 the normal graphic has no animation and is just baked into the background
+ if (!graphic.hasAnimation())
+ return;
+
graphic.update();
g_engine->drawQueue().add<AnimationDrawRequest>(graphic, true, BlendMode::AdditiveAlpha);
}
@@ -268,6 +273,20 @@ void OptionsMenuButton::trigger() {
g_engine->menu().triggerOptionsAction((OptionsMenuAction)actionId());
}
+OptionsMenuButtonV2::OptionsMenuButtonV2(Room *room, SeekableReadStream &stream)
+ : OptionsMenuButton(room, stream) {}
+
+void OptionsMenuButtonV2::update() {
+ MenuButton::update();
+ if (g_engine->input().wasMenuKeyPressed())
+ onClick();
+}
+
+void OptionsMenuButtonV2::trigger() {
+ // there is only one button, but its ID is the same as SubtitlesOn, so we override it here
+ g_engine->menu().triggerOptionsAction(OptionsMenuAction::MainMenu);
+}
+
const char *MainMenuButton::typeName() const { return "MainMenuButton"; }
MainMenuButton::MainMenuButton(Room *room, SeekableReadStream &stream)
@@ -275,25 +294,117 @@ MainMenuButton::MainMenuButton(Room *room, SeekableReadStream &stream)
void MainMenuButton::update() {
MenuButton::update();
- const auto action = (MainMenuAction)actionId();
+ const auto action = this->action();
if (g_engine->input().wasMenuKeyPressed() &&
(action == MainMenuAction::ContinueGame || action == MainMenuAction::NewGame))
onClick();
}
void MainMenuButton::trigger() {
- g_engine->menu().triggerMainMenuAction((MainMenuAction)actionId());
+ g_engine->menu().triggerMainMenuAction(action());
+}
+
+MainMenuAction MainMenuButton::action() const {
+ return (MainMenuAction)actionId();
+}
+
+MainMenuButtonV2::MainMenuButtonV2(Room *room, SeekableReadStream &stream)
+ : MainMenuButton(room, stream) {}
+
+MainMenuAction MainMenuButtonV2::action() const {
+ if (actionId() <= 7)
+ return (MainMenuAction)actionId();
+ else if (actionId() == 8)
+ return MainMenuAction::Accept;
+ else if (actionId() == 9)
+ return MainMenuAction::Cancel;
+ else {
+ warning("Unknown V2 main menu button: %d", (int)actionId());
+ return MainMenuAction::ContinueGame;
+ }
}
const char *PushButton::typeName() const { return "PushButton"; }
PushButton::PushButton(Room *room, SeekableReadStream &stream)
: PhysicalObject(room, stream)
- , _alwaysVisible(readBool(stream))
- , _graphic1(stream)
- , _graphic2(stream)
+ , _isChecked(readBool(stream))
+ , _graphicHovered(stream)
+ , _graphicChecked(stream)
, _actionId(stream.readSint32LE()) {}
+void PushButton::draw() {
+ if (!isEnabled())
+ return;
+
+ Graphic *graphic = nullptr;
+ if (_isChecked)
+ graphic = &_graphicChecked;
+ else if (wasSelected() && _graphicHovered.hasAnimation())
+ graphic = &_graphicHovered;
+
+ if (graphic != nullptr) {
+ graphic->update();
+ g_engine->drawQueue().add<AnimationDrawRequest>(*graphic, true, BlendMode::AdditiveAlpha);
+ }
+}
+
+void PushButton::update() {
+ PhysicalObject::update();
+ if (_clickTime != 0) {
+ if (g_engine->getMillis() - _clickTime > 5) {
+ _clickTime = 0;
+ trigger();
+ }
+ }
+}
+
+void PushButton::loadResources() {
+ _clickTime = 0;
+ _graphicChecked.loadResources();
+ _graphicHovered.loadResources();
+}
+
+void PushButton::freeResources() {
+ _graphicChecked.freeResources();
+ _graphicHovered.freeResources();
+}
+
+void PushButton::onHoverUpdate() {}
+
+void PushButton::onClick() {
+ _clickTime = g_engine->getMillis();
+}
+
+void PushButton::trigger() {
+ OptionsMenuAction action;
+ // these are V2 actions as CPushButton is only used in V2
+ switch (_actionId) {
+ case 0:
+ action = OptionsMenuAction::SubtitlesOn;
+ break;
+ case 1:
+ action = OptionsMenuAction::SubtitlesOff;
+ break;
+ case 2:
+ action = OptionsMenuAction::Cursor0;
+ break;
+ case 3:
+ action = OptionsMenuAction::Cursor1;
+ break;
+ case 4:
+ action = OptionsMenuAction::Cursor2;
+ break;
+ case 5:
+ action = OptionsMenuAction::Cursor3;
+ break;
+ default:
+ warning("Unknown push button action: %d", (int)_actionId);
+ return;
+ }
+ g_engine->menu().triggerOptionsAction(action);
+}
+
const char *EditBox::typeName() const { return "EditBox"; }
EditBox::EditBox(Room *room, SeekableReadStream &stream)
@@ -410,7 +521,6 @@ SlideButtonV2::SlideButtonV2(Room *room, SeekableReadStream &stream)
_minPos = Shape(stream).firstPoint();
_maxPos = Shape(stream).firstPoint();
_graphicIdle = Graphic(stream);
- _graphicHovered = _graphicIdle;
_graphicClicked = Graphic(stream);
}
@@ -425,14 +535,11 @@ SlideButtonV3::SlideButtonV3(Room *room, SeekableReadStream &stream)
}
void SlideButton::draw() {
- auto *optionsMenu = dynamic_cast<OptionsMenu *>(room());
- scumm_assert(optionsMenu != nullptr);
-
Graphic *activeGraphic;
- if (optionsMenu->currentSlideButton() == this && g_engine->input().isMouseLeftDown())
+ if (g_engine->menu().currentSlideButton() == this && g_engine->input().isMouseLeftDown())
activeGraphic = &_graphicClicked;
else
- activeGraphic = isMouseOver() ? &_graphicHovered : &_graphicIdle;
+ activeGraphic = isMouseOver() && _graphicHovered.hasAnimation() ? &_graphicHovered : &_graphicIdle;
activeGraphic->update();
g_engine->drawQueue().add<AnimationDrawRequest>(*activeGraphic, true, BlendMode::AdditiveAlpha);
}
@@ -440,11 +547,11 @@ void SlideButton::draw() {
void SlideButton::update() {
const auto mousePos = g_engine->input().mousePos2D();
auto *optionsMenu = dynamic_cast<OptionsMenu *>(room());
- scumm_assert(optionsMenu != nullptr);
+ SlideButton *¤tSlideButton = g_engine->menu().currentSlideButton();
- if (optionsMenu->currentSlideButton() == this) {
+ if (currentSlideButton == this) {
if (!g_engine->input().isMouseLeftDown()) {
- optionsMenu->currentSlideButton() = nullptr;
+ currentSlideButton = nullptr;
g_engine->menu().triggerOptionsValue((OptionsMenuValue)_valueId, _value);
update(); // to update the position
} else {
@@ -460,8 +567,9 @@ void SlideButton::update() {
return;
_graphicHovered.topLeft() = _graphicIdle.topLeft();
if (g_engine->input().wasMouseLeftPressed())
- optionsMenu->currentSlideButton() = this;
- optionsMenu->clearLastSelectedObject();
+ currentSlideButton = this;
+ if (optionsMenu != nullptr) // in V2 this is a normal room not a OptionsMenu
+ optionsMenu->clearLastSelectedObject();
g_engine->player().selectedObject() = nullptr;
}
}
More information about the Scummvm-git-logs
mailing list