[Scummvm-git-logs] scummvm master -> e9f7121d7a764c19207b647a09f1d653bb827475

mduggan noreply at scummvm.org
Fri Jul 26 11:14:25 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
e9f7121d7a DGDS: Implement HoC shell game


Commit: e9f7121d7a764c19207b647a09f1d653bb827475
    https://github.com/scummvm/scummvm/commit/e9f7121d7a764c19207b647a09f1d653bb827475
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2024-07-26T21:12:08+10:00

Commit Message:
DGDS: Implement HoC shell game

Changed paths:
  A engines/dgds/shell_game.cpp
  A engines/dgds/shell_game.h
    engines/dgds/ads.cpp
    engines/dgds/clock.cpp
    engines/dgds/dgds.cpp
    engines/dgds/dgds.h
    engines/dgds/dialog.cpp
    engines/dgds/globals.cpp
    engines/dgds/globals.h
    engines/dgds/image.h
    engines/dgds/inventory.cpp
    engines/dgds/menu.cpp
    engines/dgds/module.mk
    engines/dgds/request.cpp
    engines/dgds/scene.cpp
    engines/dgds/scene.h
    engines/dgds/ttm.cpp


diff --git a/engines/dgds/ads.cpp b/engines/dgds/ads.cpp
index 83ce852fcce..e8a8c817b03 100644
--- a/engines/dgds/ads.cpp
+++ b/engines/dgds/ads.cpp
@@ -379,11 +379,11 @@ bool ADSInterpreter::logicOpResult(uint16 code, const TTMEnviro *env, const TTMS
 		// FIXME: This should be right but we only have detail 0/1 and maybe HOC onward use
 		// different numbers?  HOC intro checks for >= 4.
 		return false;
-		//return ((int)static_cast<DgdsEngine *>(g_engine)->getDetailLevel() <= arg);
+		//return ((int)DgdsEngine::getInstance()->getDetailLevel() <= arg);
 	case 0x1390: // IF_DETAIL_GTE, 1 param
 		debugN(10, "ADS 0x%04x: if detail >= %d", code, arg);
 		return true;
-		//return ((int)static_cast<DgdsEngine *>(g_engine)->getDetailLevel() >= arg);
+		//return ((int)DgdsEngine::getInstance()->getDetailLevel() >= arg);
 	default:
 		error("Not an ADS logic op: %04x, how did we get here?", code);
 	}
diff --git a/engines/dgds/clock.cpp b/engines/dgds/clock.cpp
index eba993dde72..714543b4ce3 100644
--- a/engines/dgds/clock.cpp
+++ b/engines/dgds/clock.cpp
@@ -123,7 +123,7 @@ void Clock::draw(Graphics::ManagedSurface &surf) {
 
 	const Common::String clockStr = getTimeStr();
 
-	const FontManager *fontman = static_cast<DgdsEngine *>(g_engine)->getFontMan();
+	const FontManager *fontman = DgdsEngine::getInstance()->getFontMan();
 	const DgdsFont *font = fontman->getFont(FontManager::k4x5Font);
 	int width = font->getMaxCharWidth() * 12 + 3;
 	_drawPos.top = 0;
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index 175faec057e..f931d9a751b 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -79,7 +79,7 @@ const byte DgdsEngine::HOC_CHAR_SWAP_ICONS[] = { 0, 20, 21, 22 };
 
 DgdsEngine::DgdsEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	: Engine(syst), _fontManager(nullptr), _console(nullptr), _inventory(nullptr),
-	_soundPlayer(nullptr), _decompressor(nullptr), _scene(nullptr),
+	_soundPlayer(nullptr), _decompressor(nullptr), _scene(nullptr), _shellGame(nullptr),
 	_gdsScene(nullptr), _resource(nullptr), _gamePals(nullptr), _gameGlobals(nullptr),
 	_detailLevel(kDgdsDetailHigh), _textSpeed(1), _justChangedScene1(false), _justChangedScene2(false),
 	_random("dgds"), _currentCursor(-1), _menuToTrigger(kMenuNone), _isLoading(true),
@@ -123,6 +123,7 @@ DgdsEngine::~DgdsEngine() {
 	delete _fontManager;
 	delete _menu;
 	delete _inventory;
+	delete _shellGame;
 
 	_icons.reset();
 	_corners.reset();
@@ -325,6 +326,8 @@ void DgdsEngine::init(bool restarting) {
 	_menu = new Menu();
 	_adsInterp = new ADSInterpreter(this);
 	_inventory = new Inventory();
+	if (_gameId == GID_HOC)
+		_shellGame = new ShellGame();
 
 	_backgroundBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
 	_storedAreaBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
diff --git a/engines/dgds/dgds.h b/engines/dgds/dgds.h
index 28928d23489..6a8409813ed 100644
--- a/engines/dgds/dgds.h
+++ b/engines/dgds/dgds.h
@@ -58,6 +58,7 @@ class Menu;
 struct DgdsADS;
 class ADSInterpreter;
 class Globals;
+class ShellGame;
 
 const float MS_PER_FRAME = 16.6667f;
 
@@ -116,6 +117,9 @@ private:
 	Globals *_gameGlobals;
 	Inventory *_inventory;
 
+	// HoC only
+	ShellGame *_shellGame;
+
 	FontManager *_fontManager;
 	Common::SharedPtr<Image> _corners;
 	Common::SharedPtr<Image> _icons;
@@ -212,6 +216,9 @@ public:
 	const Common::String &getBackgroundFile() const { return _backgroundFile; }
 	void setMenuToTrigger(MenuId menu) { _menuToTrigger = menu; }
 	bool isInvButtonVisible() const;
+	ShellGame *getShellGame() { return _shellGame; }
+
+	static DgdsEngine *getInstance() { return static_cast<DgdsEngine *>(g_engine); }
 
 private:
 	Common::Error syncGame(Common::Serializer &s);
diff --git a/engines/dgds/dialog.cpp b/engines/dgds/dialog.cpp
index f85065039f5..d628ee7bb9a 100644
--- a/engines/dgds/dialog.cpp
+++ b/engines/dgds/dialog.cpp
@@ -85,7 +85,7 @@ static void _drawPixel(int x, int y, int color, void *data) {
 
 
 const DgdsFont *Dialog::getDlgTextFont() const {
-	const FontManager *fontman = static_cast<DgdsEngine *>(g_engine)->getFontMan();
+	const FontManager *fontman = DgdsEngine::getInstance()->getFontMan();
 	FontManager::FontType fontType = FontManager::kGameDlgFont;
 	if (_fontSize == 1)
 		fontType = FontManager::k8x8Font;
@@ -193,7 +193,7 @@ void Dialog::drawType2(Graphics::ManagedSurface *dst, DialogDrawStage stage) {
 
 	// Special case for HoC to update the Shekel count in their description.
 	// This is how the original game does it too.
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (_fileNum == 0x5d && _num == 0x32 && engine->getGameId() == GID_HOC) {
 		int16 shekels = engine->getGDSScene()->getGlobal(44);
 		const Common::String numstr = Common::String::format("%3d", shekels);
@@ -620,7 +620,7 @@ void Dialog::updateSelectedAction(int delta) {
 }
 
 struct DialogAction *Dialog::pickAction(bool isClosing, bool isForceClose) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (!isForceClose && isClosing) {
 		if (_action.empty())
 			return nullptr;
diff --git a/engines/dgds/globals.cpp b/engines/dgds/globals.cpp
index f67f32b8692..6b3d0164e46 100644
--- a/engines/dgds/globals.cpp
+++ b/engines/dgds/globals.cpp
@@ -37,7 +37,7 @@ public:
 	GameIsInteractiveGlobal(uint16 num, int16 *ptr) : Global(num), _ptr(ptr), _isSetOff(false) {}
 
 	int16 get() override {
-		SDSScene *scene = static_cast<DgdsEngine *>(g_engine)->getScene();
+		SDSScene *scene = DgdsEngine::getInstance()->getScene();
 		bool nonInteractive = _isSetOff || scene->getDragItem() || scene->hasVisibleOrOpeningDialog();
 		*_ptr = !nonInteractive;
 		return *_ptr;
@@ -102,11 +102,12 @@ int16 Globals::getGlobal(uint16 num) {
 		error("getGlobal: requested non-existing global %d", num);
 
 	// Bug in HoC?
-	warning("getGlobal: requested global 0");
+	//warning("getGlobal: requested global 0");
 	return 0;
 }
 
 int16 Globals::setGlobal(uint16 num, int16 val) {
+	debug("setGlobal %d -> %d", num, val);
 	for (auto &global : _globals) {
 		if (global->getNum() == num)
 			return global->set(val);
@@ -135,8 +136,8 @@ Common::Error Globals::syncState(Common::Serializer &s) {
 class DetailLevelROGlobal : public Global {
 public:
 	DetailLevelROGlobal(uint16 num) : Global(num) {}
-	int16 get() override { return static_cast<DgdsEngine *>(g_engine)->getDetailLevel(); }
-	int16 set(int16 val) override { return static_cast<DgdsEngine *>(g_engine)->getDetailLevel(); }
+	int16 get() override { return DgdsEngine::getInstance()->getDetailLevel(); }
+	int16 set(int16 val) override { return DgdsEngine::getInstance()->getDetailLevel(); }
 	void setRaw(int16 val) override { }
 };
 
@@ -219,7 +220,7 @@ class HocCharacterGlobal : public RWI16Global {
 public:
 	HocCharacterGlobal(uint16 num, int16 *val) : RWI16Global(num, val) {}
 	int16 set(int16 val) override {
-		DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+		DgdsEngine *engine = DgdsEngine::getInstance();
 		bool buttonVisible = engine->isInvButtonVisible();
 		if (buttonVisible)
 			engine->getScene()->removeInvButtonFromHotAreaList();
@@ -233,8 +234,8 @@ public:
 
 HocGlobals::HocGlobals(Clock &clock) : Globals(clock), _unk82(1), _unk55(0),
 	_unkDlgFileNum(0), _unkDlgDlgNum(0),  _currentCharacter2(0), _currentCharacter(0),
-	_unk50(0), _unk49(0), _unk48(0), _unk47(0), _unk46(0), _unk45(0x3f), _sheckels(0),
-	_unk43(0), _unk42(0), _unk41(0), _unk40(3), _unk39(0) {
+	_unk50(0), _nativeGameState(0), _unk48(0), _unk47(0), _unk46(0), _unk45(0x3f), _sheckels(0),
+	_shellBet(0), _shellPea(0), _unk41(0), _unk40(3), _unk39(0) {
 	_globals.push_back(new DetailLevelROGlobal(0x53));
 	_globals.push_back(new RWI16Global(0x52, &_unk82));
 	_globals.push_back(new RWI16Global(0x37, &_unk55)); // TODO: Special update function FUN_1407_080d, sound init related
@@ -243,14 +244,14 @@ HocGlobals::HocGlobals(Clock &clock) : Globals(clock), _unk82(1), _unk55(0),
 	_globals.push_back(new HocCharacterGlobal(0x34, &_currentCharacter));
 	_globals.push_back(new HocCharacterGlobal(0x33, &_currentCharacter2));
 	_globals.push_back(new RWI16Global(0x32, &_unk50));
-	_globals.push_back(new RWI16Global(0x31, &_unk49));
+	_globals.push_back(new RWI16Global(0x31, &_nativeGameState));
 	_globals.push_back(new RWI16Global(0x30, &_unk48));
 	_globals.push_back(new RWI16Global(0x2F, &_unk47));
 	_globals.push_back(new RWI16Global(0x2E, &_unk46));
 	_globals.push_back(new RWI16Global(0x2D, &_unk45)); // TODO: Special update function FUN_1407_0784, palette related?
 	_globals.push_back(new RWI16Global(0x2C, &_sheckels));	// used as currency in Istanbul
-	_globals.push_back(new RWI16Global(0x2B, &_unk43));
-	_globals.push_back(new RWI16Global(0x2A, &_unk42));
+	_globals.push_back(new RWI16Global(0x2B, &_shellBet));
+	_globals.push_back(new RWI16Global(0x2A, &_shellPea));
 	_globals.push_back(new RWI16Global(0x29, &_unk41));
 	_globals.push_back(new RWI16Global(0x28, &_unk40));
 	_globals.push_back(new ROI16Global(0x27, &_unk39));
@@ -262,14 +263,14 @@ Common::Error HocGlobals::syncState(Common::Serializer &s) {
 	s.syncAsSint16LE(_unk39);
 	s.syncAsSint16LE(_unk40);
 	s.syncAsSint16LE(_unk41);
-	s.syncAsSint16LE(_unk42);
-	s.syncAsSint16LE(_unk43);
+	s.syncAsSint16LE(_shellPea);
+	s.syncAsSint16LE(_shellBet);
 	s.syncAsSint16LE(_sheckels);
 	s.syncAsSint16LE(_unk45);
 	s.syncAsSint16LE(_unk46);
 	s.syncAsSint16LE(_unk47);
 	s.syncAsSint16LE(_unk48);
-	s.syncAsSint16LE(_unk49);
+	s.syncAsSint16LE(_nativeGameState);
 	s.syncAsSint16LE(_unk50);
 	s.syncAsSint16LE(_currentCharacter);
 	s.syncAsSint16LE(_currentCharacter2);
diff --git a/engines/dgds/globals.h b/engines/dgds/globals.h
index 7cc918d0e43..958aee29d35 100644
--- a/engines/dgds/globals.h
+++ b/engines/dgds/globals.h
@@ -149,19 +149,30 @@ class HocGlobals : public Globals {
 public:
 	HocGlobals(Clock &clock);
 
+	int16 getSheckels() const { return _sheckels; }
+
+	int16 getShellBet() const { return _shellBet; }
+	void setShellBet(int16 bet) { _shellBet = bet; }
+
+	int16 getShellPea() const { return _shellPea; }
+	void setShellPea(int16 pea) { _shellPea = pea; }
+
+	int16 getNativeGameState() const { return _nativeGameState; }
+	void setNativeGameState(int16 state) { _nativeGameState = state; }
+
 private:
 	// HoC-specific globals
 	int16 _unk39;
 	int16 _unk40;
 	int16 _unk41;
-	int16 _unk42;
-	int16 _unk43;
+	int16 _shellPea;
+	int16 _shellBet;
 	int16 _sheckels;
 	int16 _unk45;
 	int16 _unk46;
 	int16 _unk47;
 	int16 _unk48;
-	int16 _unk49;
+	int16 _nativeGameState; // state for the shell game, tank game, etc.
 	int16 _unk50;
 	int16 _currentCharacter;
 	int16 _currentCharacter2;
diff --git a/engines/dgds/image.h b/engines/dgds/image.h
index 1ad92c80a57..f1913fc0f8e 100644
--- a/engines/dgds/image.h
+++ b/engines/dgds/image.h
@@ -23,6 +23,7 @@
 #define DGDS_IMAGE_H
 
 #include <common/ptr.h>
+#include <common/rect.h>
 #include <graphics/palette.h>
 
 namespace Common {
diff --git a/engines/dgds/inventory.cpp b/engines/dgds/inventory.cpp
index 1f2c8d09489..9254c418f15 100644
--- a/engines/dgds/inventory.cpp
+++ b/engines/dgds/inventory.cpp
@@ -46,7 +46,7 @@ void Inventory::open() {
 	// Allow double-open becuase that's how the inventory shows item
 	// descriptions.
 	_isOpen = true;
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int curScene = engine->getScene()->getNum();
 	if (curScene != 2) {
 		_openedFromSceneNum = curScene;
@@ -61,7 +61,7 @@ void Inventory::close() {
 		return;
 	assert(_openedFromSceneNum != 0);
 	_isOpen = false;
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	engine->changeScene(_openedFromSceneNum);
 	_showZoomBox = false;
 	_openedFromSceneNum = 0;
@@ -95,7 +95,7 @@ void Inventory::setRequestData(const REQFileData &data) {
 	_fullWidth = req._rect.width;
 
 	// TODO! Beamish doesn't have a zoom box, or it's a different ID?
-	if (static_cast<DgdsEngine *>(g_engine)->getGameId() == GID_WILLY)
+	if (DgdsEngine::getInstance()->getGameId() == GID_WILLY)
 		_itemZoomBox = _itemBox;
 
 	if (!_prevPageBtn || !_nextPageBtn || !_itemZoomBox || !_exitButton || !_itemArea)
@@ -114,7 +114,7 @@ void Inventory::drawHeader(Graphics::ManagedSurface &surf) {
 	font->drawString(&surf, title, x1 + 4, y1 + 2, titleWidth, 0);
 
 	// Only draw the box around the title in DRAGON
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (engine->getGameId() == GID_DRAGON) {
 		int x2 = x1 + titleWidth + 6;
 		int y2 = y1 + font->getFontHeight();
@@ -127,7 +127,7 @@ void Inventory::drawHeader(Graphics::ManagedSurface &surf) {
 
 void Inventory::draw(Graphics::ManagedSurface &surf, int itemCount) {
 	RequestData &boxreq = _reqData._requests[0];
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	DgdsGameId gameId = engine->getGameId();
 
 	if (_showZoomBox) {
@@ -182,7 +182,7 @@ void Inventory::draw(Graphics::ManagedSurface &surf, int itemCount) {
 }
 
 void Inventory::drawTime(Graphics::ManagedSurface &surf) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (engine->getGameId() != GID_DRAGON)
 		return;
 
@@ -196,7 +196,7 @@ void Inventory::drawTime(Graphics::ManagedSurface &surf) {
 }
 
 void Inventory::drawItems(Graphics::ManagedSurface &surf) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	const Common::SharedPtr<Image> &icons = engine->getIcons();
 	int x = 0;
 	int y = 0;
@@ -259,7 +259,7 @@ void Inventory::drawItems(Graphics::ManagedSurface &surf) {
 }
 
 void Inventory::mouseMoved(const Common::Point &pt) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	GameItem *dragItem = engine->getScene()->getDragItem();
 	if (dragItem) {
 		engine->setMouseCursor(dragItem->_iconNum);
@@ -278,7 +278,7 @@ GameItem *Inventory::itemUnderMouse(const Common::Point &pt) {
 	if (!_itemArea)
 		return nullptr;
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	Common::Array<GameItem> &items = engine->getGDSScene()->getGameItems();
 	if (_itemArea->containsPoint(pt)) {
 		const int imgAreaX = _itemArea->_parentX + _itemArea->_x;
@@ -303,7 +303,7 @@ GameItem *Inventory::itemUnderMouse(const Common::Point &pt) {
 }
 
 bool Inventory::isItemInInventory(GameItem &item) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	DgdsGameId gameId = engine->getGameId();
 	bool result = item._inSceneNum == 2; // && (item._flags & 4)
 	if (gameId == GID_HOC) {
@@ -322,7 +322,7 @@ void Inventory::mouseLDown(const Common::Point &pt) {
 	if (!boxreq._rect.contains(pt))
 		return;
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	if (engine->getScene()->hasVisibleDialog() || !_itemBox->containsPoint(pt)) {
 		return engine->getScene()->mouseLDown(pt);
@@ -339,7 +339,7 @@ void Inventory::mouseLDown(const Common::Point &pt) {
 }
 
 void Inventory::mouseLUp(const Common::Point &pt) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	GameItem *dragItem = engine->getScene()->getDragItem();
 
 	if (dragItem) {
@@ -394,7 +394,7 @@ void Inventory::mouseLUp(const Common::Point &pt) {
 }
 
 void Inventory::mouseRUp(const Common::Point &pt) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (_itemBox->containsPoint(pt)) {
 		GameItem *underMouse = itemUnderMouse(pt);
 		if (underMouse) {
diff --git a/engines/dgds/menu.cpp b/engines/dgds/menu.cpp
index 8ab80354c35..bd715394950 100644
--- a/engines/dgds/menu.cpp
+++ b/engines/dgds/menu.cpp
@@ -141,8 +141,7 @@ void Menu::setScreenBuffer() {
 }
 
 bool Menu::updateOptionsGadget(Gadget *gadget) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
-	Audio::Mixer *mixer = engine->_mixer;
+	Audio::Mixer *mixer = DgdsEngine::getInstance()->_mixer;
 
 	switch (gadget->_gadgetNo) {
 	case kMenuOptionsJoystickOnOff:
@@ -167,7 +166,7 @@ bool Menu::updateOptionsGadget(Gadget *gadget) {
 }
 
 void Menu::configureGadget(MenuId menu, Gadget *gadget) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	// a bit of a hack - set up the gadget with the correct value before we draw it.
 	if (menu == kMenuControls) {
@@ -322,7 +321,7 @@ void Menu::onMouseMove(const Common::Point &mouse) {
 }
 
 void Menu::onMouseLUp(const Common::Point &mouse) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (_dragGadget && mouse != _dragStartPt) {
 		int16 setting = _dragGadget->onDragFinish(mouse);
 		switch (_dragGadget->_gadgetNo) {
@@ -369,7 +368,7 @@ void Menu::onMouseLUp(const Common::Point &mouse) {
 }
 
 void Menu::handleClick(const Common::Point &mouse) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int currentScene = engine->getScene()->getNum();
 	Gadget *gadget = getClickedMenuItem(mouse);
 	int16 clickedMenuItem = gadget->_gadgetNo;
@@ -517,7 +516,7 @@ void Menu::handleClick(const Common::Point &mouse) {
 }
 
 void Menu::handleClickOptionsMenu(const Common::Point &mouse) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	Audio::Mixer *mixer = engine->_mixer;
 	Gadget *gadget = getClickedMenuItem(mouse);
 	int16 clickedMenuItem = gadget->_gadgetNo;
@@ -556,7 +555,7 @@ void Menu::handleClickOptionsMenu(const Common::Point &mouse) {
 }
 
 void Menu::handleClickSkipPlayIntroMenu(const Common::Point &mouse) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	Gadget *gadget = getClickedMenuItem(mouse);
 	int16 clickedMenuItem = gadget->_gadgetNo;
 
diff --git a/engines/dgds/module.mk b/engines/dgds/module.mk
index 8d92c482150..707ac698040 100644
--- a/engines/dgds/module.mk
+++ b/engines/dgds/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS := \
 	request.o \
 	resource.o \
 	scripts.o \
+	shell_game.o \
 	ttm.o \
 	scene.o \
 	sound.o
diff --git a/engines/dgds/request.cpp b/engines/dgds/request.cpp
index 106e4d5ce07..e42a201d12a 100644
--- a/engines/dgds/request.cpp
+++ b/engines/dgds/request.cpp
@@ -427,7 +427,7 @@ byte ButtonGadget::drawWillyBg(Graphics::ManagedSurface *dst, bool enabled) cons
 }
 
 void ButtonGadget::draw(Graphics::ManagedSurface *dst) const {
-	DgdsGameId gameId = static_cast<DgdsEngine *>(g_engine)->getGameId();
+	DgdsGameId gameId = DgdsEngine::getInstance()->getGameId();
 	bool enabled = !(_flags3 & 9);
 
 	byte textCol;
@@ -667,7 +667,7 @@ void ImageGadget::draw(Graphics::ManagedSurface *dst) const {
 	_drawFrame(dst, xoff, yoff, _width, _height, _sval1I, _sval1I);
 
 	// NOTE: This only done in inventory in originals
-	if (static_cast<DgdsEngine *>(g_engine)->getGameId() == GID_DRAGON)
+	if (DgdsEngine::getInstance()->getGameId() == GID_DRAGON)
 		RequestData::drawCorners(dst, 19, xoff - 2, yoff - 2, _width + 4, _height + 4);
 	else
 		RequestData::drawCorners(dst, 19, xoff - 4, yoff - 4, _width + 8, _height + 8);
@@ -737,12 +737,12 @@ void RequestData::drawInvType(Graphics::ManagedSurface *dst) {
 
 /*static*/
 const DgdsFont *RequestData::getMenuFont() {
-	return static_cast<DgdsEngine *>(g_engine)->getFontMan()->getFont(FontManager::kGameFont);
+	return DgdsEngine::getInstance()->getFontMan()->getFont(FontManager::kGameFont);
 }
 
 /*static*/
 const Image *RequestData::getCorners() {
-	return static_cast<DgdsEngine *>(g_engine)->getUICorners().get();
+	return DgdsEngine::getInstance()->getUICorners().get();
 }
 
 /*static*/
@@ -833,7 +833,7 @@ void RequestData::drawBackgroundWithSliderArea(Graphics::ManagedSurface *dst, in
 	dst->transBlitFrom(*corners[9], Common::Point(x, (y + sliderBgHeight) - corners[9]->h));
 	dst->transBlitFrom(*corners[10], Common::Point((x + width) - corners[10]->w, (y + sliderBgHeight) - corners[10]->h));
 
-	if (static_cast<DgdsEngine *>(g_engine)->getGameId() == GID_DRAGON)
+	if (DgdsEngine::getInstance()->getGameId() == GID_DRAGON)
 		drawHeader(dst, x, y, width, 9, header, DragonHeaderTxtColor, true, DragonHeaderTopColor, DragonHeaderBottomColor);
 	else
 		drawHeader(dst, x, y + 4, width, 9, header, ChinaHeaderTxtColor, true, ChinaHeaderTopColor, ChinaHeaderBottomColor);
@@ -841,8 +841,7 @@ void RequestData::drawBackgroundWithSliderArea(Graphics::ManagedSurface *dst, in
 
 
 void RequestData::drawBackgroundNoSliders(Graphics::ManagedSurface *dst, const Common::String &header) const {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
-	DgdsGameId gameId = engine->getGameId();
+	DgdsGameId gameId = DgdsEngine::getInstance()->getGameId();
 
 	if (_rect.width == 0 || _rect.height == 0) {
 		warning("drawBackgroundNoSliders: empty rect");
@@ -863,7 +862,7 @@ void RequestData::drawBackgroundNoSliders(Graphics::ManagedSurface *dst, const C
 
 /*static*/
 void RequestData::fillBackground(Graphics::ManagedSurface *dst, uint16 x, uint16 y, uint16 width, uint16 height, int16 startoffset) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	if (engine->getGameId() == GID_DRAGON && engine->getDetailLevel() == kDgdsDetailHigh) {
 		Graphics::Surface area = dst->getSubArea(Common::Rect(Common::Point(x, y), width, height));
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index 300c590a0e0..208a4ecf653 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -502,7 +502,7 @@ void Scene::setItemAttrOp(const Common::Array<uint16> &args) {
 	if (args.size() < 3)
 		error("Expect 3 args for item attr opcode.");
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	for (auto &item : engine->getGDSScene()->getGameItems()) {
 		if (item._num != args[0])
 			continue;
@@ -520,7 +520,7 @@ void Scene::setItemAttrOp(const Common::Array<uint16> &args) {
 }
 
 void Scene::setDragItemOp(const Common::Array<uint16> &args) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	for (auto &item : engine->getGDSScene()->getGameItems()) {
 		if (item._num != args[0])
@@ -539,7 +539,7 @@ void Scene::setDragItemOp(const Common::Array<uint16> &args) {
 }
 
 void Scene::segmentStateOps(const Common::Array<uint16> &args) {
-	ADSInterpreter *interp = static_cast<DgdsEngine *>(g_engine)->adsInterpreter();
+	ADSInterpreter *interp = DgdsEngine::getInstance()->adsInterpreter();
 
 	for (uint i = 0; i < args.size(); i += 2) {
 		uint16 subop = args[i];
@@ -582,7 +582,7 @@ void Scene::segmentStateOps(const Common::Array<uint16> &args) {
 }
 
 static void _drawDragonCountdown(FontManager::FontType fontType, int16 x, int16 y) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int16 countdownEnd = engine->getGameGlobals()->getGlobal(0x22);
 	int16 currentMins = engine->getClock().getMins();
 	const DgdsFont *fnt = engine->getFontMan()->getFont(fontType);
@@ -592,7 +592,7 @@ static void _drawDragonCountdown(FontManager::FontType fontType, int16 x, int16
 
 
 bool Scene::runSceneOp(const SceneOp &op) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	switch (op._opCode) {
 	case kSceneOpChangeScene:
 		if (engine->changeScene(op._args[0]))
@@ -703,7 +703,7 @@ bool Scene::runSceneOp(const SceneOp &op) {
 }
 
 bool Scene::runDragonOp(const SceneOp &op) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	switch (op._opCode) {
 	case kSceneOpPasscode:
 		engine->getScene()->sceneOpUpdatePasscodeGlobal();
@@ -746,8 +746,9 @@ bool Scene::runDragonOp(const SceneOp &op) {
 	return true;
 }
 
+
 bool Scene::runChinaOp(const SceneOp &op) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	switch (op._opCode) {
 	case kSceneOpOpenChinaOpenGameOverMenu:
 		engine->setMenuToTrigger(kMenuGameOver);
@@ -759,14 +760,11 @@ bool Scene::runChinaOp(const SceneOp &op) {
 	case kSceneOpOpenChinaTrainMenu:
 		engine->setMenuToTrigger(kMenuSkipArcade);
 		break;
-	case kSceneOpShellGame:
-		// TODO: Shell game in scene 81 (accessible from scene 16)
-		// We set a high number of sheckels for now
-		engine->getGDSScene()->setGlobal(0x2C, 400);
-		// We clear the gambler's talk data, as it's not cleared
-		// by the game scripts
-		engine->getScene()->freeTalkData(8);
-		warning("TODO: Implement shell game");
+	case kSceneOpShellGameTick:
+		engine->getShellGame()->shellGameTick();
+		break;
+	case kSceneOpShellGameEnd:
+		engine->getShellGame()->shellGameEnd();
 		break;
 	case kSceneOpOpenChinaStartIntro:
 		// The game first jumps to scene 100, and then to 98
@@ -780,7 +778,7 @@ bool Scene::runChinaOp(const SceneOp &op) {
 }
 
 bool Scene::runBeamishOp(const SceneOp &op) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (op._opCode & 0x8000) {
 		uint16 opcode = op._opCode & 0x7fff;
 		for (const ConditionalSceneOp &cop : _conditionalOps) {
@@ -803,7 +801,7 @@ bool Scene::runBeamishOp(const SceneOp &op) {
 }
 
 bool Scene::runOps(const Common::Array<SceneOp> &ops, int16 addMinuites /* = 0 */) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	for (const SceneOp &op : ops) {
 		if (!checkConditions(op._conditionList))
 			continue;
@@ -838,7 +836,7 @@ bool Scene::runOps(const Common::Array<SceneOp> &ops, int16 addMinuites /* = 0 *
 }
 
 bool Scene::checkConditions(const Common::Array<SceneConditions> &conds) const {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	uint cnum = 0;
 	while (cnum < conds.size()) {
@@ -982,6 +980,7 @@ void SDSScene::unload() {
 	_objInteractions2.clear();
 	_dialogs.clear();
 	_triggers.clear();
+	_talkData.clear();
 	_sceneDialogFlags = kDlgFlagNone;
 }
 
@@ -1061,7 +1060,7 @@ Dialog *SDSScene::loadDialogData(uint16 num) {
 			return &dlg;
 
 	const Common::String filename = Common::String::format("D%d.DDS", num);
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	ResourceManager *resourceManager = engine->getResourceManager();
 	Common::SeekableReadStream *dlgFile = resourceManager->getResource(filename);
 	if (!dlgFile)
@@ -1158,7 +1157,7 @@ bool SDSScene::loadTalkData(uint16 num) {
 	}
 
 	const Common::String filename = Common::String::format("T%d.TDS", num);
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	ResourceManager *resourceManager = engine->getResourceManager();
 	Common::SeekableReadStream *dlgFile = resourceManager->getResource(filename);
 	if (!dlgFile)
@@ -1369,8 +1368,8 @@ static uint16 passcodeVal3 = 0;
 static uint16 passcodeVal4 = 0;
 
 void SDSScene::sceneOpUpdatePasscodeGlobal() {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
-	int16 globalval = engine->getGDSScene()->getGlobal(0x20);
+	GDSScene *gdsScene = DgdsEngine::getInstance()->getGDSScene();
+	int16 globalval = gdsScene->getGlobal(0x20);
 
 	if (globalval > 34)
 		return;
@@ -1430,7 +1429,7 @@ void SDSScene::sceneOpUpdatePasscodeGlobal() {
 		}
 	}
 
-	engine->getGDSScene()->setGlobal(0x20, globalval);
+	gdsScene->setGlobal(0x20, globalval);
 }
 
 void SDSScene::showDialog(uint16 fileNum, uint16 dlgNum) {
@@ -1624,13 +1623,13 @@ bool SDSScene::drawAndUpdateDialogs(Graphics::ManagedSurface *dst) {
 
 void SDSScene::globalOps(const Common::Array<uint16> &args) {
 	// The globals are held by the GDS scene
-	static_cast<DgdsEngine *>(g_engine)->getGDSScene()->globalOps(args);
+	DgdsEngine::getInstance()->getGDSScene()->globalOps(args);
 }
 
 void SDSScene::mouseMoved(const Common::Point &pt) {
 	Dialog *dlg = getVisibleDialog();
 	const HotArea *area = findAreaUnderMouse(pt);
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	int16 cursorNum = (!dlg && area) ? area->_cursorNum : 0;
 	if (_dragItem) {
@@ -1659,7 +1658,7 @@ void SDSScene::mouseLDown(const Common::Point &pt) {
 	debug(9, "Mouse LDown on area %d (%d,%d,%d,%d) cursor %d", area->_num, area->_rect.x, area->_rect.y,
 			area->_rect.width, area->_rect.height, area->_cursorNum);
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int16 addmins = engine->getGameGlobals()->getGameMinsToAddOnStartDrag();
 	runOps(area->onLDownOps, addmins);
 	GameItem *item = dynamic_cast<GameItem *>(area);
@@ -1703,7 +1702,7 @@ void SDSScene::mouseLUp(const Common::Point &pt) {
 	debug(9, "Mouse LUp on area %d (%d,%d,%d,%d) cursor %d", area->_num, area->_rect.x, area->_rect.y,
 			area->_rect.width, area->_rect.height, area->_cursorNum);
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	if (!_rbuttonDown)
 		engine->setMouseCursor(area->_cursorNum);
 
@@ -1762,7 +1761,7 @@ void SDSScene::onDragFinish(const Common::Point &pt) {
 
 	GameItem *dragItem = _dragItem;
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	const Globals *globals = engine->getGameGlobals();
 	GDSScene *gdsScene = engine->getGDSScene();
 
@@ -1852,7 +1851,7 @@ void SDSScene::mouseRUp(const Common::Point &pt) {
 	if (!area)
 		return;
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 
 	if (area->_num == 0) {
 		debug("Mouse RUp on inventory.");
@@ -1894,9 +1893,7 @@ bool SDSScene::hasVisibleOrOpeningDialog() const {
 }
 
 HotArea *SDSScene::findAreaUnderMouse(const Common::Point &pt) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
-
-	for (auto &item : engine->getGDSScene()->getGameItems()) {
+	for (auto &item : DgdsEngine::getInstance()->getGDSScene()->getGameItems()) {
 		if (item._inSceneNum == _num && checkConditions(item.enableConditions)
 			&& _isInRect(pt, item._rect)) {
 			return &item;
@@ -1912,7 +1909,7 @@ HotArea *SDSScene::findAreaUnderMouse(const Common::Point &pt) {
 }
 
 void SDSScene::addInvButtonToHotAreaList() {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	const Common::Array<MouseCursor> &cursors = engine->getGDSScene()->getCursorList();
 	const Common::SharedPtr<Image> &icons = engine->getIcons();
 
@@ -2110,7 +2107,7 @@ bool GDSScene::loadRestart(const Common::String &filename, ResourceManager *reso
 
 	/*uint32 unk = */ file->readUint32LE();
 
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	Common::Array<Global *> &globs = engine->getGameGlobals()->getAllGlobals();
 
 	if (globs.size() > 50)
@@ -2148,8 +2145,7 @@ bool GDSScene::loadRestart(const Common::String &filename, ResourceManager *reso
 }
 
 void GDSScene::initIconSizes() {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
-	const Common::SharedPtr<Image> icons = engine->getIcons();
+	const Common::SharedPtr<Image> icons = DgdsEngine::getInstance()->getIcons();
 	uint16 nicons = icons ? icons->getFrames().size() : 0;
 	for (GameItem &item : _gameItems) {
 		if (item._iconNum < nicons) {
@@ -2269,7 +2265,7 @@ void GDSScene::globalOps(const Common::Array<uint16> &args) {
 }
 
 int16 GDSScene::getGlobal(uint16 num) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int curSceneNum = engine->getScene()->getNum();
 	DgdsGameId gameId = engine->getGameId();
 
@@ -2291,7 +2287,7 @@ int16 GDSScene::getGlobal(uint16 num) {
 }
 
 int16 GDSScene::setGlobal(uint16 num, int16 val) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	int curSceneNum = engine->getScene()->getNum();
 	for (auto &global : _perSceneGlobals) {
 		if (global.matches(num, curSceneNum)) {
@@ -2309,7 +2305,7 @@ int16 GDSScene::setGlobal(uint16 num, int16 val) {
 }
 
 void GDSScene::drawItems(Graphics::ManagedSurface &surf) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	const Common::SharedPtr<Image> &icons = engine->getIcons();
 	int currentScene = engine->getScene()->getNum();
 	if (!icons || icons->loadedFrameCount() < 3)
diff --git a/engines/dgds/scene.h b/engines/dgds/scene.h
index c63d1118a04..e1ca4e75a63 100644
--- a/engines/dgds/scene.h
+++ b/engines/dgds/scene.h
@@ -28,6 +28,7 @@
 
 #include "dgds/dialog.h"
 #include "dgds/dgds_rect.h"
+#include "dgds/shell_game.h"
 
 namespace Dgds {
 
@@ -125,7 +126,8 @@ enum SceneOpCode {
 
 	// China-specific opcodes
 	kSceneOpOpenChinaTankMenu = 102,
-	kSceneOpShellGame = 110,
+	kSceneOpShellGameEnd = 109,
+	kSceneOpShellGameTick = 110,
 	kSceneOpOpenChinaTrainMenu = 113,
 	kSceneOpOpenChinaOpenGameOverMenu = 114,	// args: none.
 	kSceneOpOpenChinaOpenSkipCreditsMenu = 115,	// args: none.
diff --git a/engines/dgds/shell_game.cpp b/engines/dgds/shell_game.cpp
new file mode 100644
index 00000000000..e138674f54e
--- /dev/null
+++ b/engines/dgds/shell_game.cpp
@@ -0,0 +1,345 @@
+/* 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/math.h"
+
+#include "dgds/shell_game.h"
+#include "dgds/dgds.h"
+#include "dgds/globals.h"
+#include "dgds/font.h"
+#include "dgds/includes.h"
+#include "dgds/sound.h"
+
+namespace Dgds {
+
+ShellGame::ShellGame() : _swapStatus(0), _revealPeaStep(0),
+_currentPeaPosition(0), _lastPass(0), _distractStep(0),
+_distractDelay(0), _state13Counter(0), _swapPea1(0), _swapPea2(0),
+_lastSwapPea1(0), _swapMoveDist(0), _swapMoveStep(0), _swapCount(0),
+_reverseDirection(0), _clockwise(false)
+{
+}
+
+void ShellGame::init() {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	HocGlobals *globals = dynamic_cast<HocGlobals *>(engine->getGameGlobals());
+	_shellGameImg.reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
+	_shellGameImg->loadBitmap("SHELLGM2.BMP");
+	globals->setShellPea(engine->getRandom().getRandomNumber(2));
+	_distractStep = 14;
+	_distractDelay = 0;
+	_state13Counter = 0;
+}
+
+static int16 _getState() {
+	HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
+	return globals->getNativeGameState();
+}
+
+static void _setState(int16 val) {
+	HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
+	globals->setNativeGameState(val);
+}
+
+static int16 _getPeaPosition() {
+	HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
+	return globals->getShellPea();
+}
+
+static void _setPeaPosition(int16 val) {
+	HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
+	globals->setShellPea(val);
+}
+
+
+void ShellGame::drawShellGameStr(int16 count, int16 x, int16 y) const {
+	const Common::String countStr = Common::String::format("%d", count);
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	const DgdsFont *fnt = engine->getFontMan()->getFont(FontManager::k6x6Font);
+	fnt->drawString(&engine->getStoredAreaBuffer(), countStr, x, y, 50, 0);
+}
+
+void ShellGame::drawShells() const {
+	const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
+	for (uint i = 0; i < 3; i++)
+		_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, DgdsEngine::getInstance()->getStoredAreaBuffer());
+}
+
+void ShellGame::shellGameTick() {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	HocGlobals *globals = dynamic_cast<HocGlobals *>(engine->getGameGlobals());
+
+	if (!_shellGameImg)
+		init();
+
+	// copy the background to the saved buffer (hack.. but the original does it too)
+	engine->getStoredAreaBuffer().blitFrom(engine->getBackgroundBuffer());
+
+	// Draw the shekels.
+	drawShellGameStr(globals->getSheckels(), 131, 123);
+
+	// Draw the bet
+	drawShellGameStr(globals->getShellBet(), 201, 123);
+
+	update();
+
+	if (_revealPeaStep) {
+		revealPea(false);
+	} else {
+		if (_swapStatus)
+			swapShells(false);
+		else
+			drawShells();
+	}
+}
+
+
+void ShellGame::update() {
+	int16 state = _getState();
+	if (state == 1 || state == 7) {
+		_currentPeaPosition = _getPeaPosition();
+		if (state == 7)
+			_currentPeaPosition = _currentPeaPosition / 16;
+		_setPeaPosition(_getPeaPosition() & 0xf);
+		_revealPeaStep = 1;
+		state++;
+	} else if (state == 4 || state == 10) {
+		_swapStatus = 1;
+		_swapCount = 0;
+		state++;
+	} else if (state == 13) {
+		if (_state13Counter) {
+			_state13Counter--;
+			if (!_state13Counter)
+				state = 10;
+		} else {
+			_state13Counter = 20;
+		}
+	}
+	_setState(state);
+}
+
+
+void ShellGame::revealPea(bool flag) {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+	if (_revealPeaStep == 1)
+		engine->_soundPlayer->playSFX(147);
+
+	uint16 x_offset = 55 * _currentPeaPosition;
+	uint16 y_offset;
+	if (_revealPeaStep > 40) {
+		y_offset = 60 - _revealPeaStep;
+	} else if (_revealPeaStep > 20) {
+		y_offset = 20;
+	} else {
+		y_offset = _revealPeaStep;
+	}
+
+	// Draw the pea
+	if (_currentPeaPosition == _getPeaPosition())
+		_shellGameImg->drawBitmap(1, 112 + x_offset, 166, screenRect, engine->getStoredAreaBuffer());
+
+	// Draw the shell revealing the pea
+	_shellGameImg->drawBitmap(0, 98 + x_offset, 153 - y_offset, screenRect, engine->getStoredAreaBuffer());
+
+	// Draw the other shells
+	for (int i = 0; i < 3; i++) {
+		if (i != _currentPeaPosition)
+			_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, engine->getStoredAreaBuffer());
+	}
+
+	if (!flag) {
+		if (_lastPass) {
+			_lastPass = false;
+			_revealPeaStep = 0;
+			_setState(_getState() + 1);
+		} else {
+			if (y_offset == 0)
+				_lastPass = true;
+			else
+				_revealPeaStep++;
+		}
+	}
+}
+
+
+bool ShellGame::checkDistract() {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	HocGlobals *globals = static_cast<HocGlobals *>(engine->getGameGlobals());
+
+	int16 sheckels = globals->getSheckels();
+	int16 bet = globals->getShellBet();
+	if ((sheckels + bet >= 300)
+			|| (sheckels + bet >= 150 && (engine->getRandom().getRandomNumber(256) & 0xc0))
+			|| bet > 95
+			|| (bet > 45 && (engine->getRandom().getRandomNumber(3)))) {
+		_distractStep++;
+		if (_distractStep > 21)
+			_distractStep = 14;
+		return true;
+	}
+
+	return false;
+}
+
+
+
+void ShellGame::setupSwap() {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	do {
+		_swapPea1 = engine->getRandom().getRandomNumber(2);
+	} while (_swapPea1 == _lastSwapPea1);
+
+	_lastSwapPea1 = _swapPea1;
+
+	if (_swapPea1 == 0) {
+		_swapPea2 = 1;
+	} else if (_swapPea1 == 1) {
+			_swapPea2 = 2;
+	} else {
+		_swapPea1 = 0;
+		_swapPea2 = 2;
+	}
+
+	if (_getPeaPosition() == _swapPea1) {
+		_setPeaPosition(_swapPea2);
+	} else if (_getPeaPosition() == _swapPea2) {
+		_setPeaPosition(_swapPea1);
+	}
+
+	_swapMoveDist = (_swapPea2 - _swapPea1) * 55;
+	_lastPass = false;
+	_swapMoveStep = 0;
+	_swapStatus = 2;
+}
+
+
+void ShellGame::swapShells(bool flag) {
+	DgdsEngine *engine = DgdsEngine::getInstance();
+	const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+	if (!flag) {
+		if (_swapStatus == 1) {
+			engine->_soundPlayer->playSFX(145);
+			setupSwap();
+			_clockwise = (bool)(engine->getRandom().getRandomNumber(255) & 0x40);
+			_reverseDirection = 0;
+		}
+
+		uint16 move_speed = (2 << engine->getDifficulty()) + 2;
+		if (_getState() >= 10 && _getState() <= 12)
+			move_speed++;
+		if (!_lastPass) {
+			_swapMoveStep += move_speed;
+			if (abs(_swapPea1 - _swapPea2) != 1)
+				_swapMoveStep += move_speed;
+			if (_swapMoveStep > _swapMoveDist)
+				_swapMoveStep = _swapMoveDist;
+		}
+	}
+
+	uint16 xbase1 = 98 + _swapPea1 * 55;
+	uint16 xbase2 = 98 + _swapPea2 * 55;
+
+	double move = ((double)_swapMoveStep / _swapMoveDist) * M_PI;
+	uint16 y_offset;
+	if (abs(_swapPea1 - _swapPea2) == 1)
+		y_offset = 26;
+	else
+		y_offset = 28;
+
+	y_offset = (int16)(y_offset * sin(move));
+
+	for (int i = 0; i < 3; i++) {
+		if (i != _swapPea1 && i != _swapPea2)
+			_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, engine->getStoredAreaBuffer());
+	}
+
+	if (!flag) {
+		if ((_reverseDirection == 0) && (_swapMoveStep >= (_swapMoveDist / 2))) {
+			if (engine->getRandom().getRandomNumber(7)) {
+				_reverseDirection = -1;
+			} else {
+				_reverseDirection = 1;
+				if (_getPeaPosition() == _swapPea1)
+					_setPeaPosition(_swapPea2);
+				else if (_getPeaPosition() == _swapPea2)
+					_setPeaPosition(_swapPea1);
+			}
+		}
+	}
+
+	uint16 x1,x2,y1,y2;
+	if (_reverseDirection <= 0) {
+		x1 = xbase1 + _swapMoveStep;
+		x2 = xbase2 - _swapMoveStep;
+	} else {
+		x1 = xbase2 - _swapMoveStep;
+		x2 = xbase1 + _swapMoveStep;
+	}
+
+	if (_clockwise) {
+		y1 = 153 - y_offset;
+		y2 = 153 + y_offset;
+	} else {
+		y1 = 153 + y_offset;
+		y2 = 153 - y_offset;
+	}
+
+	_shellGameImg->drawBitmap(0, x1, y1, screenRect, engine->getStoredAreaBuffer());
+	_shellGameImg->drawBitmap(0, x2, y2, screenRect, engine->getStoredAreaBuffer());
+
+	if (!flag) {
+		int16 state = _getState();
+		if (_lastPass) {
+			_lastPass = false;
+			if (state == 11)
+				_setState(0);
+			else
+				_setState(6);
+			_swapStatus = 0;
+		} else  if (_distractDelay) {
+			_distractDelay--;
+		} else if (_swapMoveStep == _swapMoveDist) {
+			_swapCount++;
+			if (state == 11) {
+				_lastPass = true;
+			} else if (_swapCount == 8 && checkDistract()) {
+				 _setState(_distractStep);
+				 _distractDelay = 20;
+			} else {
+				 if (_swapCount < 12 || engine->getRandom().getRandomNumber(7) || state >= 14)
+					_swapStatus = 1;
+				 else
+					_lastPass = true;
+			}
+		}
+	}
+}
+
+
+void ShellGame::shellGameEnd() {
+	_shellGameImg.reset();
+}
+
+} // end namespace Dgds
diff --git a/engines/dgds/shell_game.h b/engines/dgds/shell_game.h
new file mode 100644
index 00000000000..be2285d13a0
--- /dev/null
+++ b/engines/dgds/shell_game.h
@@ -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 DGDS_SHELL_GAME_H
+#define DGDS_SHELL_GAME_H
+
+#include "dgds/image.h"
+
+namespace Dgds {
+
+/* Native code for the shell game from Heart of China. */
+class ShellGame {
+public:
+	ShellGame();
+
+	void shellGameTick();
+	void shellGameEnd();
+
+private:
+	void init();
+	void drawShellGameStr(int16 count, int16 x, int16 y) const;
+	void drawShells() const;
+	void swapShells(bool flag);
+	void revealPea(bool flag);
+	void update();
+	bool checkDistract();
+	void setupSwap();
+
+	Common::SharedPtr<Image> _shellGameImg;
+	uint16 _revealPeaStep;
+	uint16 _currentPeaPosition;
+	bool _lastPass;
+	uint16 _distractStep;
+	uint16 _distractDelay;
+	uint16 _state13Counter;
+	int16 _swapPea1;
+	int16 _swapPea2;
+	uint16 _lastSwapPea1;
+	uint16 _swapStatus;
+	int16 _swapMoveDist;
+	uint16 _swapMoveStep;
+	uint16 _swapCount;
+	int16 _reverseDirection;
+	bool _clockwise;
+};
+
+} // end namespace Dgds
+
+#endif // DGDS_SHELL_GAME_H
diff --git a/engines/dgds/ttm.cpp b/engines/dgds/ttm.cpp
index d54a00651a8..b2d2e43ba2d 100644
--- a/engines/dgds/ttm.cpp
+++ b/engines/dgds/ttm.cpp
@@ -43,7 +43,7 @@ void GetPutRegion::reset() {
 }
 
 Common::Error TTMEnviro::syncState(Common::Serializer &s) {
-	DgdsEngine *engine = static_cast<DgdsEngine *>(g_engine);
+	DgdsEngine *engine = DgdsEngine::getInstance();
 	for (auto &shape : _scriptShapes) {
 		bool hasShape = shape.get() != nullptr;
 		s.syncAsByte(hasShape);




More information about the Scummvm-git-logs mailing list