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

sev- sev at scummvm.org
Sat Aug 25 23:12:48 CEST 2018


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

Summary:
764ee3a5a3 MUTATIONOFJB: Base for new engine
95cd6b7442 MUTATIONOFJB: Load and draw scene background
3696865e69 MUTATIONOFJB: Load initial game state and allow room change
356a6809c3 MUTATIONOFJB: Fix loading room 11 (and possibly some others).
f7d5a825a0 MUTATIONOFJB: Start implementation of ATN scripts (IF command).
3672ea5572 MUTATIONOFJB: Continue implementation of if/else script commands.
6d926ff55b MUTATIONOFJB: Add support for CHANGED, CHANGEO and CHANGES commands.
bbd3750aee MUTATIONOFJB: Add change scene command, implement listsections and showsection debug console commands.
5d29112f1c MUTATIONOFJB: Add say command with dummy implementation.
bdb6582bb2 MUTATIONOFJB: Implement data model for inventory.
dae522f63c MUTATIONOFJB: Implement inventory commands.
b4dad9bca7 MUTATIONOFJB: Show multiple script commands in showsection debug command.
d3e281e24c MUTATIONOFJB: Fix uninitialized ChangeOperation, fix parsing tag in IF command and add some comments.
5854d310ee MUTATIONOFJB: Fix some code formatting issues.
37ae32e1d3 MUTATIONOFJB: Don't store ActionInfo pointers, because they might be invalidated, and parse/show actions with two object
fa9c8be129 MUTATIONOFJB: Convert section names to 7bit ASCII in debug console.
ae979a8310 MUTATIONOFJB: Add support for IFITEM command and fix parsing conditional commands that are right after #ELSE.
523f973e0a MUTATIONOFJB: Add support of IFPIGGY command.
54c2c0aee6 MUTATIONOFJB: Add support for labels and goto.
fb75e483e4 MUTATIONOFJB: Fix GOTO command.
1d84041508 MUTATIONOFJB: Add Game class.
7a081f0605 MUTATIONOFJB: Load local (room) scripts.
3928c52c0e MUTATIONOFJB: Add support for CAMEFROM command.
63c0dac961 MUTATIONOFJB: Add support for macro definitions.
e1d173ed75 MUTATIONOFJB: Add changescene debug command and fix macro debug commands.
938f222d48 MUTATIONOFJB: Add support for calling macros.
7a18987301 MUTATIONOFJB: Support for running commands.
e93e20dbe8 MUTATIONOFJB: Parse startup sections in scripts and fix change scene command.
574bb83b97 MUTATIONOFJB: Add support for NEWROOM command.
128d30c91c MUTATIONOFJB: Run action when clicking on static or door.
2b94873694 MUTATIONOFJB: Introduce better animation loader that supports diff frames.
99d9055e20 MUTATIONOFJB: Load object frames and implement special handling for map scenes.
9a3a66ab68 MUTATIONOFJB: Fix object animatation loader.
9af3d8a238 MUTATIONOFJB: Implement UI for inventory.
c25ed89572 MUTATIONOFJB: Refactor inventory UI into separate widget, add button widgets.
29a809d691 MUTATIONOFJB: Add rename command.
5290d9a74b MUTATIONOFJB: Draw HUD background.
61c106b330 MUTATIONOFJB: Add font support and conversation widget.
2fb867b2f5 MUTATIONOFJB: Fix issue with parsing #MACRO and #STARTUP right after end block command.
f102667fc2 MUTATIONOFJB: Add support for DEFINE_STRUCT script command.
20d6d71ec9 MUTATIONOFJB: Basic conversation support.
d2e354b51f MUTATIONOFJB: Implement scroll buttons.
febff83a4e MUTATIONOFJB: Draw objects (first frame only) and improve conversation support.
3b614f0832 MUTATIONOFJB: Implement RANDOM command.
296835f84a MUTATIONOFJB: Draw object animations on map scene.
d22da95282 MUTATIONOFJB: Fix multiple RANDOM commands in one script.
d358a65bbc MUTATIONOFJB: Run extra sections from conversation.
eaba12cecd MUTATIONOFJB: Use the vanilla cursor.
2cd1728f42 MUTATIONOFJB: Add support for repeating choices.
2ee0a90059 MUTATIONOFJB: Change cursor color when it's under entity.
74ef0d9cfe MUTATIONOFJB: Correctly handle empty animation frames.
f70eb01061 MUTATIONOFJB: Add null check.
f94ff7aa8e MUTATIONOFJB: Implement word wrapping for subtitles.
cda1f0dd3a MUTATIONOFJB: Animate objects.
578a6794de MUTATIONOFJB: Improve documentation, rename cryptic variables.
9aa911314f MUTATIONOFJB: Handle hardcoded animations.
96fbe2f881 MUTATIONOFJB: Fix crash when static/door name is set to empty string.
215b87ccca MUTATIONOFJB: When redrawing room, draw object animations at their current frame instead of their first frame.
6c4ae7f198 MUTATIONOFJB: Implement multiple speeches in one response line.
2e656e69b3 MUTATIONOFJB: Blit with threshold.
3306cbfeaa MUTATIONOFJB: Implement SayCommand::execute.
32df911b4d MUTATIONOFJB: Implement SETCOL command.
4fbbaf944a MUTATIONOFJB: Extend blit_if to support both ManagedSurface and Surface.
cd15dd82a2 MUTATIONOFJB: Check for out of bounds destination in blit_if.
298bfc3d10 MUTATIONOFJB: Subclass Graphics::Font to reuse existing code.
6ff609c514 MUTATIONOFJB: Improve documentation for statics.
a25715a29b MUTATIONOFJB: Fix code formatting issues (with astyle).
4633b83986 MUTATIONOFJB: Improve documentation and naming.
9f1c628d4b MUTATIONOFJB: Fix forward declarations of structs.
561309eaa2 MUTATIONOFJB: Fix missing lines between block ends.
959f37dfe4 MUTATIONOFJB: Don't mark internal strings as translatable.
cf878d8777 MUTATIONOFJB: Change old-style C casts to static_cast.
696b61c146 MUTATIONOFJB: Move method comments to headers.
0e90d6eae3 MUTATIONOFJB: Use advanced detector.
b00395b0b9 MUTATIONOFJB: Fix MSVC warnings.


Commit: 764ee3a5a3f9f600b8d369d8474d2702b22f73f7
    https://github.com/scummvm/scummvm/commit/764ee3a5a3f9f600b8d369d8474d2702b22f73f7
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Base for new engine

Changed paths:
  A engines/mutationofjb/configure.engine
  A engines/mutationofjb/detection.cpp
  A engines/mutationofjb/module.mk
  A engines/mutationofjb/mutationofjb.cpp
  A engines/mutationofjb/mutationofjb.h


diff --git a/engines/mutationofjb/configure.engine b/engines/mutationofjb/configure.engine
new file mode 100644
index 0000000..499e078
--- /dev/null
+++ b/engines/mutationofjb/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine mutationofjb "Mutation of JB" no
diff --git a/engines/mutationofjb/detection.cpp b/engines/mutationofjb/detection.cpp
new file mode 100644
index 0000000..f95f696
--- /dev/null
+++ b/engines/mutationofjb/detection.cpp
@@ -0,0 +1,113 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/mutationofjb.h"
+
+#include "common/config-manager.h"
+
+#include "engines/metaengine.h"
+
+static const PlainGameDescriptor mutationofjb_setting[] = {
+	{"mutationofjb", "Mutation of J.B."},
+	{0, 0}
+};
+
+class MutationOfJBMetaEngine : public MetaEngine {
+public:
+	virtual const char *getName() const {
+		return "Mutation of J.B.";
+	}
+
+	virtual const char *getOriginalCopyright() const {
+		return "Mutation of J.B. (C) 1996 RIKI Computer Games";
+	}
+
+	virtual PlainGameList getSupportedGames() const {
+		PlainGameList games;
+		const PlainGameDescriptor *g = mutationofjb_setting;
+		while (g->gameId) {
+			games.push_back(*g);
+			g++;
+		}
+
+		return games;
+	}
+
+	virtual PlainGameDescriptor findGame(const char *gameid) const {
+		const PlainGameDescriptor *g = mutationofjb_setting;
+		while (g->gameId) {
+			if (0 == scumm_stricmp(gameid, g->gameId))
+				return *g;
+			g++;
+		}
+		return PlainGameDescriptor::empty();
+	}
+
+	virtual DetectedGames detectGames(const Common::FSList &fslist) const {
+		DetectedGames detectedGames;
+
+		for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+			if (!file->isDirectory()) {
+				const char *gameName = file->getName().c_str();
+
+				if (0 == scumm_stricmp("startup.dat", gameName)) {
+					detectedGames.push_back(DetectedGame(mutationofjb_setting[0]));
+					break;
+				}
+			}
+		}
+		return detectedGames;
+	}
+
+	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const {
+		assert(syst);
+		assert(engine);
+
+		Common::FSList fslist;
+		Common::FSNode dir(ConfMan.get("path"));
+		if (!dir.getChildren(fslist, Common::FSNode::kListAll)) {
+			return Common::kNoGameDataFoundError;
+		}
+
+		// Invoke the detector
+		Common::String gameid = ConfMan.get("gameid");
+		DetectedGames detectedGames = detectGames(fslist);
+
+		for (uint i = 0; i < detectedGames.size(); i++) {
+			if (detectedGames[i].gameId == gameid) {
+				// At this point you may want to perform additional sanity checks.
+				*engine = new MutationOfJB::MutationOfJBEngine(syst);
+				return Common::kNoError;
+			}
+		}
+
+		// Failed to find any game data
+		return Common::kNoGameDataFoundError;
+	}
+};
+
+#if PLUGIN_ENABLED_DYNAMIC(MUTATIONOFJB)
+	REGISTER_PLUGIN_DYNAMIC(MUTATIONOFJB, PLUGIN_TYPE_ENGINE, MutationOfJBMetaEngine);
+#else
+	REGISTER_PLUGIN_STATIC(MUTATIONOFJB, PLUGIN_TYPE_ENGINE, MutationOfJBMetaEngine);
+#endif
+
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
new file mode 100644
index 0000000..2e28d41
--- /dev/null
+++ b/engines/mutationofjb/module.mk
@@ -0,0 +1,13 @@
+MODULE := engines/mutationofjb
+
+MODULE_OBJS := \
+	detection.o \
+	mutationofjb.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_MUTATIONOFJB), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
new file mode 100644
index 0000000..f50ba70
--- /dev/null
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+#include "common/debug.h"
+#include "common/error.h"
+
+#include "engines/util.h"
+
+#include "mutationofjb/mutationofjb.h"
+
+namespace MutationOfJB {
+
+MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
+: Engine(syst), _console(nullptr) {
+	debug("MutationOfJBEngine::MutationOfJBEngine");
+}
+
+MutationOfJBEngine::~MutationOfJBEngine() {
+	debug("MutationOfJBEngine::~MutationOfJBEngine");
+}
+
+Common::Error MutationOfJBEngine::run() {
+	initGraphics(320, 200, false);
+	_console = new Console(this);
+	debug("MutationOfJBEngine::run");
+
+	return Common::kNoError;
+}
+}
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
new file mode 100644
index 0000000..1c87fe4
--- /dev/null
+++ b/engines/mutationofjb/mutationofjb.h
@@ -0,0 +1,51 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_MUTATIONOFJB_H
+#define MUTATIONOFJB_MUTATIONOFJB_H
+
+#include "engines/engine.h"
+#include "gui/debugger.h"
+
+namespace MutationOfJB {
+
+class Console;
+
+class MutationOfJBEngine : public Engine {
+public:
+	MutationOfJBEngine(OSystem *syst);
+	~MutationOfJBEngine();
+
+	virtual Common::Error run();
+private:
+	Console *_console;
+};
+
+class Console : public GUI::Debugger {
+public:
+	Console(MutationOfJBEngine *vm) {}
+	virtual ~Console(void) {}
+};
+
+}
+
+#endif


Commit: 95cd6b74426b555e58577be0c46006a60fd77979
    https://github.com/scummvm/scummvm/commit/95cd6b74426b555e58577be0c46006a60fd77979
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Load and draw scene background

Changed paths:
  A engines/mutationofjb/encryptedfile.cpp
  A engines/mutationofjb/encryptedfile.h
  A engines/mutationofjb/room.cpp
  A engines/mutationofjb/room.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h


diff --git a/engines/mutationofjb/encryptedfile.cpp b/engines/mutationofjb/encryptedfile.cpp
new file mode 100644
index 0000000..0796811
--- /dev/null
+++ b/engines/mutationofjb/encryptedfile.cpp
@@ -0,0 +1,64 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "encryptedfile.h"
+
+static const uint8 XOR_TABLE[] = {
+  0x41, 0x2b, 0x7a, 0x0c, 0xc8, 0xe5, 0x0c, 0xde, 0x45, 0xa8, 0x00, 0xad,
+  0x70, 0xac, 0x23, 0xe0, 0x0c, 0xde, 0xac, 0x16, 0xa1, 0x1a, 0x70, 0x17,
+  0x1b, 0x90, 0x34, 0x6e, 0x53, 0xfe, 0xdd, 0x3c, 0xef, 0x74, 0x75, 0x3e,
+  0x2e, 0xb0, 0x3d, 0x2b, 0x74, 0x6d, 0x59, 0xde, 0xc6, 0x20, 0xb8, 0xf3,
+  0x7d, 0xfa, 0x4d, 0xbd, 0xdb, 0x3c, 0xc5, 0xcb, 0x57, 0x13, 0x40, 0x6b,
+  0xb8, 0xad, 0xb9, 0xc1, 0x6a, 0x37, 0x39, 0x80, 0x94, 0xd3, 0xdf, 0x4b,
+  0xe4, 0xe4, 0x7a, 0x4c, 0x0f, 0x21, 0x27, 0x9a, 0x7e, 0x52, 0x35, 0x58,
+  0xb4, 0xbc, 0x5a, 0xc9, 0x48, 0x7f, 0xcc, 0xb6, 0x97, 0x7b, 0xf1, 0xd5,
+  0x88, 0x8c, 0xa9, 0x27, 0xf7, 0x20, 0x68, 0x65, 0xad, 0x6f, 0x9e, 0x07,
+  0xf8, 0xf6, 0x2c, 0x65, 0x4a, 0xe9, 0xd8, 0x13, 0x6a, 0xb5, 0x14, 0x6f,
+  0x29, 0xdc, 0x68, 0xf8, 0xa0, 0xb6, 0x73, 0x03, 0x69, 0xe3, 0x4f, 0xb6,
+  0x59, 0x79, 0xae, 0x93, 0xad, 0x40, 0x27, 0xd0, 0xb5, 0x7a, 0x68, 0x5d,
+  0x5e, 0x19, 0x53, 0x4f, 0x40, 0x56, 0x3d, 0x10, 0xf8, 0x0a, 0xc6, 0x90,
+  0x06, 0x45, 0x13, 0x4a, 0x6a, 0xfe, 0x56, 0xeb, 0xbc, 0xd9, 0xee, 0xe0,
+  0x85, 0x5e, 0x98, 0x23, 0xf9, 0x19, 0x60, 0xf9, 0x7e, 0x8d, 0x61, 0xa0,
+  0x7c, 0xe1, 0x84, 0xf2, 0x7a, 0xb8, 0xbe, 0x8e, 0x81, 0x9e, 0x87, 0x20,
+  0x32, 0xf3, 0x8c, 0xb4, 0x2c, 0x4d, 0xc8, 0x50, 0x9b, 0xa5, 0x9c, 0x27,
+  0x02, 0xd6, 0x7f, 0x2a, 0xaf, 0x46, 0x65, 0xd0, 0x6a, 0xae, 0xfa, 0x53,
+  0x37, 0x6c, 0x49, 0xb9, 0x4d, 0xcd, 0x6c, 0x6b, 0xa7, 0x2d, 0x66, 0x32,
+  0xb4, 0xf5, 0x41, 0xd5, 0x18, 0xc4, 0xfd, 0xbe, 0x8a, 0x47, 0x11, 0x50,
+  0x3d, 0x97, 0x64, 0xda, 0x5a, 0x27, 0x18, 0x60, 0x78, 0x80, 0x86, 0x8a,
+  0x2a, 0x72, 0x40, 0x89
+};
+
+namespace MutationOfJB {
+
+uint32 EncryptedFile::read(void *dataPtr, uint32 dataSize) {
+	uint8 xorPos = pos() % 256;
+	const uint32 readBytes = Common::File::read(dataPtr, dataSize);
+
+	for (uint32 i = 0; i < readBytes; ++i) {
+		static_cast<uint8 *>(dataPtr)[i] ^= XOR_TABLE[xorPos];
+		xorPos++;
+	}
+
+	return readBytes;
+}
+
+}
diff --git a/engines/mutationofjb/encryptedfile.h b/engines/mutationofjb/encryptedfile.h
new file mode 100644
index 0000000..69f6f62
--- /dev/null
+++ b/engines/mutationofjb/encryptedfile.h
@@ -0,0 +1,32 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/file.h"
+
+namespace MutationOfJB {
+
+class EncryptedFile : public Common::File {
+public:
+	virtual uint32 read(void *dataPtr, uint32 dataSize) override;
+};
+
+}
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 2e28d41..fcb9478 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -1,8 +1,10 @@
 MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
+	encryptedfile.o \
 	detection.o \
-	mutationofjb.o
+	mutationofjb.o \
+	room.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_MUTATIONOFJB), DYNAMIC_PLUGIN)
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index f50ba70..0406864 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -24,15 +24,23 @@
 
 #include "common/debug.h"
 #include "common/error.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "graphics/screen.h"
 
 #include "engines/util.h"
 
 #include "mutationofjb/mutationofjb.h"
+#include "mutationofjb/room.h"
 
 namespace MutationOfJB {
 
 MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
-: Engine(syst), _console(nullptr) {
+: Engine(syst),
+  _console(nullptr),
+  _room(nullptr),
+  _screen(nullptr)
+{
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -41,10 +49,20 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 }
 
 Common::Error MutationOfJBEngine::run() {
-	initGraphics(320, 200, false);
-	_console = new Console(this);
 	debug("MutationOfJBEngine::run");
 
+	initGraphics(320, 200);
+	_console = new Console(this);
+	_screen = new Graphics::Screen;
+	_room = new Room(_screen);
+	_room->load(13, false);
+
+	while(!shouldQuit()) {
+		Common::Event event;
+		while (_eventMan->pollEvent(event));
+		_system->delayMillis(100);
+	}
+
 	return Common::kNoError;
 }
 }
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 1c87fe4..815baf8 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -26,9 +26,14 @@
 #include "engines/engine.h"
 #include "gui/debugger.h"
 
+namespace Graphics {
+	class Screen;
+}
+
 namespace MutationOfJB {
 
 class Console;
+class Room;
 
 class MutationOfJBEngine : public Engine {
 public:
@@ -38,6 +43,8 @@ public:
 	virtual Common::Error run();
 private:
 	Console *_console;
+	Room *_room;
+	Graphics::Screen *_screen;
 };
 
 class Console : public GUI::Debugger {
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
new file mode 100644
index 0000000..e927971
--- /dev/null
+++ b/engines/mutationofjb/room.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/room.h"
+#include "mutationofjb/encryptedfile.h"
+#include "graphics/screen.h"
+#include "engines/engine.h"
+#include "common/translation.h"
+
+namespace MutationOfJB {
+
+Room::Room(Graphics::Screen *screen) : _screen(screen) {}
+
+bool Room::load(uint8 roomNumber, bool roomB) {
+	EncryptedFile file;
+	Common::String fileName = Common::String::format("room%d%s.dat", roomNumber, roomB ? "b" : "");
+
+	file.open(fileName);
+
+	if (!file.isOpen()) {
+		Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName.c_str());
+		GUIErrorMessage(errorMessage);
+		warning("%s", errorMessage.c_str());
+
+		return false;
+	}
+
+	file.seek(0x80);
+
+	while (!file.eos()) {
+		// Record.
+		const uint32 length = file.readUint32LE();
+		uint8 info[4] = {0};
+		file.read(info, 4);
+
+		// TODO Find out what these are.
+		uint32 unknown;
+		unknown = file.readUint32LE();
+		unknown = file.readUint32LE();
+
+		// Subrecords.
+		if (info[0] == 0xFA && info[1] == 0xF1) {
+			for (int i = 0; i < info[2]; ++i) {
+				const uint32 subLength = file.readUint32LE();
+				const uint16 type = file.readUint16LE();
+
+				if (type == 0x0B) {
+					loadPalette(file);
+				} else if (type == 0x0F) {
+					loadBackground(file, subLength - 6);
+				}
+			}
+		}
+	}
+
+	file.close();
+
+	return true;
+}
+
+void Room::loadPalette(EncryptedFile &file) {
+	uint32 unknown;
+
+	// TODO Find out what this is.
+	unknown = file.readUint32LE();
+
+	uint8 palette[PALETTE_SIZE];
+	file.read(palette, PALETTE_SIZE);
+
+	for (int j = 0; j < PALETTE_SIZE; ++j) {
+		palette[j] <<= 2; // Uses 6-bit colors.
+	}
+
+	_screen->setPalette(palette);
+}
+
+void Room::loadBackground(EncryptedFile &file, uint32 size) {
+	_screen->clear();
+
+	uint8 * const pixels = static_cast<uint8 *>(_screen->getPixels());
+	uint8 *ptr = pixels;
+	uint32 readBytes = 0;
+
+	while (readBytes != size) {
+		uint8 no = file.readByte();
+		readBytes++;
+		while (no--) {
+			uint8 n = file.readByte();
+			readBytes++;
+			if (n < 0x80) {
+				// RLE - Copy color n times.
+				uint8 color = file.readByte();
+				readBytes++;
+				while(n--) {
+					*ptr++ = color;
+				}
+			} else {
+				// Take next 0x100 - n bytes as they are.
+				const uint32 rawlen = 0x100 - n;
+				file.read(ptr, rawlen);
+				readBytes += rawlen;
+				ptr += rawlen;
+			}
+		}
+	}
+
+	_screen->update();
+}
+
+}
diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h
new file mode 100644
index 0000000..7158580
--- /dev/null
+++ b/engines/mutationofjb/room.h
@@ -0,0 +1,49 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_ROOM_H
+#define MUTATIONOFJB_ROOM_H
+
+#include "common/scummsys.h"
+
+namespace Graphics {
+	class Screen;
+}
+
+namespace MutationOfJB {
+
+class EncryptedFile;
+
+class Room {
+public:
+	Room(Graphics::Screen *screen);
+	bool load(uint8 roomNumber, bool roomB);
+private:
+	void loadPalette(EncryptedFile &file);
+	void loadBackground(EncryptedFile &file, uint32 size);
+
+	Graphics::Screen *_screen;
+};
+
+}
+
+#endif


Commit: 3696865e6910067e011500d09e0d0e44bab796c1
    https://github.com/scummvm/scummvm/commit/3696865e6910067e011500d09e0d0e44bab796c1
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Load initial game state and allow room change

Changed paths:
  A engines/mutationofjb/game.cpp
  A engines/mutationofjb/game.h
  A engines/mutationofjb/util.cpp
  A engines/mutationofjb/util.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
new file mode 100644
index 0000000..581077a
--- /dev/null
+++ b/engines/mutationofjb/game.cpp
@@ -0,0 +1,152 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/game.h"
+#include "common/stream.h"
+#include "common/util.h"
+
+namespace MutationOfJB {
+
+static bool readString(Common::ReadStream &stream, char *str)
+{
+	char buf[MAX_STR_LENGTH];
+	memset(str, 0, MAX_STR_LENGTH + 1);
+
+	uint8 len = stream.readByte();
+	stream.read(buf, MAX_STR_LENGTH);
+
+	len = MIN(len, MAX_STR_LENGTH);
+	memcpy(str, buf, len);
+
+	return true;
+}
+
+bool Door::loadFromStream(Common::ReadStream &stream) {
+	readString(stream, _name);
+
+	_destSceneId = stream.readByte();
+	_destX = stream.readUint16LE();
+	_destY = stream.readUint16LE();
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_width = stream.readUint16LE();
+	_height = stream.readByte();
+	_walkToX = stream.readUint16LE();
+	_walkToY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Object::loadFromStream(Common::ReadStream &stream) {
+	_AC = stream.readByte();
+	_FA = stream.readByte();
+	_FR = stream.readByte();
+	_NA = stream.readByte();
+	_FS = stream.readByte();
+	_unknown = stream.readByte();
+	_CA = stream.readByte();
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_XL = stream.readUint16LE();
+	_YL = stream.readByte();
+	_WX = stream.readUint16LE();
+	_WY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Static::loadFromStream(Common::ReadStream &stream) {
+	_active = stream.readByte();
+	readString(stream, _name);
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_width = stream.readUint16LE();
+	_height = stream.readByte();
+	_walkToX = stream.readUint16LE();
+	_walkToY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Bitmap::loadFromStream(Common::ReadStream &stream) {
+	_unknown = stream.readByte();
+	_isVisible = stream.readByte();
+	_x1 = stream.readUint16LE();
+	_y1 = stream.readByte();
+	_x2 = stream.readUint16LE();
+	_y2 = stream.readByte();
+
+	return true;
+}
+
+bool SceneInfo::loadFromStream(Common::ReadStream &stream) {
+	int i;
+
+	_startup = stream.readByte();
+	_unknown001 = stream.readByte();
+	_unknown002 = stream.readByte();
+	_unknown003 = stream.readByte();
+	_DL = stream.readByte();
+
+	_noDoors = stream.readByte();
+	for (i = 0; i < ARRAYSIZE(_doors); ++i) {
+		_doors[i].loadFromStream(stream);
+	}
+
+	_noObjects = stream.readByte();
+	for (i = 0; i < ARRAYSIZE(_objects); ++i) {
+		_objects[i].loadFromStream(stream);
+	}
+
+	_noStatics = stream.readByte();
+	for (i = 0; i < ARRAYSIZE(_statics); ++i) {
+		_statics[i].loadFromStream(stream);
+	}
+
+	for (i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
+		_bitmaps[i].loadFromStream(stream);
+	}
+
+	_obstacleY1 = stream.readByte();
+	_unknown386 = stream.readByte();
+	_palRotStart = stream.readByte();
+	_palRotEnd = stream.readByte();
+	_palRotPeriod = stream.readByte();
+	stream.read(_unknown38A, 80);
+
+	return true;
+}
+
+GameData::GameData() : _currentScene(0) {}
+
+bool GameData::loadFromStream(Common::ReadStream &stream) {
+	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
+		_scenes[i].loadFromStream(stream);
+	}
+
+	return true;
+}
+
+}
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
new file mode 100644
index 0000000..d8f2079
--- /dev/null
+++ b/engines/mutationofjb/game.h
@@ -0,0 +1,134 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+namespace Common
+{
+	class ReadStream;
+}
+
+namespace MutationOfJB
+{
+
+static const uint8 MAX_STR_LENGTH = 0x14;
+
+struct Door {
+	char _name[MAX_STR_LENGTH + 1];
+	uint8  _destSceneId;
+	uint16 _destX;
+	uint16 _destY;
+	uint16 _x;
+	uint8  _y;
+	uint16 _width;
+	uint8  _height;
+	uint16 _walkToX;
+	uint8  _walkToY;
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Object {
+	uint8  _AC;
+	uint8  _FA;
+	uint8  _FR;
+	uint8  _NA;
+	uint8  _FS;
+	uint8  _unknown;
+	uint8  _CA;
+	uint16 _x;
+	uint8  _y;
+	uint16 _XL;
+	uint8  _YL;
+	uint16 _WX;
+	uint8  _WY;
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Static {
+	uint8  _active;
+	char _name[MAX_STR_LENGTH + 1];
+	uint16 _x;
+	uint8  _y;
+	uint16 _width;
+	uint8  _height;
+	uint16 _walkToX;
+	uint8  _walkToY;
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Bitmap {
+	uint8  _unknown;
+	uint8  _isVisible;
+	uint16 _x1;
+	uint8  _y1;
+	uint16 _x2;
+	uint8  _y2;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+
+struct SceneInfo {
+	uint8 _startup;
+	uint8 _unknown001;
+	uint8 _unknown002;
+	uint8 _unknown003;
+	uint8 _DL;
+
+	uint8 _noDoors;
+	Door _doors[5];
+
+	uint8 _noObjects;
+	Object _objects[9];
+
+	uint8 _noStatics;
+	Static _statics[15];
+
+	Bitmap _bitmaps[10];
+
+	uint8 _obstacleY1;
+	uint8 _unknown386;
+	uint8 _palRotStart;
+	uint8 _palRotEnd;
+	uint8 _palRotPeriod;
+	uint8 _unknown38A[80];
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct GameData
+{
+	GameData();
+
+	SceneInfo _scenes[45];
+	uint8 _currentScene;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+}
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index fcb9478..7baea82 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -1,10 +1,12 @@
 MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
-	encryptedfile.o \
 	detection.o \
+	encryptedfile.o \
+	game.o \
 	mutationofjb.o \
-	room.o
+	room.o \
+	util.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_MUTATIONOFJB), DYNAMIC_PLUGIN)
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 0406864..dc7f518 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -27,11 +27,15 @@
 #include "common/system.h"
 #include "common/events.h"
 #include "graphics/screen.h"
+#include "graphics/cursorman.h"
 
 #include "engines/util.h"
 
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/room.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/util.h"
 
 namespace MutationOfJB {
 
@@ -48,19 +52,73 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 	debug("MutationOfJBEngine::~MutationOfJBEngine");
 }
 
+bool MutationOfJBEngine::loadGameData(bool partB) {
+	EncryptedFile file;
+	const char *fileName = !partB ? "startup.dat" : "startupb.dat";
+	file.open(fileName);
+	if (!file.isOpen()) {
+		reportFileMissingError(fileName);
+		return false;
+	}
+
+	_gameData->loadFromStream(file);
+
+	file.close();
+
+	return true;
+}
+
+void MutationOfJBEngine::setupCursor()
+{
+	const uint8 white[] = {0xFF, 0xFF, 0xFF};
+	const uint8 cursor[] = {0xFF};
+
+	_screen->setPalette(white, 0xFF, 1);
+
+	CursorMan.disableCursorPalette(true);
+	CursorMan.pushCursor(cursor, 1, 1, 0, 0, 0);
+	CursorMan.showMouse(true);
+}
+
 Common::Error MutationOfJBEngine::run() {
 	debug("MutationOfJBEngine::run");
 
 	initGraphics(320, 200);
+
 	_console = new Console(this);
 	_screen = new Graphics::Screen;
+	setupCursor();
+
+	_gameData = new GameData;
+	_gameData->_currentScene = 13;
+	loadGameData(false);
+
 	_room = new Room(_screen);
-	_room->load(13, false);
+	_room->load(_gameData->_currentScene, false);
 
 	while(!shouldQuit()) {
 		Common::Event event;
-		while (_eventMan->pollEvent(event));
-		_system->delayMillis(100);
+		while (_eventMan->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_LBUTTONDOWN:
+			{
+				const SceneInfo &sceneInfo = _gameData->_scenes[_gameData->_currentScene - 1];
+				for (int i = 0; i < MIN(ARRAYSIZE(sceneInfo._doors), (int) sceneInfo._noDoors); ++i) {
+					const Door &door = sceneInfo._doors[i];
+					if ((event.mouse.x >= door._x) && (event.mouse.x < door._x + door._width) && (event.mouse.y >= door._y) && (event.mouse.y < door._y + door._height)) {
+						_gameData->_currentScene = door._destSceneId;
+						_room->load(_gameData->_currentScene, false);
+					}
+				}
+				break;
+			}
+			default:
+				break;
+			}
+		}
+
+		_system->delayMillis(40);
+		_screen->update();
 	}
 
 	return Common::kNoError;
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 815baf8..75bd678 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -34,6 +34,7 @@ namespace MutationOfJB {
 
 class Console;
 class Room;
+struct GameData;
 
 class MutationOfJBEngine : public Engine {
 public:
@@ -42,8 +43,12 @@ public:
 
 	virtual Common::Error run();
 private:
+	bool loadGameData(bool partB);
+	void setupCursor();
+
 	Console *_console;
 	Room *_room;
+	GameData *_gameData;
 	Graphics::Screen *_screen;
 };
 
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index e927971..298a54b 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -22,9 +22,9 @@
 
 #include "mutationofjb/room.h"
 #include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/util.h"
+#include "common/str.h"
 #include "graphics/screen.h"
-#include "engines/engine.h"
-#include "common/translation.h"
 
 namespace MutationOfJB {
 
@@ -37,9 +37,7 @@ bool Room::load(uint8 roomNumber, bool roomB) {
 	file.open(fileName);
 
 	if (!file.isOpen()) {
-		Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName.c_str());
-		GUIErrorMessage(errorMessage);
-		warning("%s", errorMessage.c_str());
+		reportFileMissingError(fileName.c_str());
 
 		return false;
 	}
@@ -90,7 +88,7 @@ void Room::loadPalette(EncryptedFile &file) {
 		palette[j] <<= 2; // Uses 6-bit colors.
 	}
 
-	_screen->setPalette(palette);
+	_screen->setPalette(palette, 0x00, 0xBF); // Load only 0xBF colors.
 }
 
 void Room::loadBackground(EncryptedFile &file, uint32 size) {
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
new file mode 100644
index 0000000..41d2a3a
--- /dev/null
+++ b/engines/mutationofjb/util.cpp
@@ -0,0 +1,35 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/util.h"
+#include "common/str.h"
+#include "common/translation.h"
+#include "engines/engine.h"
+
+namespace MutationOfJB {
+	void reportFileMissingError(const char *fileName) {
+		Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName);
+		GUIErrorMessage(errorMessage);
+		warning("%s", errorMessage.c_str());
+	}
+}
+
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
new file mode 100644
index 0000000..3059d51
--- /dev/null
+++ b/engines/mutationofjb/util.h
@@ -0,0 +1,25 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+namespace MutationOfJB {
+	void reportFileMissingError(const char *fileName);
+}


Commit: 356a6809c31224f4ed2bfcc5e6bacd131f144026
    https://github.com/scummvm/scummvm/commit/356a6809c31224f4ed2bfcc5e6bacd131f144026
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix loading room 11 (and possibly some others).

Changed paths:
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 298a54b..95a1a49 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -24,6 +24,7 @@
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
+#include "common/translation.h"
 #include "graphics/screen.h"
 
 namespace MutationOfJB {
@@ -65,6 +66,9 @@ bool Room::load(uint8 roomNumber, bool roomB) {
 					loadPalette(file);
 				} else if (type == 0x0F) {
 					loadBackground(file, subLength - 6);
+				} else {
+					debug(_("Unsupported record type %02X."), type);
+					file.seek(subLength - 6, SEEK_CUR);
 				}
 			}
 		}
@@ -88,7 +92,7 @@ void Room::loadPalette(EncryptedFile &file) {
 		palette[j] <<= 2; // Uses 6-bit colors.
 	}
 
-	_screen->setPalette(palette, 0x00, 0xBF); // Load only 0xBF colors.
+	_screen->setPalette(palette, 0x00, 0xC0); // Load only 0xC0 colors.
 }
 
 void Room::loadBackground(EncryptedFile &file, uint32 size) {
@@ -97,8 +101,15 @@ void Room::loadBackground(EncryptedFile &file, uint32 size) {
 	uint8 * const pixels = static_cast<uint8 *>(_screen->getPixels());
 	uint8 *ptr = pixels;
 	uint32 readBytes = 0;
+	uint32 lines = 0;
 
 	while (readBytes != size) {
+		if (lines == 200) {
+			// Some background files have an unknown byte at the end,
+			// so break when we encounter all 200 lines.
+			break;
+		}
+
 		uint8 no = file.readByte();
 		readBytes++;
 		while (no--) {
@@ -119,6 +130,7 @@ void Room::loadBackground(EncryptedFile &file, uint32 size) {
 				ptr += rawlen;
 			}
 		}
+		lines++;
 	}
 
 	_screen->update();


Commit: f7d5a825a053199ddbf4d7c564e84e9f9709d958
    https://github.com/scummvm/scummvm/commit/f7d5a825a053199ddbf4d7c564e84e9f9709d958
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Start implementation of ATN scripts (IF command).

Changed paths:
  A engines/mutationofjb/commands/command.cpp
  A engines/mutationofjb/commands/command.h
  A engines/mutationofjb/commands/conditionalcommand.cpp
  A engines/mutationofjb/commands/conditionalcommand.h
  A engines/mutationofjb/commands/ifcommand.cpp
  A engines/mutationofjb/commands/ifcommand.h
  A engines/mutationofjb/commands/seqcommand.cpp
  A engines/mutationofjb/commands/seqcommand.h
  A engines/mutationofjb/script.cpp
  A engines/mutationofjb/script.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
new file mode 100644
index 0000000..d0b3f82
--- /dev/null
+++ b/engines/mutationofjb/commands/command.cpp
@@ -0,0 +1,33 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/command.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+Command::~Command() {}
+
+SeqCommand *Command::asSeqCommand() {
+	return nullptr;
+}
+
+}
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
new file mode 100644
index 0000000..beae9d2
--- /dev/null
+++ b/engines/mutationofjb/commands/command.h
@@ -0,0 +1,58 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_COMMAND_H
+#define MUTATIONOFJB_COMMAND_H
+
+namespace Common {
+	class String;
+}
+
+namespace MutationOfJB {
+
+class GameData;
+class SeqCommand;
+class IfCommand;
+class CallMacroCommand;
+class ScriptParseContext;
+class Command;
+
+typedef bool (*CommandParseFunc)(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
+
+class Command {
+public:
+	enum ExecuteResult {
+		None,
+		Finished,
+		InProgress
+	};
+
+	virtual ~Command();
+
+	virtual ExecuteResult execute(GameData &gameData) = 0;
+	virtual Command *next() const = 0;
+
+	virtual SeqCommand *asSeqCommand();
+};
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
new file mode 100644
index 0000000..3118e6d
--- /dev/null
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -0,0 +1,41 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+ConditionalCommand::ConditionalCommand() :
+	_trueCommand(nullptr),
+	_falseCommand(nullptr),
+	_cachedResult(false)
+{}
+
+Command *ConditionalCommand::next() const {
+	if (_cachedResult) {
+		return _trueCommand;
+	} else {
+		return _falseCommand;
+	}
+}
+};
diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
new file mode 100644
index 0000000..e355662
--- /dev/null
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -0,0 +1,41 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/command.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class ConditionalCommand : public Command {
+public:
+	ConditionalCommand();
+	void setTrueCommand(Command *command);
+	void setFalseCommand(Command *command);
+
+	virtual Command *next() const override;
+protected:
+	Command *_trueCommand;
+	Command *_falseCommand;
+	bool _cachedResult;
+};
+
+}
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
new file mode 100644
index 0000000..18b8081
--- /dev/null
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -0,0 +1,88 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/ifcommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+#include "common/str.h"
+#include "common/translation.h"
+
+namespace MutationOfJB {
+
+bool IfCommand::ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command)
+{
+	// IFtss oo val!
+	// <t>   1B Tag.
+	// <ss>  2B Scene.
+	// <oo>  2B Object ID.
+	// <val> VL Value.
+	// !     1B Negation (optional).
+
+	if (line.size() < 10) {
+		return false;
+	}
+	
+	if (strncmp(line.c_str(), "IF", 2) != 0) {
+		return false;
+	}
+
+	const char *const cstr = line.c_str();
+	const char tag = cstr[2];
+	const uint8 sceneId = atoi(cstr + 3);
+	const uint8 objectId = atoi(cstr + 6);
+	const uint8 value = atoi(cstr + 9);
+	const bool negative = (line.lastChar() == '!');
+
+	IfCommand *ifCommand = new IfCommand(sceneId, objectId, value, negative);
+
+	command = ifCommand;
+	parseContext.addConditionalCommand(ifCommand, tag);
+	return true;
+}
+
+IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative) :
+	_sceneId(sceneId),
+	_objectId(objectId),
+	_value(value),
+	_negative(negative)
+{}
+
+Command::ExecuteResult IfCommand::execute(GameData &gameData) {
+	Scene* const scene = gameData.getScene(_sceneId);
+	if (!scene) {
+		return Finished;
+	}
+
+	Object* const object = scene->getObject(_objectId);
+	if (!object) {
+		return Finished;
+	}
+
+	_cachedResult = (object->_WX == _value);
+	if (_negative) {
+		_cachedResult = !_cachedResult;
+	}
+
+	return Finished;
+}
+}
+
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
new file mode 100644
index 0000000..d33f34f
--- /dev/null
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -0,0 +1,52 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_IFCOMMAND_H
+#define MUTATIONOFJB_IFCOMMAND_H
+
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class ScriptParseContext;
+
+class IfCommand : public ConditionalCommand {
+public:
+	static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
+
+	IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
+	
+	virtual ExecuteResult execute(GameData &gameData) override;
+
+private:
+	uint8 _sceneId;
+	uint8 _objectId;
+	uint16 _value;
+	bool _negative;
+
+	bool _cachedResult;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
new file mode 100644
index 0000000..ab98497
--- /dev/null
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -0,0 +1,40 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "seqcommand.h"
+
+namespace MutationOfJB {
+
+void SeqCommand::setNextCommand(Command *nextCommand)
+{
+	_nextCommand = nextCommand;
+}
+
+Command *SeqCommand::next() const {
+	return _nextCommand;
+}
+
+SeqCommand *SeqCommand::asSeqCommand() {
+	return this;
+}
+
+}
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
new file mode 100644
index 0000000..b247fb2
--- /dev/null
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -0,0 +1,44 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_SEQCOMMAND_H
+#define MUTATIONOFJB_SEQCOMMAND_H 
+
+#include "mutationofjb/commands/command.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class SeqCommand : public Command {
+public:
+	void setNextCommand(Command *nextCommand);
+	virtual Command *next() const override;
+	virtual SeqCommand *asSeqCommand();
+
+private:
+	Command *_nextCommand;
+};
+
+}
+
+#endif
+
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 581077a..740fb1c 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -23,6 +23,7 @@
 #include "mutationofjb/game.h"
 #include "common/stream.h"
 #include "common/util.h"
+#include "common/translation.h"
 
 namespace MutationOfJB {
 
@@ -101,7 +102,7 @@ bool Bitmap::loadFromStream(Common::ReadStream &stream) {
 	return true;
 }
 
-bool SceneInfo::loadFromStream(Common::ReadStream &stream) {
+bool Scene::loadFromStream(Common::ReadStream &stream) {
 	int i;
 
 	_startup = stream.readByte();
@@ -111,16 +112,19 @@ bool SceneInfo::loadFromStream(Common::ReadStream &stream) {
 	_DL = stream.readByte();
 
 	_noDoors = stream.readByte();
+	_noDoors = MIN(_noDoors, (uint8) ARRAYSIZE(_doors));
 	for (i = 0; i < ARRAYSIZE(_doors); ++i) {
 		_doors[i].loadFromStream(stream);
 	}
 
 	_noObjects = stream.readByte();
+	_noObjects = MIN(_noObjects, (uint8) ARRAYSIZE(_objects));
 	for (i = 0; i < ARRAYSIZE(_objects); ++i) {
 		_objects[i].loadFromStream(stream);
 	}
 
 	_noStatics = stream.readByte();
+	_noStatics = MIN(_noStatics, (uint8) ARRAYSIZE(_statics));
 	for (i = 0; i < ARRAYSIZE(_statics); ++i) {
 		_statics[i].loadFromStream(stream);
 	}
@@ -139,8 +143,27 @@ bool SceneInfo::loadFromStream(Common::ReadStream &stream) {
 	return true;
 }
 
+Object *Scene::getObject(uint8 objectId) {
+	if (objectId == 0 || objectId > _noObjects) {
+		warning(_("Object %d does not exist"), objectId);
+		return nullptr;
+	}
+
+	return &_objects[objectId - 1];
+}
+
 GameData::GameData() : _currentScene(0) {}
 
+Scene *GameData::getScene(uint8 sceneId)
+{
+	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
+		warning(_("Scene %d does not exist"), sceneId);
+		return nullptr;
+	}
+
+	return &_scenes[sceneId - 1];
+}
+
 bool GameData::loadFromStream(Common::ReadStream &stream) {
 	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
 		_scenes[i].loadFromStream(stream);
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index d8f2079..d49a966 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -22,13 +22,11 @@
 
 #include "common/scummsys.h"
 
-namespace Common
-{
+namespace Common {
 	class ReadStream;
 }
 
-namespace MutationOfJB
-{
+namespace MutationOfJB {
 
 static const uint8 MAX_STR_LENGTH = 0x14;
 
@@ -93,7 +91,10 @@ struct Bitmap {
 };
 
 
-struct SceneInfo {
+struct Scene {
+
+	Object *getObject(uint8 objectId);
+
 	uint8 _startup;
 	uint8 _unknown001;
 	uint8 _unknown002;
@@ -123,12 +124,16 @@ struct SceneInfo {
 
 struct GameData
 {
+public:
 	GameData();
+	Scene *getScene(uint8 sceneId);
+
+	bool loadFromStream(Common::ReadStream &stream);
 
-	SceneInfo _scenes[45];
 	uint8 _currentScene;
+private:
+	Scene _scenes[45];
 
-	bool loadFromStream(Common::ReadStream &stream);
 };
 
 }
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 7baea82..e6c539a 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -1,11 +1,16 @@
 MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
+	commands/command.o \
+	commands/conditionalcommand.o \
+	commands/ifcommand.o \
+	commands/seqcommand.o \
 	detection.o \
 	encryptedfile.o \
 	game.o \
 	mutationofjb.o \
 	room.o \
+	script.o \
 	util.o
 
 # This module can be built as a plugin
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index dc7f518..cdefbb9 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -102,12 +102,14 @@ Common::Error MutationOfJBEngine::run() {
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 			{
-				const SceneInfo &sceneInfo = _gameData->_scenes[_gameData->_currentScene - 1];
-				for (int i = 0; i < MIN(ARRAYSIZE(sceneInfo._doors), (int) sceneInfo._noDoors); ++i) {
-					const Door &door = sceneInfo._doors[i];
-					if ((event.mouse.x >= door._x) && (event.mouse.x < door._x + door._width) && (event.mouse.y >= door._y) && (event.mouse.y < door._y + door._height)) {
-						_gameData->_currentScene = door._destSceneId;
-						_room->load(_gameData->_currentScene, false);
+				const Scene* const scene = _gameData->getScene(_gameData->_currentScene);
+				if (scene) {
+					for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
+						const Door &door = scene->_doors[i];
+						if ((event.mouse.x >= door._x) && (event.mouse.x < door._x + door._width) && (event.mouse.y >= door._y) && (event.mouse.y < door._y + door._height)) {
+							_gameData->_currentScene = door._destSceneId;
+							_room->load(_gameData->_currentScene, false);
+						}
 					}
 				}
 				break;
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 95a1a49..eae430a 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -132,6 +132,9 @@ void Room::loadBackground(EncryptedFile &file, uint32 size) {
 		}
 		lines++;
 	}
+	if (readBytes < size) {
+		file.seek(size - readBytes, SEEK_CUR);
+	}
 
 	_screen->update();
 }
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
new file mode 100644
index 0000000..0280c86
--- /dev/null
+++ b/engines/mutationofjb/script.cpp
@@ -0,0 +1,75 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "script.h"
+
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/stream.h"
+#include "mutationofjb/commands/command.h"
+
+namespace MutationOfJB {
+
+static CommandParseFunc* getParseFuncs() {
+	static CommandParseFunc funcs[] = {
+		nullptr
+	};
+
+	return funcs;
+}
+
+
+ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) : _stream(stream) {}
+
+bool ScriptParseContext::readLine(Common::String &line) {
+	do {
+		Common::String str = _stream.readLine();
+		if (str.empty() || str[0] != '.') {
+			line = str;
+			if (line[0] == '*') {
+				line.deleteChar(0);
+			}
+			return true;
+		}
+	} while(_stream.eos());
+
+	return false;
+}
+
+void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char tag) {
+	ConditionalCommandInfo cmi = {command, tag};
+	_pendingCondCommands.push_back(cmi);
+}
+
+bool Script::loadFromStream(Common::SeekableReadStream &stream) {
+
+	CommandParseFunc * const parseFuncs = getParseFuncs();
+
+	ScriptParseContext parseCtx(stream);
+
+	Common::HashMap<Common::String, Command *> macros;
+	Common::HashMap<Common::String, Command *> labels;
+
+	return true;
+}
+
+}
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
new file mode 100644
index 0000000..9c0f0f3
--- /dev/null
+++ b/engines/mutationofjb/script.h
@@ -0,0 +1,64 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_SCRIPT_H
+#define MUTATIONOFJB_SCRIPT_H 
+
+#include "common/array.h"
+
+namespace Common {
+	class SeekableReadStream;
+	class String;
+}
+
+namespace MutationOfJB {
+
+class ConditionalCommand;
+
+class ScriptParseContext
+{
+public:
+	ScriptParseContext(Common::SeekableReadStream &stream);
+	bool readLine(Common::String &line);
+	void addConditionalCommand(ConditionalCommand *command, char tag);
+	//void setLastIfCommand(IfCommand *command);
+
+private:
+	Common::SeekableReadStream &_stream;
+
+	struct ConditionalCommandInfo {
+		ConditionalCommand *command;
+		char tag;
+	};
+	Common::Array<ConditionalCommandInfo> _pendingCondCommands;
+};
+
+class Script {
+public:
+	bool loadFromStream(Common::SeekableReadStream &stream);
+private:
+
+};
+
+}
+
+#endif


Commit: 3672ea55720666acfd64b6df01b35d0727d6be78
    https://github.com/scummvm/scummvm/commit/3672ea55720666acfd64b6df01b35d0727d6be78
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Continue implementation of if/else script commands.

Changed paths:
  A engines/mutationofjb/commands/endblockcommand.cpp
  A engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/commands/command.cpp
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/conditionalcommand.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifcommand.h
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
index d0b3f82..af4c28e 100644
--- a/engines/mutationofjb/commands/command.cpp
+++ b/engines/mutationofjb/commands/command.cpp
@@ -24,6 +24,10 @@
 #include "common/scummsys.h"
 
 namespace MutationOfJB {
+
+void CommandParser::transition(ScriptParseContext &, Command *, Command *) {}
+CommandParser::~CommandParser() {}
+
 Command::~Command() {}
 
 SeqCommand *Command::asSeqCommand() {
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index beae9d2..ab0ebc6 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -38,6 +38,15 @@ class Command;
 
 typedef bool (*CommandParseFunc)(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
 
+class CommandParser {
+public:
+	virtual ~CommandParser();
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) = 0;
+
+	/* Old command - created by this parser. */
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+};
+
 class Command {
 public:
 	enum ExecuteResult {
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index 3118e6d..13ea674 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -31,6 +31,23 @@ ConditionalCommand::ConditionalCommand() :
 	_cachedResult(false)
 {}
 
+
+Command *ConditionalCommand::getTrueCommand() const {
+	return _trueCommand;
+}
+
+Command *ConditionalCommand::getFalseCommand() const {
+	return _falseCommand;
+}
+
+void ConditionalCommand::setTrueCommand(Command *command) {
+	_trueCommand = command;
+}
+
+void ConditionalCommand::setFalseCommand(Command *command) {
+	_falseCommand = command;
+}
+
 Command *ConditionalCommand::next() const {
 	if (_cachedResult) {
 		return _trueCommand;
@@ -38,4 +55,4 @@ Command *ConditionalCommand::next() const {
 		return _falseCommand;
 	}
 }
-};
+}
diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
index e355662..27a8ac1 100644
--- a/engines/mutationofjb/commands/conditionalcommand.h
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -28,6 +28,10 @@ namespace MutationOfJB {
 class ConditionalCommand : public Command {
 public:
 	ConditionalCommand();
+
+	Command *getTrueCommand() const;
+	Command *getFalseCommand() const;
+
 	void setTrueCommand(Command *command);
 	void setFalseCommand(Command *command);
 
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
new file mode 100644
index 0000000..218be1a
--- /dev/null
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -0,0 +1,108 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/endblockcommand.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/str.h"
+#include "common/debug.h"
+
+namespace MutationOfJB {
+
+bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *& command) {
+	if (line.empty()) {
+		return false;
+	}
+
+	const char firstChar = line.firstChar();
+	if (firstChar != '#' && firstChar != '=' && firstChar != '-') {
+		return false;
+	}
+
+	// This is the start or end of section/block.
+
+	if (line.size() >= 4 && (line.hasPrefix("#L ") || line.hasPrefix("-L "))) {
+		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Look, line.c_str() + 3, "", firstChar == '#'};
+		parseCtx._actionInfos.push_back(ai);
+		debug("# Look: %s", line.c_str() + 3);
+	} else if (line.size() >= 4 && (line.hasPrefix("#W ") || line.hasPrefix("-W "))) {
+		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Walk, line.c_str() + 3, "", firstChar == '#'};
+		parseCtx._actionInfos.push_back(ai);
+	} else if (line.size() >= 4 && (line.hasPrefix("#T ") || line.hasPrefix("-T "))) {
+		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Talk, line.c_str() + 3, "", firstChar == '#'};
+		parseCtx._actionInfos.push_back(ai);
+	} else if (line.size() >= 4 && (line.hasPrefix("#U ") || line.hasPrefix("-U "))) {
+		int secondObjPos = -1;
+		for (int i = 3; i < (int) line.size(); ++i) {
+			if (line[i] == ' ') {
+				secondObjPos = i + 1;
+				break;
+			}
+		}
+		ScriptParseContext::ActionInfo ai = {
+			ScriptParseContext::Talk,
+			line.c_str() + 3,
+			(secondObjPos != -1) ? line.c_str() + secondObjPos : "",
+			firstChar == '#'
+		};
+		parseCtx._actionInfos.push_back(ai); 
+	} else if ((line.hasPrefix("#ELSE") || line.hasPrefix("=ELSE"))) {
+		_elseFound = true;
+		_ifTag = 0;
+		if (line.size() >= 6) {
+			_ifTag = line[5];
+		}
+	}
+
+	command = new EndBlockCommand();
+
+	return true;
+}
+
+void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand) {
+	if (_elseFound) {
+		if (newCommand) {
+			ScriptParseContext::ConditionalCommandInfos::iterator it = parseCtx._pendingCondCommands.begin();
+
+			while (it != parseCtx._pendingCondCommands.end()) {
+				if (it->_tag == _ifTag) {
+					it->_command->setFalseCommand(newCommand);
+					it = parseCtx._pendingCondCommands.erase(it);
+				} else {
+					++it;
+				}
+			}
+		}
+
+		_elseFound = false;
+		_ifTag = 0;
+	}
+}
+
+Command::ExecuteResult EndBlockCommand::execute(GameData &) {
+	return Finished;
+}
+
+Command *EndBlockCommand::next() const {
+	return nullptr;
+}
+}
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
new file mode 100644
index 0000000..1ac636c
--- /dev/null
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -0,0 +1,53 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_ENDBLOCKCOMMAND_H
+#define MUTATIONOFJB_ENDBLOCKCOMMAND_H
+
+#include "mutationofjb/commands/command.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class EndBlockCommandParser : public CommandParser {
+public:
+	EndBlockCommandParser() : _elseFound(false), _ifTag(0) {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+
+private:
+	bool _elseFound;
+	char _ifTag;
+};
+
+class EndBlockCommand : public Command {
+public:
+	static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Command *next() const override;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 18b8081..5ba35bc 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -28,8 +28,7 @@
 
 namespace MutationOfJB {
 
-bool IfCommand::ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command)
-{
+bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &parseContext, Command *&command) {
 	// IFtss oo val!
 	// <t>   1B Tag.
 	// <ss>  2B Scene.
@@ -59,6 +58,15 @@ bool IfCommand::ParseFunc(const Common::String &line, ScriptParseContext &parseC
 	return true;
 }
 
+void IfCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand) {
+	if (!oldCommand || !newCommand) {
+		warning(_("Unexpected empty command in transition"));
+		return;
+	}
+
+	static_cast<IfCommand *>(oldCommand)->setTrueCommand(newCommand);
+}
+
 IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative) :
 	_sceneId(sceneId),
 	_objectId(objectId),
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
index d33f34f..290260b 100644
--- a/engines/mutationofjb/commands/ifcommand.h
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -30,6 +30,12 @@ namespace MutationOfJB {
 
 class ScriptParseContext;
 
+class IfCommandParser : public CommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+};
+
 class IfCommand : public ConditionalCommand {
 public:
 	static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index ab98497..22d1c90 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -20,10 +20,20 @@
  *
  */
 
-#include "seqcommand.h"
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/translation.h"
 
 namespace MutationOfJB {
 
+void SeqCommandParser::transition(ScriptParseContext &, Command * oldCommand, Command * newCommand) {
+	if (!oldCommand || !newCommand) {
+		warning(_("Unexpected empty command in transition"));
+		return;
+	}
+
+	static_cast<SeqCommand *>(oldCommand)->setNextCommand(newCommand);
+}
+
 void SeqCommand::setNextCommand(Command *nextCommand)
 {
 	_nextCommand = nextCommand;
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index b247fb2..1d21f66 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -28,6 +28,11 @@
 
 namespace MutationOfJB {
 
+class SeqCommandParser : public CommandParser
+{
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand) override;
+};
+
 class SeqCommand : public Command {
 public:
 	void setNextCommand(Command *nextCommand);
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index e6c539a..132d869 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/mutationofjb
 MODULE_OBJS := \
 	commands/command.o \
 	commands/conditionalcommand.o \
+	commands/endblockcommand.o \
 	commands/ifcommand.o \
 	commands/seqcommand.o \
 	detection.o \
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index cdefbb9..59553d4 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -36,6 +36,7 @@
 #include "mutationofjb/game.h"
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/util.h"
+#include "mutationofjb/script.h"
 
 namespace MutationOfJB {
 
@@ -96,10 +97,24 @@ Common::Error MutationOfJBEngine::run() {
 	_room = new Room(_screen);
 	_room->load(_gameData->_currentScene, false);
 
+	EncryptedFile globalScriptFile;
+	globalScriptFile.open("global.atn");
+	Script *script = new Script;
+	script->loadFromStream(globalScriptFile);
+	globalScriptFile.close();
+
 	while(!shouldQuit()) {
 		Common::Event event;
 		while (_eventMan->pollEvent(event)) {
 			switch (event.type) {
+			case Common::EVENT_KEYDOWN:
+			{
+				if ((event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) ||
+						event.kbd.ascii == '~' || event.kbd.ascii == '#') {
+					_console->attach();
+				}
+				break;
+			}
 			case Common::EVENT_LBUTTONDOWN:
 			{
 				const Scene* const scene = _gameData->getScene(_gameData->_currentScene);
@@ -119,6 +134,7 @@ Common::Error MutationOfJBEngine::run() {
 			}
 		}
 
+		_console->onFrame();
 		_system->delayMillis(40);
 		_screen->update();
 	}
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 0280c86..a20c11e 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -25,25 +25,37 @@
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 #include "common/stream.h"
+#include "common/debug.h"
 #include "mutationofjb/commands/command.h"
+#include "mutationofjb/commands/ifcommand.h"
+#include "mutationofjb/commands/endblockcommand.h"
 
 namespace MutationOfJB {
 
-static CommandParseFunc* getParseFuncs() {
-	static CommandParseFunc funcs[] = {
+static CommandParser** getParsers() {
+	static CommandParser* parsers[] = {
+		new IfCommandParser,
+		new EndBlockCommandParser,
 		nullptr
 	};
 
-	return funcs;
+	return parsers;
 }
 
 
-ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) : _stream(stream) {}
+ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) :
+	_stream(stream),
+	_currentCommand(nullptr),
+	_lastCommand(nullptr)
+{}
 
 bool ScriptParseContext::readLine(Common::String &line) {
 	do {
 		Common::String str = _stream.readLine();
-		if (str.empty() || str[0] != '.') {
+		if (str.empty())
+			continue;
+
+		if (str[0] != '.') {
 			line = str;
 			if (line[0] == '*') {
 				line.deleteChar(0);
@@ -61,15 +73,52 @@ void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char
 }
 
 bool Script::loadFromStream(Common::SeekableReadStream &stream) {
+	destroy();
 
-	CommandParseFunc * const parseFuncs = getParseFuncs();
+	CommandParser **parsers = getParsers();
 
 	ScriptParseContext parseCtx(stream);
 
+	Common::String line;
+
+	Command *lastCmd = nullptr;
+	CommandParser *lastParser = nullptr;
+	while (parseCtx.readLine(line)) {
+		Command *currentCmd = nullptr;
+		CommandParser *currentParser = nullptr;
+
+		for (CommandParser **parser = parsers; *parser; ++parser) {
+			if ((*parser)->parse(line, parseCtx, currentCmd)) {
+				currentParser = *parser;
+				break;
+			}
+		}
+		if (lastParser) {
+			lastParser->transition(parseCtx, lastCmd, currentCmd);
+		}
+
+		if (currentCmd) {
+			_allCommands.push_back(currentCmd);
+		}
+
+		lastParser = currentParser;
+	}
+
 	Common::HashMap<Common::String, Command *> macros;
 	Common::HashMap<Common::String, Command *> labels;
 
 	return true;
 }
 
+void Script::destroy() {
+	for (Commands::iterator it = _allCommands.begin(); it != _allCommands.end(); ++it) {
+		delete *it;
+	}
+	_allCommands.clear();
+}
+
+Script::~Script() {
+	destroy();
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 9c0f0f3..da90a24 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -32,7 +32,9 @@ namespace Common {
 
 namespace MutationOfJB {
 
+class Command;
 class ConditionalCommand;
+typedef Common::Array<Command*> Commands;
 
 class ScriptParseContext
 {
@@ -40,23 +42,45 @@ public:
 	ScriptParseContext(Common::SeekableReadStream &stream);
 	bool readLine(Common::String &line);
 	void addConditionalCommand(ConditionalCommand *command, char tag);
-	//void setLastIfCommand(IfCommand *command);
+	void addLookSection(const Common::String & item, bool walkTo);
 
-private:
 	Common::SeekableReadStream &_stream;
+	Command *_currentCommand;
+	Command *_lastCommand;
 
 	struct ConditionalCommandInfo {
-		ConditionalCommand *command;
-		char tag;
+		ConditionalCommand *_command;
+		char _tag;
+	};
+	typedef Common::Array<ConditionalCommandInfo> ConditionalCommandInfos;
+
+	ConditionalCommandInfos _pendingCondCommands;
+
+	enum Action {
+		Walk,
+		Talk,
+		Look,
+		Use
 	};
-	Common::Array<ConditionalCommandInfo> _pendingCondCommands;
+
+	struct ActionInfo {
+		Action _action;
+		Common::String _object1Name;
+		Common::String _object2Name;
+		bool walkTo;
+	};
+	typedef Common::Array<ActionInfo> ActionInfos;
+	ActionInfos _actionInfos;
+private:
 };
 
 class Script {
 public:
 	bool loadFromStream(Common::SeekableReadStream &stream);
+	~Script();
 private:
-
+	void destroy();
+	Commands _allCommands;
 };
 
 }


Commit: 6d926ff55b2fccbf8e96495f88b3d4dda6e906d7
    https://github.com/scummvm/scummvm/commit/6d926ff55b2fccbf8e96495f88b3d4dda6e906d7
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for CHANGED, CHANGEO and CHANGES commands.

Changed paths:
  A engines/mutationofjb/commands/changecommand.cpp
  A engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
new file mode 100644
index 0000000..54c6d1e
--- /dev/null
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -0,0 +1,334 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/changecommand.h"
+#include "common/translation.h"
+
+namespace MutationOfJB {
+
+// CHANGEe rr ss ii val
+// <e>   1B  Entity to change register for.
+//           D door
+//           O object
+//           S static
+// <rr>  2B  Register name.
+// <ss>  2B  Scene ID.
+// <ii>  2B  Entity ID.
+// <val> VL  Value.
+
+bool ChangeCommandParser::parseValueString(const Common::String &valueString, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
+	if (valueString.size() < 8) {
+		return false;
+	}
+
+	sceneId = atoi(valueString.c_str() + 3);
+	entityId = atoi(valueString.c_str() + 6);
+	const char *val = nullptr;
+	if (valueString.size() >= 9) {
+		val = valueString.c_str() + 9;
+	}
+
+	if (valueString.hasPrefix("NM")) {
+		reg = ChangeCommand::NM;
+		op = ChangeCommand::SetValue;
+		strncpy(ccv._strVal, val, MAX_STR_LENGTH);
+	} else if (valueString.hasPrefix("LT")) {
+		reg = ChangeCommand::LT;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("SX")) {
+		reg = ChangeCommand::SX;
+		ccv._wordVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("SY")) {
+		reg = ChangeCommand::SY;
+		ccv._wordVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("XX")) {
+		reg = ChangeCommand::XX;
+		ccv._wordVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("YY")) {
+		reg = ChangeCommand::YY;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("XL")) {
+		reg = ChangeCommand::XL;
+		ccv._wordVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("YL")) {
+		reg = ChangeCommand::YL;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("WX")) {
+		reg = ChangeCommand::WX;
+		ccv._wordVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("WY")) {
+		reg = ChangeCommand::WY;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("AC")) {
+		reg = ChangeCommand::AC;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("FA")) {
+		reg = ChangeCommand::FA;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("FR")) {
+		reg = ChangeCommand::FR;
+		ccv._byteVal = parseInteger(val, op); 
+	} else if (valueString.hasPrefix("NA")) {
+		reg = ChangeCommand::NA;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("FS")) {
+		reg = ChangeCommand::FS;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("CA")) {
+		reg = ChangeCommand::CA;
+		ccv._byteVal = parseInteger(val, op);
+	}
+
+	return true;
+}
+
+
+bool ChangeDoorCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("CHANGED ")) {
+		return false;
+	}
+	uint8 sceneId = 0;
+	uint8 objectId = 0;
+	ChangeCommand::ChangeRegister reg;
+	ChangeCommand::ChangeOperation op;
+	ChangeCommandValue val;
+	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+		return false;
+	}
+
+	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
+	return true;
+}
+
+bool ChangeObjectCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("CHANGEO ")) {
+		return false;
+	}
+	uint8 sceneId = 0;
+	uint8 objectId = 0;
+	ChangeCommand::ChangeRegister reg;
+	ChangeCommand::ChangeOperation op;
+	ChangeCommandValue val;
+	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+		return false;
+	}
+
+	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
+	return true;
+}
+
+bool ChangeStaticCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("CHANGES ")) {
+		return false;
+	}
+	uint8 sceneId = 0;
+	uint8 objectId = 0;
+	ChangeCommand::ChangeRegister reg;
+	ChangeCommand::ChangeOperation op;
+	ChangeCommandValue val;
+	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+		return false;
+	}
+
+	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
+	return true;
+}
+
+int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOperation &op) {
+	if (!val || !(*val)) {
+		op = ChangeCommand::SetValue;
+		return 0;
+	}
+
+	if (val[0] == '\\') {
+		op = ChangeCommand::SetValue;
+		val++;
+	} else if (val[0] == '+') {
+		op = ChangeCommand::AddValue;
+		val++;
+	} else if (val[0] == '-') {
+		op = ChangeCommand::SubtractValue;
+		val++;
+	}
+
+	return atoi(val);
+}
+
+Command::ExecuteResult ChangeDoorCommand::execute(GameData &gameData) {
+	Scene *const scene = gameData.getScene(_sceneId);
+	if (!scene) {
+		return Finished;
+	}
+
+	Door *const door = scene->getDoor(_entityId);
+	if (!door) {
+		return Finished;
+	}
+
+	switch (_register) {
+	case NM:
+		strncpy(door->_name, _value._strVal, MAX_STR_LENGTH);
+		break;
+	case LT:
+		door->_destSceneId = _value._byteVal;
+		break;
+	case SX:
+		door->_destX = _value._wordVal;
+		break;
+	case SY:
+		door->_destY = _value._wordVal;
+		break;
+	case XX:
+		door->_x = _value._wordVal;
+		break;
+	case YY:
+		door->_y = _value._byteVal;
+		break;
+	case XL:
+		door->_width = _value._wordVal;
+		break;
+	case YL:
+		door->_height = _value._byteVal;
+		break;
+	case WX:
+		door->_walkToX = _value._wordVal;
+		break;
+	case WY:
+		door->_walkToY = _value._byteVal;
+		break;
+	case SP:
+		door->_SP = _value._byteVal;
+		break;
+	default:
+		warning("Object does not support changing this register.");
+		break;
+	}
+
+	return Finished;
+}
+
+Command::ExecuteResult ChangeObjectCommand::execute(GameData &gameData) {
+	Scene *const scene = gameData.getScene(_sceneId);
+	if (!scene) {
+		return Finished;
+	}
+
+	Object *const object = scene->getObject(_entityId);
+	if (!object) {
+		return Finished;
+	}
+
+	switch (_register) {
+	case AC:
+		object->_AC = _value._byteVal;
+		break;
+	case FA:
+		object->_FA = _value._byteVal;
+		break;
+	case FR:
+		object->_FR = _value._byteVal;
+		break;
+	case NA:
+		object->_NA = _value._byteVal;
+		break;
+	case FS:
+		object->_FS = _value._byteVal;
+		break;
+	case CA:
+		object->_CA = _value._byteVal;
+		break;
+	case XX:
+		object->_x = _value._wordVal;
+		break;
+	case YY:
+		object->_y = _value._byteVal;
+		break;
+	case XL:
+		object->_XL = _value._wordVal;
+		break;
+	case YL:
+		object->_YL = _value._byteVal;
+		break;
+	case WX:
+		object->_WX = _value._wordVal;
+		break;
+	case WY:
+		object->_WY = _value._byteVal;
+		break;
+	case SP:
+		object->_SP = _value._byteVal;
+		break;
+	default:
+		warning("Object does not support changing this register.");
+		break;
+	}
+
+	return Finished;
+}
+
+Command::ExecuteResult ChangeStaticCommand::execute(GameData &gameData) {
+	Scene *const scene = gameData.getScene(_sceneId);
+	if (!scene) {
+		return Finished;
+	}
+
+	Static *const stat = scene->getStatic(_entityId);
+	if (!stat) {
+		return Finished;
+	}
+
+	switch (_register) {
+	case AC:
+		stat->_active = _value._byteVal;
+		break;
+	case NM:
+		strncpy(stat->_name, _value._strVal, MAX_STR_LENGTH);
+		break;
+	case XX:
+		stat->_x = _value._wordVal;
+		break;
+	case YY:
+		stat->_y = _value._byteVal;
+		break;
+	case XL:
+		stat->_width = _value._wordVal;
+		break;
+	case YL:
+		stat->_height = _value._byteVal;
+		break;
+	case WX:
+		stat->_walkToX = _value._wordVal;
+		break;
+	case WY:
+		stat->_walkToY = _value._byteVal;
+		break;
+	case SP:
+		stat->_SP = _value._byteVal;
+		break;
+	default:
+		warning("Object does not support changing this register.");
+		break;
+	}
+
+	return Finished;
+}
+}
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
new file mode 100644
index 0000000..5b9a91e
--- /dev/null
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -0,0 +1,120 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "mutationofjb/game.h"
+
+namespace MutationOfJB {
+
+union ChangeCommandValue {
+	uint8 _byteVal;
+	uint16 _wordVal;
+	char _strVal[MAX_STR_LENGTH + 1];
+};
+
+class ChangeCommand : public SeqCommand {
+public:
+	enum ChangeRegister {
+		NM, // Name
+		LT, // Destination scene ID
+		SX, // Destination X
+		SY, // Destination Y
+		XX, // X
+		YY, // Y
+		XL, // Width
+		YL, // Height
+		WX, // Walk to X
+		WY, // Walk to Y
+		SP, // 
+		AC, // Active
+		FA, // First animation
+		FR,
+		NA,
+		FS,
+		CA
+	};
+
+	enum ChangeOperation {
+		SetValue,
+		AddValue,
+		SubtractValue
+	};
+
+	ChangeCommand(uint8 sceneId, uint8 entityId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val) :
+		_sceneId(sceneId), _entityId(entityId), _register(reg), _operation(op), _value(val)
+	{}
+protected:
+	uint8 _sceneId;
+	uint8 _entityId;
+	ChangeRegister _register;
+	ChangeOperation _operation;
+	ChangeCommandValue _value;
+};
+
+class ChangeCommandParser : public SeqCommandParser {
+protected:
+	bool parseValueString(const Common::String &valueString, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv);
+	int parseInteger(const char *val, ChangeCommand::ChangeOperation &op);
+};
+
+class ChangeObjectCommandParser : public ChangeCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class ChangeDoorCommandParser : public ChangeCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class ChangeStaticCommandParser : public ChangeCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+
+
+class ChangeDoorCommand : public ChangeCommand {
+public:
+	ChangeDoorCommand(uint8 sceneId, uint8 doorId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+		: ChangeCommand(sceneId, doorId, reg, op, val)
+	{}
+	virtual ExecuteResult execute(GameData &gameData) override;
+};
+
+class ChangeObjectCommand : public ChangeCommand {
+public:
+	ChangeObjectCommand(uint8 sceneId, uint8 objectId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+		: ChangeCommand(sceneId, objectId, reg, op, val)
+	{}
+	virtual ExecuteResult execute(GameData &gameData) override;
+};
+
+class ChangeStaticCommand : public ChangeCommand {
+public:
+	ChangeStaticCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+		: ChangeCommand(sceneId, staticId, reg, op, val)
+	{}
+	virtual ExecuteResult execute(GameData &gameData) override;
+};
+
+}
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index ab0ebc6..f8c160e 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -36,8 +36,6 @@ class CallMacroCommand;
 class ScriptParseContext;
 class Command;
 
-typedef bool (*CommandParseFunc)(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
-
 class CommandParser {
 public:
 	virtual ~CommandParser();
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 5ba35bc..8026c84 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -40,7 +40,7 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &pars
 		return false;
 	}
 	
-	if (strncmp(line.c_str(), "IF", 2) != 0) {
+	if (!line.hasPrefix("IF")) {
 		return false;
 	}
 
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 1d21f66..90e3028 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -30,6 +30,7 @@ namespace MutationOfJB {
 
 class SeqCommandParser : public CommandParser
 {
+public:
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand) override;
 };
 
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 740fb1c..9f517d6 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -143,6 +143,15 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	return true;
 }
 
+Door *Scene::getDoor(uint8 doorId) {
+	if (doorId == 0 || doorId > _noDoors) {
+		warning(_("Door %d does not exist"), doorId);
+		return nullptr;
+	}
+
+	return &_doors[doorId - 1];
+}
+
 Object *Scene::getObject(uint8 objectId) {
 	if (objectId == 0 || objectId > _noObjects) {
 		warning(_("Object %d does not exist"), objectId);
@@ -152,6 +161,15 @@ Object *Scene::getObject(uint8 objectId) {
 	return &_objects[objectId - 1];
 }
 
+Static *Scene::getStatic(uint8 staticId) {
+	if (staticId == 0 || staticId > _noStatics) {
+		warning(_("Static %d does not exist"), staticId);
+		return nullptr;
+	}
+
+	return &_statics[staticId - 1];
+}
+
 GameData::GameData() : _currentScene(0) {}
 
 Scene *GameData::getScene(uint8 sceneId)
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index d49a966..eda178b 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -93,7 +93,9 @@ struct Bitmap {
 
 struct Scene {
 
+	Door *getDoor(uint8 objectId);
 	Object *getObject(uint8 objectId);
+	Static *getStatic(uint8 staticId);
 
 	uint8 _startup;
 	uint8 _unknown001;
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 132d869..16a0c3b 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
+	commands/changecommand.o \
 	commands/command.o \
 	commands/conditionalcommand.o \
 	commands/endblockcommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index a20c11e..0deeccb 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -29,6 +29,7 @@
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/ifcommand.h"
 #include "mutationofjb/commands/endblockcommand.h"
+#include "mutationofjb/commands/changecommand.h"
 
 namespace MutationOfJB {
 
@@ -36,6 +37,9 @@ static CommandParser** getParsers() {
 	static CommandParser* parsers[] = {
 		new IfCommandParser,
 		new EndBlockCommandParser,
+		new ChangeDoorCommandParser,
+		new ChangeObjectCommandParser,
+		new ChangeStaticCommandParser,
 		nullptr
 	};
 
@@ -101,6 +105,7 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 			_allCommands.push_back(currentCmd);
 		}
 
+		lastCmd = currentCmd;
 		lastParser = currentParser;
 	}
 


Commit: bbd3750aeec2bdb91a8ab31eca8672c34ce61f83
    https://github.com/scummvm/scummvm/commit/bbd3750aeec2bdb91a8ab31eca8672c34ce61f83
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add change scene command, implement listsections and showsection debug console commands.

Changed paths:
  A engines/mutationofjb/debug.cpp
  A engines/mutationofjb/debug.h
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/command.cpp
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifcommand.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index 54c6d1e..e9bb9cc 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -96,6 +96,30 @@ bool ChangeCommandParser::parseValueString(const Common::String &valueString, ui
 	} else if (valueString.hasPrefix("CA")) {
 		reg = ChangeCommand::CA;
 		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("DS")) {
+		reg = ChangeCommand::DS;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("DL")) {
+		reg = ChangeCommand::DL;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("ND")) {
+		reg = ChangeCommand::ND;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("NO")) {
+		reg = ChangeCommand::NO;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("NS")) {
+		reg = ChangeCommand::NS;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("PF")) {
+		reg = ChangeCommand::PF;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("PL")) {
+		reg = ChangeCommand::PL;
+		ccv._byteVal = parseInteger(val, op);
+	} else if (valueString.hasPrefix("PD")) {
+		reg = ChangeCommand::PD;
+		ccv._byteVal = parseInteger(val, op);
 	}
 
 	return true;
@@ -115,7 +139,7 @@ bool ChangeDoorCommandParser::parse(const Common::String &line, ScriptParseConte
 		return false;
 	}
 
-	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
+	command = new ChangeDoorCommand(sceneId, objectId, reg, op, val);
 	return true;
 }
 
@@ -149,7 +173,24 @@ bool ChangeStaticCommandParser::parse(const Common::String &line, ScriptParseCon
 		return false;
 	}
 
-	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
+	command = new ChangeStaticCommand(sceneId, objectId, reg, op, val);
+	return true;
+}
+
+bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("CHANGE ")) {
+		return false;
+	}
+	uint8 sceneId = 0;
+	uint8 objectId = 0;
+	ChangeCommand::ChangeRegister reg;
+	ChangeCommand::ChangeOperation op;
+	ChangeCommandValue val;
+	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+		return false;
+	}
+
+	command = new ChangeSceneCommand(sceneId, objectId, reg, op, val);
 	return true;
 }
 
@@ -173,6 +214,86 @@ int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOper
 	return atoi(val);
 }
 
+
+const char *ChangeCommand::getRegisterAsString() const {
+	switch (_register) {
+	case NM: return "NM";
+	case LT: return "LT";
+	case SX: return "SX";
+	case SY: return "SY";
+	case XX: return "XX";
+	case YY: return "YY";
+	case XL: return "XL";
+	case YL: return "YL";
+	case WX: return "WX";
+	case WY: return "WY";
+	case SP: return "SP";
+	case AC: return "AC";
+	case FA: return "FA";
+	case FR: return "FR";
+	case NA: return "NA";
+	case FS: return "FS";
+	case CA: return "CA";
+	case DS: return "DS";
+	case DL: return "DL";
+	case ND: return "ND";
+	case NO: return "NO";
+	case NS: return "NS";
+	case PF: return "PF";
+	case PL: return "PL";
+	case PD: return "PD";
+	default: return "(unknown)";
+	}
+}
+
+Common::String ChangeCommand::getValueAsString() const {
+	switch (_register) {
+	case NM:
+		return Common::String::format("\"%s\"", _value._strVal);
+	case LT:
+	case YY:
+	case YL:
+	case WY:
+	case SP:
+	case AC:
+	case FA:
+	case FR:
+	case NA:
+	case FS:
+	case CA:
+	case DS:
+	case DL:
+	case ND:
+	case NO:
+	case NS:
+	case PF:
+	case PL:
+	case PD:
+		return Common::String::format("%d", (int)_value._byteVal);
+	case SX:
+	case SY:
+	case XX:
+	case XL:
+	case WX:
+		return Common::String::format("%d", (int)_value._wordVal);
+	default:
+		return "(unknown)";
+	}
+}
+
+const char *ChangeCommand::getOperationAsString() const {
+	switch (_operation) {
+	case SetValue:
+		return "=";
+	case AddValue:
+		return "+=";
+	case SubtractValue:
+		return "-=";
+	default:
+		return "(unknown)";
+	}
+}
+
 Command::ExecuteResult ChangeDoorCommand::execute(GameData &gameData) {
 	Scene *const scene = gameData.getScene(_sceneId);
 	if (!scene) {
@@ -226,6 +347,10 @@ Command::ExecuteResult ChangeDoorCommand::execute(GameData &gameData) {
 	return Finished;
 }
 
+Common::String ChangeDoorCommand::debugString() const {
+	return Common::String::format("scene%d.door%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+}
+
 Command::ExecuteResult ChangeObjectCommand::execute(GameData &gameData) {
 	Scene *const scene = gameData.getScene(_sceneId);
 	if (!scene) {
@@ -285,6 +410,10 @@ Command::ExecuteResult ChangeObjectCommand::execute(GameData &gameData) {
 	return Finished;
 }
 
+Common::String ChangeObjectCommand::debugString() const {
+	return Common::String::format("scene%d.object%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+}
+
 Command::ExecuteResult ChangeStaticCommand::execute(GameData &gameData) {
 	Scene *const scene = gameData.getScene(_sceneId);
 	if (!scene) {
@@ -331,4 +460,51 @@ Command::ExecuteResult ChangeStaticCommand::execute(GameData &gameData) {
 
 	return Finished;
 }
+
+Common::String ChangeStaticCommand::debugString() const {
+	return Common::String::format("scene%d.static%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+}
+
+Command::ExecuteResult ChangeSceneCommand::execute(GameData &gameData) {
+	Scene *const scene = gameData.getScene(_sceneId);
+	if (!scene) {
+		return Finished;
+	}
+
+	switch (_register) {
+	case DS:
+		scene->_startup = _value._byteVal;
+		break;
+	case DL:
+		scene->_DL = _value._byteVal;
+		break;
+	case ND:
+		scene->_noDoors = _value._byteVal;
+		break;
+	case NO:
+		scene->_noObjects = _value._byteVal;
+		break;
+	case NS:
+		scene->_noStatics = _value._byteVal;
+		break;
+	case PF:
+		scene->_palRotStart = _value._byteVal;
+		break;
+	case PL:
+		scene->_palRotEnd = _value._byteVal;
+		break;
+	case PD:
+		scene->_palRotPeriod = _value._byteVal;
+		break;
+	default:
+		warning("Scene does not support changing this register.");
+		break;
+	}
+
+	return Finished;
+}
+
+Common::String ChangeSceneCommand::debugString() const {
+	return Common::String::format("scene%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+}
 }
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index 5b9a91e..d590fa5 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -50,7 +50,15 @@ public:
 		FR,
 		NA,
 		FS,
-		CA
+		CA,
+		DS, // Startup
+		DL,
+		ND, // Number of doors
+		NO, // Number of objects
+		NS, // Number of statics
+		PF, // Palette rotation first
+		PL, // Palette rotation last
+		PD  // Palette rotation delay
 	};
 
 	enum ChangeOperation {
@@ -63,6 +71,10 @@ public:
 		_sceneId(sceneId), _entityId(entityId), _register(reg), _operation(op), _value(val)
 	{}
 protected:
+	const char *getRegisterAsString() const;
+	Common::String getValueAsString() const;
+	const char *getOperationAsString() const;
+
 	uint8 _sceneId;
 	uint8 _entityId;
 	ChangeRegister _register;
@@ -91,7 +103,10 @@ public:
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 };
 
-
+class ChangeSceneCommandParser : public ChangeCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
 
 class ChangeDoorCommand : public ChangeCommand {
 public:
@@ -99,6 +114,7 @@ public:
 		: ChangeCommand(sceneId, doorId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
 };
 
 class ChangeObjectCommand : public ChangeCommand {
@@ -107,6 +123,7 @@ public:
 		: ChangeCommand(sceneId, objectId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
 };
 
 class ChangeStaticCommand : public ChangeCommand {
@@ -115,6 +132,16 @@ public:
 		: ChangeCommand(sceneId, staticId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
+};
+
+class ChangeSceneCommand : public ChangeCommand {
+public:
+	ChangeSceneCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+		: ChangeCommand(sceneId, staticId, reg, op, val)
+	{}
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
 };
 
 }
diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
index af4c28e..943986e 100644
--- a/engines/mutationofjb/commands/command.cpp
+++ b/engines/mutationofjb/commands/command.cpp
@@ -25,7 +25,7 @@
 
 namespace MutationOfJB {
 
-void CommandParser::transition(ScriptParseContext &, Command *, Command *) {}
+void CommandParser::transition(ScriptParseContext &, Command *, Command *, CommandParser *) {}
 CommandParser::~CommandParser() {}
 
 Command::~Command() {}
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index f8c160e..ccfdea2 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -42,7 +42,7 @@ public:
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) = 0;
 
 	/* Old command - created by this parser. */
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
 };
 
 class Command {
@@ -59,6 +59,7 @@ public:
 	virtual Command *next() const = 0;
 
 	virtual SeqCommand *asSeqCommand();
+	virtual Common::String debugString() const = 0;
 };
 }
 
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 218be1a..be610c7 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -41,15 +41,17 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 	// This is the start or end of section/block.
 
 	if (line.size() >= 4 && (line.hasPrefix("#L ") || line.hasPrefix("-L "))) {
-		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Look, line.c_str() + 3, "", firstChar == '#'};
-		parseCtx._actionInfos.push_back(ai);
-		debug("# Look: %s", line.c_str() + 3);
+		ActionInfo ai = {ActionInfo::Look, line.c_str() + 3, "", firstChar == '#', nullptr};
+		parseCtx._lookActionInfos.push_back(ai);
+		_pendingActionInfos.push_back(&parseCtx._lookActionInfos.back());
 	} else if (line.size() >= 4 && (line.hasPrefix("#W ") || line.hasPrefix("-W "))) {
-		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Walk, line.c_str() + 3, "", firstChar == '#'};
-		parseCtx._actionInfos.push_back(ai);
+		ActionInfo ai = {ActionInfo::Walk, line.c_str() + 3, "", firstChar == '#', nullptr};
+		parseCtx._walkActionInfos.push_back(ai);
+		_pendingActionInfos.push_back(&parseCtx._walkActionInfos.back());
 	} else if (line.size() >= 4 && (line.hasPrefix("#T ") || line.hasPrefix("-T "))) {
-		ScriptParseContext::ActionInfo ai = {ScriptParseContext::Talk, line.c_str() + 3, "", firstChar == '#'};
-		parseCtx._actionInfos.push_back(ai);
+		ActionInfo ai = {ActionInfo::Talk, line.c_str() + 3, "", firstChar == '#', nullptr};
+		parseCtx._talkActionInfos.push_back(ai);
+		_pendingActionInfos.push_back(&parseCtx._talkActionInfos.back());
 	} else if (line.size() >= 4 && (line.hasPrefix("#U ") || line.hasPrefix("-U "))) {
 		int secondObjPos = -1;
 		for (int i = 3; i < (int) line.size(); ++i) {
@@ -58,13 +60,15 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 				break;
 			}
 		}
-		ScriptParseContext::ActionInfo ai = {
-			ScriptParseContext::Talk,
+		ActionInfo ai = {
+			ActionInfo::Use,
 			line.c_str() + 3,
 			(secondObjPos != -1) ? line.c_str() + secondObjPos : "",
-			firstChar == '#'
+			firstChar == '#',
+			nullptr
 		};
-		parseCtx._actionInfos.push_back(ai); 
+		parseCtx._useActionInfos.push_back(ai); 
+		_pendingActionInfos.push_back(&parseCtx._useActionInfos.back());
 	} else if ((line.hasPrefix("#ELSE") || line.hasPrefix("=ELSE"))) {
 		_elseFound = true;
 		_ifTag = 0;
@@ -78,7 +82,7 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 	return true;
 }
 
-void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand) {
+void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *newCommandParser) {
 	if (_elseFound) {
 		if (newCommand) {
 			ScriptParseContext::ConditionalCommandInfos::iterator it = parseCtx._pendingCondCommands.begin();
@@ -96,6 +100,14 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		_elseFound = false;
 		_ifTag = 0;
 	}
+
+	if (!_pendingActionInfos.empty() && newCommandParser != this) {
+		debug("Fixing pending action info.\n");
+		for (Common::Array<ActionInfo *>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
+			(*it)->_command = newCommand;
+		}
+		_pendingActionInfos.clear();
+	}
 }
 
 Command::ExecuteResult EndBlockCommand::execute(GameData &) {
@@ -105,4 +117,9 @@ Command::ExecuteResult EndBlockCommand::execute(GameData &) {
 Command *EndBlockCommand::next() const {
 	return nullptr;
 }
+
+Common::String EndBlockCommand::debugString() const {
+	return "ENDBLOCK";
+}
+
 }
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 1ac636c..51f7993 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -25,19 +25,23 @@
 
 #include "mutationofjb/commands/command.h"
 #include "common/scummsys.h"
+#include "common/array.h"
 
 namespace MutationOfJB {
 
+class ActionInfo;
+
 class EndBlockCommandParser : public CommandParser {
 public:
 	EndBlockCommandParser() : _elseFound(false), _ifTag(0) {}
 
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
 
 private:
 	bool _elseFound;
 	char _ifTag;
+	Common::Array<ActionInfo*> _pendingActionInfos;
 };
 
 class EndBlockCommand : public Command {
@@ -46,6 +50,7 @@ public:
 
 	virtual ExecuteResult execute(GameData &gameData) override;
 	virtual Command *next() const override;
+	virtual Common::String debugString() const;
 };
 
 }
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 8026c84..a41875f 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -58,7 +58,7 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &pars
 	return true;
 }
 
-void IfCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand) {
+void IfCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
 		warning(_("Unexpected empty command in transition"));
 		return;
@@ -92,5 +92,10 @@ Command::ExecuteResult IfCommand::execute(GameData &gameData) {
 
 	return Finished;
 }
+
+Common::String IfCommand::debugString() const {
+	return Common::String::format("IF scene%d.object%d.WX %s %d", _sceneId, _objectId, _negative ? "!=" : "==", _value);
+}
+
 }
 
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
index 290260b..9462278 100644
--- a/engines/mutationofjb/commands/ifcommand.h
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -33,7 +33,7 @@ class ScriptParseContext;
 class IfCommandParser : public CommandParser {
 public:
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand);
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
 };
 
 class IfCommand : public ConditionalCommand {
@@ -43,6 +43,7 @@ public:
 	IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
 	
 	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
 
 private:
 	uint8 _sceneId;
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
new file mode 100644
index 0000000..4861ab1
--- /dev/null
+++ b/engines/mutationofjb/debug.cpp
@@ -0,0 +1,162 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/debug.h"
+#include "mutationofjb/mutationofjb.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/commands/command.h"
+#include "common/debug-channels.h"
+#include "common/translation.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+/*
+TODO
+static Common::String convertTo7bitASCII() {
+	return Common::String();
+}
+*/
+
+Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
+	registerCmd("listsections", WRAP_METHOD(Console, cmd_listsections));
+	registerCmd("showsection", WRAP_METHOD(Console, cmd_showsection));
+}
+
+bool Console::cmd_listsections(int argc, const char **argv) {
+	if (argc == 3) {
+		Script *script = nullptr;
+		if (strcmp(argv[1], "G") == 0) {
+			script = _vm->getGlobalScript();
+		} else if (strcmp(argv[1], "L") == 0) {
+			script = _vm->getLocalScript();
+		}
+		if (!script) {
+			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+		} else {
+			if (strcmp(argv[2], "L") == 0) {
+				const ActionInfos &actionInfos = script->getLookActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					debugPrintf(_("Look %s\n"), actionInfo._object1Name.c_str());
+				}
+			} else if (strcmp(argv[2], "W") == 0) {
+				const ActionInfos &actionInfos = script->getWalkActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					debugPrintf(_("Walk %s\n"), actionInfo._object1Name.c_str());
+				}
+			} else if (strcmp(argv[2], "T") == 0) {
+				const ActionInfos &actionInfos = script->getTalkActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					debugPrintf(_("Talk %s\n"), actionInfo._object1Name.c_str());
+				}
+			} else if (strcmp(argv[2], "U") == 0) {
+				const ActionInfos &actionInfos = script->getUseActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					debugPrintf(_("Use %s\n"), actionInfo._object1Name.c_str());
+				}
+			} else {
+				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk) or 'U' (use).\n"));
+			}
+		}
+	} else {
+		debugPrintf(_("listsections <G|L> <L|W|T|U>\n"));
+	}
+	return true;
+}
+
+bool Console::cmd_showsection(int argc, const char **argv) {
+	if (argc == 4) {
+		Script *script = nullptr;
+		if (strcmp(argv[1], "G") == 0) {
+			script = _vm->getGlobalScript();
+		} else if (strcmp(argv[1], "L") == 0) {
+			script = _vm->getLocalScript();
+		}
+		if (!script) {
+			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+		} else {
+			Command *command = nullptr;
+			bool found = false;
+			if (strcmp(argv[2], "L") == 0) {
+				const ActionInfos &actionInfos = script->getLookActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					if (actionInfo._object1Name == argv[3]) {
+						found = true;
+						command = actionInfo._command;
+						break;
+					}
+				}
+			} else if (strcmp(argv[2], "W") == 0) {
+				const ActionInfos &actionInfos = script->getWalkActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					if (actionInfo._object1Name == argv[3]) {
+						found = true;
+						command = actionInfo._command;
+						break;
+					}
+				}
+			} else if (strcmp(argv[2], "T") == 0) {
+				const ActionInfos &actionInfos = script->getTalkActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					if (actionInfo._object1Name == argv[3]) {
+						found = true;
+						command = actionInfo._command;
+						break;
+					}
+				}
+			} else if (strcmp(argv[2], "U") == 0) {
+				const ActionInfos &actionInfos = script->getUseActionInfos();
+				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
+					const ActionInfo &actionInfo = *it;
+					if (actionInfo._object1Name == argv[3]) {
+						found = true;
+						command = actionInfo._command;
+						break;
+					}
+				}
+			} else {
+				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk) or 'U' (use).\n"));
+			}
+
+			if (found) {
+				if (command) {
+					debugPrintf("%s\n", command->debugString().c_str());
+				}
+			} else {
+				debugPrintf("Section not found.\n");
+			}
+		}
+	} else {
+		debugPrintf(_("showsection <G|L> <L|W|T|U> <sectionname>\n"));
+	}
+
+	return true;
+}
+
+}
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
new file mode 100644
index 0000000..86ee844
--- /dev/null
+++ b/engines/mutationofjb/debug.h
@@ -0,0 +1,40 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gui/debugger.h"
+
+namespace MutationOfJB {
+
+class MutationOfJBEngine;
+
+class Console : public GUI::Debugger {
+public:
+	Console(MutationOfJBEngine *vm);
+	virtual ~Console(void) {}
+private:
+	bool cmd_listsections(int argc, const char **argv);
+	bool cmd_showsection(int argc, const char **argv);
+	MutationOfJBEngine *_vm;
+};
+
+}
+
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 16a0c3b..09f00db 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	commands/endblockcommand.o \
 	commands/ifcommand.o \
 	commands/seqcommand.o \
+	debug.o \
 	detection.o \
 	encryptedfile.o \
 	game.o \
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 59553d4..3576fd5 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -37,14 +37,17 @@
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/util.h"
 #include "mutationofjb/script.h"
+#include "mutationofjb/debug.h"
 
 namespace MutationOfJB {
 
 MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 : Engine(syst),
-  _console(nullptr),
-  _room(nullptr),
-  _screen(nullptr)
+ _console(nullptr),
+ _room(nullptr),
+ _screen(nullptr),
+ _globalScript(nullptr),
+ _localScript(nullptr)
 {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
@@ -99,8 +102,8 @@ Common::Error MutationOfJBEngine::run() {
 
 	EncryptedFile globalScriptFile;
 	globalScriptFile.open("global.atn");
-	Script *script = new Script;
-	script->loadFromStream(globalScriptFile);
+	_globalScript = new Script;
+	_globalScript->loadFromStream(globalScriptFile);
 	globalScriptFile.close();
 
 	while(!shouldQuit()) {
@@ -141,4 +144,13 @@ Common::Error MutationOfJBEngine::run() {
 
 	return Common::kNoError;
 }
+
+Script *MutationOfJBEngine::getGlobalScript() {
+	return _globalScript;
+}
+
+Script *MutationOfJBEngine::getLocalScript() {
+	return _localScript;
+}
+
 }
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 75bd678..efd8898 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -24,7 +24,6 @@
 #define MUTATIONOFJB_MUTATIONOFJB_H
 
 #include "engines/engine.h"
-#include "gui/debugger.h"
 
 namespace Graphics {
 	class Screen;
@@ -35,6 +34,7 @@ namespace MutationOfJB {
 class Console;
 class Room;
 struct GameData;
+class Script;
 
 class MutationOfJBEngine : public Engine {
 public:
@@ -42,6 +42,9 @@ public:
 	~MutationOfJBEngine();
 
 	virtual Common::Error run();
+	Script *getGlobalScript();
+	Script *getLocalScript();
+
 private:
 	bool loadGameData(bool partB);
 	void setupCursor();
@@ -50,13 +53,10 @@ private:
 	Room *_room;
 	GameData *_gameData;
 	Graphics::Screen *_screen;
+	Script *_globalScript;
+	Script *_localScript;
 };
 
-class Console : public GUI::Debugger {
-public:
-	Console(MutationOfJBEngine *vm) {}
-	virtual ~Console(void) {}
-};
 
 }
 
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 0deeccb..8985e06 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -40,6 +40,7 @@ static CommandParser** getParsers() {
 		new ChangeDoorCommandParser,
 		new ChangeObjectCommandParser,
 		new ChangeStaticCommandParser,
+		new ChangeSceneCommandParser,
 		nullptr
 	};
 
@@ -97,8 +98,12 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 				break;
 			}
 		}
+		if (!currentParser) {
+			continue;
+		}
+
 		if (lastParser) {
-			lastParser->transition(parseCtx, lastCmd, currentCmd);
+			lastParser->transition(parseCtx, lastCmd, currentCmd, currentParser);
 		}
 
 		if (currentCmd) {
@@ -109,6 +114,11 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 		lastParser = currentParser;
 	}
 
+	_lookActionInfos = parseCtx._lookActionInfos;
+	_walkActionInfos = parseCtx._walkActionInfos;
+	_talkActionInfos = parseCtx._talkActionInfos;
+	_useActionInfos = parseCtx._useActionInfos;
+
 	Common::HashMap<Common::String, Command *> macros;
 	Common::HashMap<Common::String, Command *> labels;
 
@@ -126,4 +136,20 @@ Script::~Script() {
 	destroy();
 }
 
+const ActionInfos &Script::getLookActionInfos() const {
+	return _lookActionInfos;
+}
+
+const ActionInfos &Script::getWalkActionInfos() const {
+	return _walkActionInfos;
+}
+
+const ActionInfos &Script::getTalkActionInfos() const {
+	return _talkActionInfos;
+}
+
+const ActionInfos &Script::getUseActionInfos() const {
+	return _useActionInfos;
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index da90a24..be04dc5 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -36,6 +36,24 @@ class Command;
 class ConditionalCommand;
 typedef Common::Array<Command*> Commands;
 
+
+struct ActionInfo {
+	enum Action {
+		Walk,
+		Talk,
+		Look,
+		Use
+	};
+
+	Action _action;
+	Common::String _object1Name;
+	Common::String _object2Name;
+	bool _walkTo;
+	Command *_command;
+};
+
+typedef Common::Array<ActionInfo> ActionInfos;
+
 class ScriptParseContext
 {
 public:
@@ -56,21 +74,11 @@ public:
 
 	ConditionalCommandInfos _pendingCondCommands;
 
-	enum Action {
-		Walk,
-		Talk,
-		Look,
-		Use
-	};
+	ActionInfos _lookActionInfos;
+	ActionInfos _walkActionInfos;
+	ActionInfos _talkActionInfos;
+	ActionInfos _useActionInfos;
 
-	struct ActionInfo {
-		Action _action;
-		Common::String _object1Name;
-		Common::String _object2Name;
-		bool walkTo;
-	};
-	typedef Common::Array<ActionInfo> ActionInfos;
-	ActionInfos _actionInfos;
 private:
 };
 
@@ -78,9 +86,19 @@ class Script {
 public:
 	bool loadFromStream(Common::SeekableReadStream &stream);
 	~Script();
+
+	const ActionInfos &getLookActionInfos() const;
+	const ActionInfos &getWalkActionInfos() const;
+	const ActionInfos &getTalkActionInfos() const;
+	const ActionInfos &getUseActionInfos() const;
+
 private:
 	void destroy();
 	Commands _allCommands;
+	ActionInfos _lookActionInfos;
+	ActionInfos _walkActionInfos;
+	ActionInfos _talkActionInfos;
+	ActionInfos _useActionInfos;
 };
 
 }


Commit: 5d29112f1c06dea3a789b36b0d109e6529f8cd61
    https://github.com/scummvm/scummvm/commit/5d29112f1c06dea3a789b36b0d109e6529f8cd61
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add say command with dummy implementation.

Changed paths:
  A engines/mutationofjb/commands/saycommand.cpp
  A engines/mutationofjb/commands/saycommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index be610c7..af9282f 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -102,7 +102,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 	}
 
 	if (!_pendingActionInfos.empty() && newCommandParser != this) {
-		debug("Fixing pending action info.\n");
+		debug("Fixing pending action info.");
 		for (Common::Array<ActionInfo *>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
 			(*it)->_command = newCommand;
 		}
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index a41875f..0643c3e 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -26,6 +26,25 @@
 #include "common/str.h"
 #include "common/translation.h"
 
+/*
+	"IF" <tag> <sceneId> <objectId> <value> ["!"]
+
+	IF command compares the value of the WX pseudo-register of the object in the specified scene.
+	If the values match, execution continues to the next line.
+	Otherwise execution continues after first "#ELSE" with the same <tag>.
+	The logic can be reversed with exclamation mark at the end.
+
+	<tag> is always 1 character long, <sceneId> and <objectId> 2 characters long.
+
+	Please note that this does not work line you are used to from saner languages.
+	IF does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+		IF something
+		IF something else
+		#ELSE
+		...
+	This is effectively logical AND.
+*/
+
 namespace MutationOfJB {
 
 bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &parseContext, Command *&command) {
diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
new file mode 100644
index 0000000..9448203
--- /dev/null
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -0,0 +1,151 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/saycommand.h"
+#include "mutationofjb/script.h"
+#include "common/str.h"
+#include "common/debug.h"
+#include "common/debug-channels.h"
+
+/*
+	("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> ["<" <voiceFile> | "<!"]
+	<skipped> " " <lineToSay> ("<" <voiceFile> | "<!")
+
+	Say command comes in four variants: SM, SLM, NM and NLM.
+	Note: In script files, they are usually written as *SM.
+	The asterisk is ignored by the readLine function.
+
+	Each of them plays a voice file (if present) and/or shows a message
+	(if voice file not present or subtitles are enabled).
+
+	The difference between versions starting with "S" and "N" is that
+	the "N" version does not show talking animation.
+
+	The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
+
+	If the line ends with "<!", it means the message continues to the next line.
+	Next line usually has "SM" (or other variant) repeated, but it does not have to.
+	Then we have the rest of the string to say (which is concatenated with the previous line)
+	and possibly the voice file or "<!" again.
+*/
+
+namespace MutationOfJB {
+
+bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
+	bool waitForPrevious = false;
+	bool talkingAnimation = false;
+
+	if (line.hasPrefix("SM")) {
+		waitForPrevious = false;
+		talkingAnimation = true;
+	} else if (line.hasPrefix("SLM")) {
+		waitForPrevious = true;
+		talkingAnimation = true;
+	} else if (line.hasPrefix("NM")) {
+		waitForPrevious = false;
+		talkingAnimation = false;
+	} else if (line.hasPrefix("NLM")) {
+		waitForPrevious = true;
+		talkingAnimation = false;
+	} else {
+		return false;
+	}
+
+	Common::String currentLine = line;
+
+	Common::String lineToSay;
+	Common::String voiceFile;
+
+	bool cont = false;
+	bool firstPass = true;
+
+	do {
+		cont = false;
+
+		uint startPos;
+		for (startPos = 0; startPos < currentLine.size(); ++startPos) {
+			if (currentLine[startPos] == ' ') {
+				break;
+			}
+		}
+		if (startPos == currentLine.size()) {
+			if (!firstPass) {
+				warning("Unable to parse line '%s'", currentLine.c_str());
+				break;
+			}
+		}
+		startPos++;
+
+		uint endPos;
+		for (endPos = startPos; endPos < currentLine.size(); ++endPos) {
+			if (currentLine[endPos] == '<') {
+				break;
+			}
+		}
+
+		Common::String talkStr(currentLine.c_str() + startPos, endPos - startPos);
+
+		if (endPos != currentLine.size()) {
+			const char * end = currentLine.c_str() + endPos + 1;
+			if (end[0] == '!') {
+				cont = true;
+			} else {
+				voiceFile = end;
+			}
+		}
+
+		if (talkStr.lastChar() == '~') {
+			debug("Found say command ending with '~'. Please take a look.");
+		}
+
+		if (lineToSay.empty()) {
+			lineToSay = talkStr;
+		} else {
+			lineToSay = " " + talkStr;
+		}
+
+		if (cont) {
+			if (!parseCtx.readLine(currentLine)) {
+				cont = false;
+			}
+		}
+
+		firstPass = false;
+	} while (cont);
+
+	command = new SayCommand(lineToSay, voiceFile, waitForPrevious, talkingAnimation);
+
+	return true;
+}
+
+
+Command::ExecuteResult SayCommand::execute(GameData &) {
+	// TODO: Actual implementation.
+	debug("%s [%s]", _lineToSay.c_str(), _voiceFile.c_str());
+	return Finished;
+}
+
+Common::String SayCommand::debugString() const {
+	return Common::String::format("SHOWMSG%s%s '%s' '%s'", _waitForPrevious ? "+WAIT" : "", _talkingAnimation ? "+TALKANIM" : "", _lineToSay.c_str(), _voiceFile.c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/saycommand.h b/engines/mutationofjb/commands/saycommand.h
new file mode 100644
index 0000000..16303de
--- /dev/null
+++ b/engines/mutationofjb/commands/saycommand.h
@@ -0,0 +1,56 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_SAYCOMMAND_H
+#define MUTATIONOFJB_SAYCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class SayCommandParser : public SeqCommandParser {
+public:
+	SayCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class SayCommand : public SeqCommand {
+public:
+	SayCommand(Common::String &lineToSay, Common::String &voiceFile, bool waitForPrevious, bool talkingAnimation) :
+		_lineToSay(lineToSay),
+		_voiceFile(voiceFile),
+		_waitForPrevious(waitForPrevious),
+		_talkingAnimation(talkingAnimation) {}
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const override;
+private:
+	Common::String _lineToSay;
+	Common::String _voiceFile;
+	bool _waitForPrevious;
+	bool _talkingAnimation;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index 22d1c90..6c167e9 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -25,7 +25,7 @@
 
 namespace MutationOfJB {
 
-void SeqCommandParser::transition(ScriptParseContext &, Command * oldCommand, Command * newCommand) {
+void SeqCommandParser::transition(ScriptParseContext &, Command * oldCommand, Command * newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
 		warning(_("Unexpected empty command in transition"));
 		return;
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 90e3028..0fa30ab 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -31,7 +31,7 @@ namespace MutationOfJB {
 class SeqCommandParser : public CommandParser
 {
 public:
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand) override;
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
 };
 
 class SeqCommand : public Command {
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 09f00db..2c89988 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	commands/conditionalcommand.o \
 	commands/endblockcommand.o \
 	commands/ifcommand.o \
+	commands/saycommand.o \
 	commands/seqcommand.o \
 	debug.o \
 	detection.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 8985e06..4490af5 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -30,6 +30,7 @@
 #include "mutationofjb/commands/ifcommand.h"
 #include "mutationofjb/commands/endblockcommand.h"
 #include "mutationofjb/commands/changecommand.h"
+#include "mutationofjb/commands/saycommand.h"
 
 namespace MutationOfJB {
 
@@ -41,6 +42,7 @@ static CommandParser** getParsers() {
 		new ChangeObjectCommandParser,
 		new ChangeStaticCommandParser,
 		new ChangeSceneCommandParser,
+		new SayCommandParser,
 		nullptr
 	};
 


Commit: bdb6582bb2523f6216f92bbc79a602b33137d023
    https://github.com/scummvm/scummvm/commit/bdb6582bb2523f6216f92bbc79a602b33137d023
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement data model for inventory.

Changed paths:
  A engines/mutationofjb/inventory.cpp
  A engines/mutationofjb/inventory.h
    engines/mutationofjb/commands/conditionalcommand.h
    engines/mutationofjb/debug.h
    engines/mutationofjb/encryptedfile.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/util.h


diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
index 27a8ac1..6f64720 100644
--- a/engines/mutationofjb/commands/conditionalcommand.h
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -20,6 +20,9 @@
  *
  */
 
+#ifndef MUTATIONOFJB_CONDITIONALCOMMAND_H
+#define MUTATIONOFJB_CONDITIONALCOMMAND_H
+
 #include "mutationofjb/commands/command.h"
 #include "common/scummsys.h"
 
@@ -43,3 +46,5 @@ protected:
 };
 
 }
+
+#endif
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 86ee844..a41baca 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -20,6 +20,9 @@
  *
  */
 
+#ifndef MUTATIONOFJB_DEBUG_H
+#define MUTATIONOFJB_DEBUG_H
+
 #include "gui/debugger.h"
 
 namespace MutationOfJB {
@@ -38,3 +41,5 @@ private:
 
 }
 
+#endif
+
diff --git a/engines/mutationofjb/encryptedfile.h b/engines/mutationofjb/encryptedfile.h
index 69f6f62..2053f1f 100644
--- a/engines/mutationofjb/encryptedfile.h
+++ b/engines/mutationofjb/encryptedfile.h
@@ -20,6 +20,9 @@
  *
  */
 
+#ifndef MUTATIONOFJB_ENCRYPTEDFILE_H
+#define MUTATIONOFJB_ENCRYPTEDFILE_H
+
 #include "common/file.h"
 
 namespace MutationOfJB {
@@ -30,3 +33,5 @@ public:
 };
 
 }
+
+#endif
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 9f517d6..15fbd03 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -170,6 +170,7 @@ Static *Scene::getStatic(uint8 staticId) {
 	return &_statics[staticId - 1];
 }
 
+
 GameData::GameData() : _currentScene(0) {}
 
 Scene *GameData::getScene(uint8 sceneId)
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index eda178b..77e336f 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -20,7 +20,11 @@
  *
  */
 
+#ifndef MUTATIONOFJB_GAME_H
+#define MUTATIONOFJB_GAME_H
+
 #include "common/scummsys.h"
+#include "mutationofjb/inventory.h"
 
 namespace Common {
 	class ReadStream;
@@ -133,9 +137,12 @@ public:
 	bool loadFromStream(Common::ReadStream &stream);
 
 	uint8 _currentScene;
+	Inventory _inventory;
 private:
 	Scene _scenes[45];
 
 };
 
 }
+
+#endif
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
new file mode 100644
index 0000000..0f91a93
--- /dev/null
+++ b/engines/mutationofjb/inventory.cpp
@@ -0,0 +1,92 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/inventory.h"
+#include "common/algorithm.h"
+#include "common/debug.h"
+
+namespace MutationOfJB {
+
+static const uint VISIBLE_ITEMS = 6;
+
+const Inventory::Items &Inventory::getItems() const {
+	return _items;
+}
+
+void Inventory::addItem(const Common::String &item) {
+	_items.push_back(item);
+
+	if (_items.size() > VISIBLE_ITEMS) {
+		rotateItemsRight(VISIBLE_ITEMS);
+	}
+}
+
+void Inventory::removeItem(const Common::String &item) {
+	Items::iterator it = find(_items.begin(), _items.end(), item);
+	if (it == _items.end()) {
+		debug("Item '%s' not in inventory.", item.c_str());
+		return;
+	}
+
+	_items.remove_at(it - _items.begin());
+}
+
+void Inventory::removeAllItems() {
+	_items.clear();
+}
+
+void Inventory::rotateItemsRight(uint n) {
+	if (_items.size() < 2) {
+		return;
+	}
+
+	n %= _items.size();
+
+	reverseItems(0, _items.size() - 1);
+	reverseItems(0, n - 1);
+	reverseItems(n, _items.size() - 1);
+}
+
+void Inventory::rotateItemsLeft(uint n) {
+	if (_items.size() < 2) {
+		return;
+	}
+
+	n %= _items.size();
+	reverseItems(0, _items.size() - 1);
+	reverseItems(_items.size() - n, _items.size() - 1);
+	reverseItems(0, _items.size() - n - 1);
+}
+
+void Inventory::reverseItems(uint from, uint to) {
+	assert(from <= to);
+	if (from == to) {
+		return;
+	}
+
+	const uint size = to - from + 1;
+	for (uint i = 0; i < size / 2; ++i) {
+		SWAP(_items[i], _items[size - i - 1]);
+	}
+}
+
+}
diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h
new file mode 100644
index 0000000..02cccc0
--- /dev/null
+++ b/engines/mutationofjb/inventory.h
@@ -0,0 +1,52 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_INVENTORY_H
+#define MUTATIONOFJB_INVENTORY_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class Inventory {
+public:
+	typedef Common::Array<Common::String> Items;
+
+	const Items &getItems() const;
+	void addItem(const Common::String &item);
+	void removeItem(const Common::String &item);
+	void removeAllItems();
+
+	void rotateItemsRight(uint n);
+	void rotateItemsLeft(uint n);
+
+private:
+	void reverseItems(uint from, uint to);
+
+	Items _items;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 2c89988..f719ae0 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS := \
 	detection.o \
 	encryptedfile.o \
 	game.o \
+	inventory.o \
 	mutationofjb.o \
 	room.o \
 	script.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 4490af5..98b1727 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -69,7 +69,7 @@ bool ScriptParseContext::readLine(Common::String &line) {
 			}
 			return true;
 		}
-	} while(_stream.eos());
+	} while(!_stream.eos());
 
 	return false;
 }
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 3059d51..7dd7495 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -20,6 +20,11 @@
  *
  */
 
+#ifndef MUTATIONOFJB_UTIL_H
+#define MUTATIONOFJB_UTIL_H
+
 namespace MutationOfJB {
 	void reportFileMissingError(const char *fileName);
 }
+
+#endif


Commit: dae522f63c05029298da1694038af9b24655ac05
    https://github.com/scummvm/scummvm/commit/dae522f63c05029298da1694038af9b24655ac05
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement inventory commands.

Changed paths:
  A engines/mutationofjb/commands/additemcommand.cpp
  A engines/mutationofjb/commands/additemcommand.h
  A engines/mutationofjb/commands/removeallitemscommand.cpp
  A engines/mutationofjb/commands/removeallitemscommand.h
  A engines/mutationofjb/commands/removeitemcommand.cpp
  A engines/mutationofjb/commands/removeitemcommand.h
    engines/mutationofjb/module.mk


diff --git a/engines/mutationofjb/commands/additemcommand.cpp b/engines/mutationofjb/commands/additemcommand.cpp
new file mode 100644
index 0000000..b0c7d21
--- /dev/null
+++ b/engines/mutationofjb/commands/additemcommand.cpp
@@ -0,0 +1,52 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/additemcommand.h"
+#include "mutationofjb/game.h"
+
+/*
+	"ADDITEM" " " <item>
+
+	Adds item to inventory.
+*/
+
+namespace MutationOfJB {
+
+bool AddItemCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("ADDITEM") || line.size() < 9) {
+		return false;
+	}
+
+	command = new AddItemCommand(line.c_str() + 8);
+	return true;
+}
+
+Command::ExecuteResult AddItemCommand::execute(GameData &gameData) {
+	gameData._inventory.addItem(_item);
+	return Finished;
+}
+
+Common::String AddItemCommand::debugString() const {
+	return Common::String::format("ADDITEM '%s'", _item.c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/additemcommand.h b/engines/mutationofjb/commands/additemcommand.h
new file mode 100644
index 0000000..cb4c131
--- /dev/null
+++ b/engines/mutationofjb/commands/additemcommand.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_ADDITEMCOMMAND_H
+#define MUTATIONOFJB_ADDITEMCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class AddItemCommandParser : public SeqCommandParser {
+public:
+	AddItemCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class AddItemCommand : public SeqCommand {
+public:
+	AddItemCommand(const Common::String &item) : _item(item) {}
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const override;
+private:
+	Common::String _item;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/removeallitemscommand.cpp b/engines/mutationofjb/commands/removeallitemscommand.cpp
new file mode 100644
index 0000000..8c6309f
--- /dev/null
+++ b/engines/mutationofjb/commands/removeallitemscommand.cpp
@@ -0,0 +1,52 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/removeallitemscommand.h"
+#include "mutationofjb/game.h"
+
+/*
+	"DELALLITEMS"
+
+	Removes all items from inventory.
+*/
+
+namespace MutationOfJB {
+
+bool RemoveAllItemsCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line != "DELALLITEMS") {
+		return false;
+	}
+
+	command = new RemoveAllItemsCommand();
+	return true;
+}
+
+Command::ExecuteResult RemoveAllItemsCommand::execute(GameData &gameData) {
+	gameData._inventory.removeAllItems();
+	return Finished;
+}
+
+Common::String RemoveAllItemsCommand::debugString() const {
+	return "DELALLITEM";
+}
+
+}
diff --git a/engines/mutationofjb/commands/removeallitemscommand.h b/engines/mutationofjb/commands/removeallitemscommand.h
new file mode 100644
index 0000000..166aed8
--- /dev/null
+++ b/engines/mutationofjb/commands/removeallitemscommand.h
@@ -0,0 +1,48 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_REMOVEALLITEMSCOMMAND_H
+#define MUTATIONOFJB_REMOVEALLITEMSCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+
+namespace MutationOfJB {
+
+class RemoveAllItemsCommandParser : public SeqCommandParser {
+public:
+	RemoveAllItemsCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class RemoveAllItemsCommand : public SeqCommand {
+public:
+	RemoveAllItemsCommand() {}
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const override;
+private:
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/removeitemcommand.cpp b/engines/mutationofjb/commands/removeitemcommand.cpp
new file mode 100644
index 0000000..c6aad0e
--- /dev/null
+++ b/engines/mutationofjb/commands/removeitemcommand.cpp
@@ -0,0 +1,52 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/removeitemcommand.h"
+#include "mutationofjb/game.h"
+
+/*
+	"DELITEM" " " <item>
+
+	Removes item from inventory.
+*/
+
+namespace MutationOfJB {
+
+bool RemoveItemCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (!line.hasPrefix("DELITEM") || line.size() < 9) {
+		return false;
+	}
+
+	command = new RemoveItemCommand(line.c_str() + 8);
+	return true;
+}
+
+Command::ExecuteResult RemoveItemCommand::execute(GameData &gameData) {
+	gameData._inventory.removeItem(_item);
+	return Finished;
+}
+
+Common::String RemoveItemCommand::debugString() const {
+	return Common::String::format("DELITEM '%s'", _item.c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/removeitemcommand.h b/engines/mutationofjb/commands/removeitemcommand.h
new file mode 100644
index 0000000..452a3b2
--- /dev/null
+++ b/engines/mutationofjb/commands/removeitemcommand.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_REMOVEITEMCOMMAND_H
+#define MUTATIONOFJB_REMOVEITEMCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class RemoveItemCommandParser : public SeqCommandParser {
+public:
+	RemoveItemCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class RemoveItemCommand : public SeqCommand {
+public:
+	RemoveItemCommand(const Common::String &item) : _item(item) {}
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const override;
+private:
+	Common::String _item;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index f719ae0..27c4f4b 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -1,11 +1,14 @@
 MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
+	commands/additemcommand.o \
 	commands/changecommand.o \
 	commands/command.o \
 	commands/conditionalcommand.o \
 	commands/endblockcommand.o \
 	commands/ifcommand.o \
+	commands/removeallitemscommand.o \
+	commands/removeitemcommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
 	debug.o \


Commit: b4dad9bca7593029ab368bc99f7bd96c71cbf4d8
    https://github.com/scummvm/scummvm/commit/b4dad9bca7593029ab368bc99f7bd96c71cbf4d8
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Show multiple script commands in showsection debug command.

Changed paths:
    engines/mutationofjb/commands/saycommand.cpp
    engines/mutationofjb/commands/saycommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
index 9448203..a0c9c79 100644
--- a/engines/mutationofjb/commands/saycommand.cpp
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -93,7 +93,9 @@ bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &par
 				break;
 			}
 		}
-		startPos++;
+		if (startPos != currentLine.size()) {
+			startPos++;
+		}
 
 		uint endPos;
 		for (endPos = startPos; endPos < currentLine.size(); ++endPos) {
@@ -120,7 +122,7 @@ bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &par
 		if (lineToSay.empty()) {
 			lineToSay = talkStr;
 		} else {
-			lineToSay = " " + talkStr;
+			lineToSay += " " + talkStr;
 		}
 
 		if (cont) {
diff --git a/engines/mutationofjb/commands/saycommand.h b/engines/mutationofjb/commands/saycommand.h
index 16303de..e2a1207 100644
--- a/engines/mutationofjb/commands/saycommand.h
+++ b/engines/mutationofjb/commands/saycommand.h
@@ -37,7 +37,7 @@ public:
 
 class SayCommand : public SeqCommand {
 public:
-	SayCommand(Common::String &lineToSay, Common::String &voiceFile, bool waitForPrevious, bool talkingAnimation) :
+	SayCommand(const Common::String &lineToSay, const Common::String &voiceFile, bool waitForPrevious, bool talkingAnimation) :
 		_lineToSay(lineToSay),
 		_voiceFile(voiceFile),
 		_waitForPrevious(waitForPrevious),
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 4861ab1..cf96fc6 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -24,6 +24,8 @@
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/commands/command.h"
+#include "mutationofjb/commands/seqcommand.h"
+#include "mutationofjb/commands/conditionalcommand.h"
 #include "common/debug-channels.h"
 #include "common/translation.h"
 #include "common/scummsys.h"
@@ -87,6 +89,31 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 	return true;
 }
 
+void Console::showIndent(int indentLevel) {
+	for (int i = 0; i < indentLevel; ++i) {
+		debugPrintf("  ");
+	}
+}
+
+void Console::showCommands(Command *command, int indentLevel) {
+	while (command) {
+		showIndent(indentLevel);
+		debugPrintf("%s\n", command->debugString().c_str());
+
+		if (SeqCommand *const seqCmd = dynamic_cast<SeqCommand *>(command)) {
+			command = seqCmd->next();
+		} else if (ConditionalCommand *const condCmd = dynamic_cast<ConditionalCommand *>(command)) {
+			showCommands(condCmd->getTrueCommand(), indentLevel + 1);
+			showIndent(indentLevel);
+			debugPrintf("ELSE\n");
+			showCommands(condCmd->getFalseCommand(), indentLevel + 1);
+			command = nullptr;
+		} else {
+			command = nullptr;
+		}
+	}
+}
+
 bool Console::cmd_showsection(int argc, const char **argv) {
 	if (argc == 4) {
 		Script *script = nullptr;
@@ -146,7 +173,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 
 			if (found) {
 				if (command) {
-					debugPrintf("%s\n", command->debugString().c_str());
+					showCommands(command);
 				}
 			} else {
 				debugPrintf("Section not found.\n");
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index a41baca..ee187cb 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -28,6 +28,7 @@
 namespace MutationOfJB {
 
 class MutationOfJBEngine;
+class Command;
 
 class Console : public GUI::Debugger {
 public:
@@ -36,6 +37,10 @@ public:
 private:
 	bool cmd_listsections(int argc, const char **argv);
 	bool cmd_showsection(int argc, const char **argv);
+
+	void showIndent(int indentLevel);
+	void showCommands(Command *command, int indentLevel = 0);
+
 	MutationOfJBEngine *_vm;
 };
 
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 98b1727..1b11545 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -31,6 +31,9 @@
 #include "mutationofjb/commands/endblockcommand.h"
 #include "mutationofjb/commands/changecommand.h"
 #include "mutationofjb/commands/saycommand.h"
+#include "mutationofjb/commands/additemcommand.h"
+#include "mutationofjb/commands/removeitemcommand.h"
+#include "mutationofjb/commands/removeallitemscommand.h"
 
 namespace MutationOfJB {
 
@@ -43,6 +46,9 @@ static CommandParser** getParsers() {
 		new ChangeStaticCommandParser,
 		new ChangeSceneCommandParser,
 		new SayCommandParser,
+		new AddItemCommandParser,
+		new RemoveItemCommandParser,
+		new RemoveAllItemsCommandParser,
 		nullptr
 	};
 


Commit: d3e281e24cb0ce522dc943b2d2a6bdde0766b62c
    https://github.com/scummvm/scummvm/commit/d3e281e24cb0ce522dc943b2d2a6bdde0766b62c
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix uninitialized ChangeOperation, fix parsing tag in IF command and add some comments.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index e9bb9cc..e4699eb 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -195,8 +195,9 @@ bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseCont
 }
 
 int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOperation &op) {
+	op = ChangeCommand::SetValue;
+
 	if (!val || !(*val)) {
-		op = ChangeCommand::SetValue;
 		return 0;
 	}
 
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 0643c3e..a2816c7 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -64,7 +64,7 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &pars
 	}
 
 	const char *const cstr = line.c_str();
-	const char tag = cstr[2];
+	const char tag = cstr[2] == ' ' ? 0 : cstr[2];
 	const uint8 sceneId = atoi(cstr + 3);
 	const uint8 objectId = atoi(cstr + 6);
 	const uint8 value = atoi(cstr + 9);
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 15fbd03..c79f8d9 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -92,7 +92,7 @@ bool Static::loadFromStream(Common::ReadStream &stream) {
 }
 
 bool Bitmap::loadFromStream(Common::ReadStream &stream) {
-	_unknown = stream.readByte();
+	_frame = stream.readByte();
 	_isVisible = stream.readByte();
 	_x1 = stream.readUint16LE();
 	_y1 = stream.readByte();
@@ -133,8 +133,7 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 		_bitmaps[i].loadFromStream(stream);
 	}
 
-	_obstacleY1 = stream.readByte();
-	_unknown386 = stream.readByte();
+	_obstacleY1 = stream.readUint16LE();
 	_palRotStart = stream.readByte();
 	_palRotEnd = stream.readByte();
 	_palRotPeriod = stream.readByte();
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 77e336f..550be2e 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -35,16 +35,33 @@ namespace MutationOfJB {
 static const uint8 MAX_STR_LENGTH = 0x14;
 
 struct Door {
+	/*
+		Door name.
+		Can be empty - deactivates door completely.
+	*/
 	char _name[MAX_STR_LENGTH + 1];
+	/*
+		Scene ID where the door leads.
+		Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
+	*/
 	uint8  _destSceneId;
+	/* X coordinate for player's position after going through the door. */
 	uint16 _destX;
+	/* Y coordinate for player's position after going through the door. */
 	uint16 _destY;
+	/* X coordinate of the door rectangle. */
 	uint16 _x;
+	/* Y coordinate of the door rectangle. */
 	uint8  _y;
+	/* Width of the door rectangle. */
 	uint16 _width;
+	/* Height of the door rectangle. */
 	uint8  _height;
+	/* X coordinate for position towards player will walk after clicking the door. */
 	uint16 _walkToX;
+	/* Y coordinate for position towards player will walk after clicking the door. */
 	uint8  _walkToY;
+	/* Unknown for now - likely not even used. */
 	uint8  _SP;
 
 	bool loadFromStream(Common::ReadStream &stream);
@@ -84,7 +101,7 @@ struct Static {
 };
 
 struct Bitmap {
-	uint8  _unknown;
+	uint8  _frame;
 	uint8  _isVisible;
 	uint16 _x1;
 	uint8  _y1;
@@ -118,8 +135,7 @@ struct Scene {
 
 	Bitmap _bitmaps[10];
 
-	uint8 _obstacleY1;
-	uint8 _unknown386;
+	uint16 _obstacleY1;
 	uint8 _palRotStart;
 	uint8 _palRotEnd;
 	uint8 _palRotPeriod;


Commit: 5854d310eee2c4a6fc4d189009631d4042ce0df8
    https://github.com/scummvm/scummvm/commit/5854d310eee2c4a6fc4d189009631d4042ce0df8
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix some code formatting issues.

Changed paths:
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/inventory.cpp
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/room.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h
    engines/mutationofjb/util.cpp
    engines/mutationofjb/util.h


diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index d590fa5..af9a608 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -110,7 +110,7 @@ public:
 
 class ChangeDoorCommand : public ChangeCommand {
 public:
-	ChangeDoorCommand(uint8 sceneId, uint8 doorId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+	ChangeDoorCommand(uint8 sceneId, uint8 doorId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, doorId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
@@ -119,7 +119,7 @@ public:
 
 class ChangeObjectCommand : public ChangeCommand {
 public:
-	ChangeObjectCommand(uint8 sceneId, uint8 objectId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+	ChangeObjectCommand(uint8 sceneId, uint8 objectId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, objectId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
@@ -128,7 +128,7 @@ public:
 
 class ChangeStaticCommand : public ChangeCommand {
 public:
-	ChangeStaticCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+	ChangeStaticCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, staticId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
@@ -137,7 +137,7 @@ public:
 
 class ChangeSceneCommand : public ChangeCommand {
 public:
-	ChangeSceneCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val)
+	ChangeSceneCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, staticId, reg, op, val)
 	{}
 	virtual ExecuteResult execute(GameData &gameData) override;
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index af9282f..2f1901d 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -28,7 +28,7 @@
 
 namespace MutationOfJB {
 
-bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *& command) {
+bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
 	if (line.empty()) {
 		return false;
 	}
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index a2816c7..da8efc1 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -94,12 +94,12 @@ IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative)
 {}
 
 Command::ExecuteResult IfCommand::execute(GameData &gameData) {
-	Scene* const scene = gameData.getScene(_sceneId);
+	Scene *const scene = gameData.getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
 
-	Object* const object = scene->getObject(_objectId);
+	Object *const object = scene->getObject(_objectId);
 	if (!object) {
 		return Finished;
 	}
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index 6c167e9..03d9538 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -25,7 +25,7 @@
 
 namespace MutationOfJB {
 
-void SeqCommandParser::transition(ScriptParseContext &, Command * oldCommand, Command * newCommand, CommandParser *) {
+void SeqCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
 		warning(_("Unexpected empty command in transition"));
 		return;
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 0fa30ab..c3455ce 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -28,8 +28,7 @@
 
 namespace MutationOfJB {
 
-class SeqCommandParser : public CommandParser
-{
+class SeqCommandParser : public CommandParser {
 public:
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
 };
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index c79f8d9..4b6d98e 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -27,8 +27,7 @@
 
 namespace MutationOfJB {
 
-static bool readString(Common::ReadStream &stream, char *str)
-{
+static bool readString(Common::ReadStream &stream, char *str) {
 	char buf[MAX_STR_LENGTH];
 	memset(str, 0, MAX_STR_LENGTH + 1);
 
@@ -172,8 +171,7 @@ Static *Scene::getStatic(uint8 staticId) {
 
 GameData::GameData() : _currentScene(0) {}
 
-Scene *GameData::getScene(uint8 sceneId)
-{
+Scene *GameData::getScene(uint8 sceneId) {
 	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
 		warning(_("Scene %d does not exist"), sceneId);
 		return nullptr;
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 550be2e..26f4d38 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -144,8 +144,7 @@ struct Scene {
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
-struct GameData
-{
+struct GameData {
 public:
 	GameData();
 	Scene *getScene(uint8 sceneId);
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
index 0f91a93..8dcec8d 100644
--- a/engines/mutationofjb/inventory.cpp
+++ b/engines/mutationofjb/inventory.cpp
@@ -60,7 +60,6 @@ void Inventory::rotateItemsRight(uint n) {
 	}
 
 	n %= _items.size();
-
 	reverseItems(0, _items.size() - 1);
 	reverseItems(0, n - 1);
 	reverseItems(n, _items.size() - 1);
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 3576fd5..17ff7b5 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -47,8 +47,7 @@ MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
  _room(nullptr),
  _screen(nullptr),
  _globalScript(nullptr),
- _localScript(nullptr)
-{
+ _localScript(nullptr) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -72,8 +71,7 @@ bool MutationOfJBEngine::loadGameData(bool partB) {
 	return true;
 }
 
-void MutationOfJBEngine::setupCursor()
-{
+void MutationOfJBEngine::setupCursor() {
 	const uint8 white[] = {0xFF, 0xFF, 0xFF};
 	const uint8 cursor[] = {0xFF};
 
@@ -120,7 +118,7 @@ Common::Error MutationOfJBEngine::run() {
 			}
 			case Common::EVENT_LBUTTONDOWN:
 			{
-				const Scene* const scene = _gameData->getScene(_gameData->_currentScene);
+				const Scene *const scene = _gameData->getScene(_gameData->_currentScene);
 				if (scene) {
 					for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
 						const Door &door = scene->_doors[i];
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index eae430a..8d8fbdb 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -98,7 +98,7 @@ void Room::loadPalette(EncryptedFile &file) {
 void Room::loadBackground(EncryptedFile &file, uint32 size) {
 	_screen->clear();
 
-	uint8 * const pixels = static_cast<uint8 *>(_screen->getPixels());
+	uint8 *const pixels = static_cast<uint8 *>(_screen->getPixels());
 	uint8 *ptr = pixels;
 	uint32 readBytes = 0;
 	uint32 lines = 0;
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 1b11545..5f4b526 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -37,8 +37,8 @@
 
 namespace MutationOfJB {
 
-static CommandParser** getParsers() {
-	static CommandParser* parsers[] = {
+static CommandParser **getParsers() {
+	static CommandParser *parsers[] = {
 		new IfCommandParser,
 		new EndBlockCommandParser,
 		new ChangeDoorCommandParser,
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index be04dc5..275ed78 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -34,7 +34,7 @@ namespace MutationOfJB {
 
 class Command;
 class ConditionalCommand;
-typedef Common::Array<Command*> Commands;
+typedef Common::Array<Command *> Commands;
 
 
 struct ActionInfo {
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 41d2a3a..0878748 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -26,10 +26,12 @@
 #include "engines/engine.h"
 
 namespace MutationOfJB {
-	void reportFileMissingError(const char *fileName) {
-		Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName);
-		GUIErrorMessage(errorMessage);
-		warning("%s", errorMessage.c_str());
-	}
+
+void reportFileMissingError(const char *fileName) {
+	Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName);
+	GUIErrorMessage(errorMessage);
+	warning("%s", errorMessage.c_str());
+}
+
 }
 
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 7dd7495..95a896d 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -24,7 +24,9 @@
 #define MUTATIONOFJB_UTIL_H
 
 namespace MutationOfJB {
-	void reportFileMissingError(const char *fileName);
+
+void reportFileMissingError(const char *fileName);
+
 }
 
 #endif


Commit: 37ae32e1d376aa672e3f0b45f3820fe244c7772b
    https://github.com/scummvm/scummvm/commit/37ae32e1d376aa672e3f0b45f3820fe244c7772b
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Don't store ActionInfo pointers, because they might be invalidated, and parse/show actions with two objects correctly.

Changed paths:
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 2f1901d..019c3b5 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -42,33 +42,43 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 
 	if (line.size() >= 4 && (line.hasPrefix("#L ") || line.hasPrefix("-L "))) {
 		ActionInfo ai = {ActionInfo::Look, line.c_str() + 3, "", firstChar == '#', nullptr};
-		parseCtx._lookActionInfos.push_back(ai);
-		_pendingActionInfos.push_back(&parseCtx._lookActionInfos.back());
+		parseCtx._actionInfos.push_back(ai);
+		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
 	} else if (line.size() >= 4 && (line.hasPrefix("#W ") || line.hasPrefix("-W "))) {
 		ActionInfo ai = {ActionInfo::Walk, line.c_str() + 3, "", firstChar == '#', nullptr};
-		parseCtx._walkActionInfos.push_back(ai);
-		_pendingActionInfos.push_back(&parseCtx._walkActionInfos.back());
+		parseCtx._actionInfos.push_back(ai);
+		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
 	} else if (line.size() >= 4 && (line.hasPrefix("#T ") || line.hasPrefix("-T "))) {
 		ActionInfo ai = {ActionInfo::Talk, line.c_str() + 3, "", firstChar == '#', nullptr};
-		parseCtx._talkActionInfos.push_back(ai);
-		_pendingActionInfos.push_back(&parseCtx._talkActionInfos.back());
+		parseCtx._actionInfos.push_back(ai);
+		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
 	} else if (line.size() >= 4 && (line.hasPrefix("#U ") || line.hasPrefix("-U "))) {
 		int secondObjPos = -1;
-		for (int i = 3; i < (int) line.size(); ++i) {
+		for (uint i = 3; i < line.size(); ++i) {
 			if (line[i] == ' ') {
 				secondObjPos = i + 1;
 				break;
 			}
 		}
+
+		Common::String obj1;
+		Common::String obj2;
+		if (secondObjPos == -1) {
+			obj1 = line.c_str() + 3;
+		} else {
+			obj1 = Common::String(line.c_str() + 3, secondObjPos - 4);
+			obj2 = line.c_str() + secondObjPos;
+		}
+
 		ActionInfo ai = {
 			ActionInfo::Use,
-			line.c_str() + 3,
-			(secondObjPos != -1) ? line.c_str() + secondObjPos : "",
+			obj1,
+			obj2,
 			firstChar == '#',
 			nullptr
 		};
-		parseCtx._useActionInfos.push_back(ai); 
-		_pendingActionInfos.push_back(&parseCtx._useActionInfos.back());
+		parseCtx._actionInfos.push_back(ai);
+		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
 	} else if ((line.hasPrefix("#ELSE") || line.hasPrefix("=ELSE"))) {
 		_elseFound = true;
 		_ifTag = 0;
@@ -101,12 +111,13 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		_ifTag = 0;
 	}
 
-	if (!_pendingActionInfos.empty() && newCommandParser != this) {
-		debug("Fixing pending action info.");
-		for (Common::Array<ActionInfo *>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
-			(*it)->_command = newCommand;
+	if (newCommandParser != this) {
+		if (!_pendingActionInfos.empty()) {
+			for (Common::Array<uint>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
+				parseCtx._actionInfos[*it]._command = newCommand;
+			}
+			_pendingActionInfos.clear();
 		}
-		_pendingActionInfos.clear();
 	}
 }
 
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 51f7993..24c4e5c 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -41,7 +41,8 @@ public:
 private:
 	bool _elseFound;
 	char _ifTag;
-	Common::Array<ActionInfo*> _pendingActionInfos;
+
+	Common::Array<uint> _pendingActionInfos;
 };
 
 class EndBlockCommand : public Command {
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index cf96fc6..92758af 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -77,7 +77,11 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getUseActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Use %s\n"), actionInfo._object1Name.c_str());
+					if (actionInfo._object2Name.empty()) {
+						debugPrintf(_("Use %s\n"), actionInfo._object1Name.c_str());
+					} else {
+						debugPrintf(_("Use %s %s\n"), actionInfo._object1Name.c_str(), actionInfo._object2Name.c_str());
+					}
 				}
 			} else {
 				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk) or 'U' (use).\n"));
@@ -115,7 +119,7 @@ void Console::showCommands(Command *command, int indentLevel) {
 }
 
 bool Console::cmd_showsection(int argc, const char **argv) {
-	if (argc == 4) {
+	if (argc >= 4) {
 		Script *script = nullptr;
 		if (strcmp(argv[1], "G") == 0) {
 			script = _vm->getGlobalScript();
@@ -161,7 +165,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getUseActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object1Name == argv[3]) {
+					if (actionInfo._object1Name == argv[3] && ((argc == 4 && actionInfo._object2Name.empty()) || (argc > 4 && actionInfo._object2Name == argv[4]))) {
 						found = true;
 						command = actionInfo._command;
 						break;
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 5f4b526..1df6c91 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -122,10 +122,20 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 		lastParser = currentParser;
 	}
 
-	_lookActionInfos = parseCtx._lookActionInfos;
-	_walkActionInfos = parseCtx._walkActionInfos;
-	_talkActionInfos = parseCtx._talkActionInfos;
-	_useActionInfos = parseCtx._useActionInfos;
+	for (ActionInfos::iterator it = parseCtx._actionInfos.begin(); it != parseCtx._actionInfos.end(); ++it) {
+		if (it->_action == ActionInfo::Look) {
+			_lookActionInfos.push_back(*it);
+		}
+		if (it->_action == ActionInfo::Walk) {
+			_walkActionInfos.push_back(*it);
+		}
+		if (it->_action == ActionInfo::Talk) {
+			_talkActionInfos.push_back(*it);
+		}
+		if (it->_action == ActionInfo::Use) {
+			_useActionInfos.push_back(*it);
+		}
+	}
 
 	Common::HashMap<Common::String, Command *> macros;
 	Common::HashMap<Common::String, Command *> labels;
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 275ed78..563dbc7 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -74,10 +74,7 @@ public:
 
 	ConditionalCommandInfos _pendingCondCommands;
 
-	ActionInfos _lookActionInfos;
-	ActionInfos _walkActionInfos;
-	ActionInfos _talkActionInfos;
-	ActionInfos _useActionInfos;
+	ActionInfos _actionInfos;
 
 private:
 };


Commit: fa9c8be1292e1f0174454193807f39b5c3ee52bf
    https://github.com/scummvm/scummvm/commit/fa9c8be1292e1f0174454193807f39b5c3ee52bf
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Convert section names to 7bit ASCII in debug console.

Changed paths:
    engines/mutationofjb/debug.cpp


diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 92758af..c4eb0a3 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -32,12 +32,25 @@
 
 namespace MutationOfJB {
 
-/*
-TODO
-static Common::String convertTo7bitASCII() {
-	return Common::String();
+/* Converts CP895 string to 7bit ASCII, so we can show it in the console. */
+static Common::String convertToASCII(const Common::String &str) {
+	static const char conversionTable[] = {
+		'C', 'u', 'e', 'd', 'a', 'D', 'T', 'c', 'e', 'E', 'L', 'I', 'l', 'l', 'A', 'A', /* 0x80-0x8F */
+		'E', 'z', 'Z', 'o', 'o', 'O', 'u', 'U', 'y', 'O', 'U', 'S', 'L', 'Y', 'R', 't', /* 0x90-0x9F */
+		'a', 'i', 'o', 'u', 'n', 'N', 'U', 'O', 's', 'r', 'r', 'R'						/* 0xA0-0xAB */
+	};
+
+	Common::String ret = str;
+	for (Common::String::iterator it = ret.begin(); it != ret.end(); ++it) {
+		const byte cp895Byte = reinterpret_cast<const byte &>(*it);
+		if (cp895Byte >= 0x80 && cp895Byte <= 0xAB) {
+			*it = conversionTable[cp895Byte - 0x80];
+		} else if (cp895Byte == 0xE1) { // ß
+			*it = 's';
+		}
+	}
+	return ret;
 }
-*/
 
 Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
 	registerCmd("listsections", WRAP_METHOD(Console, cmd_listsections));
@@ -59,28 +72,28 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getLookActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Look %s\n"), actionInfo._object1Name.c_str());
+					debugPrintf(_("Look %s\n"), convertToASCII(actionInfo._object1Name).c_str());
 				}
 			} else if (strcmp(argv[2], "W") == 0) {
 				const ActionInfos &actionInfos = script->getWalkActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Walk %s\n"), actionInfo._object1Name.c_str());
+					debugPrintf(_("Walk %s\n"), convertToASCII(actionInfo._object1Name).c_str());
 				}
 			} else if (strcmp(argv[2], "T") == 0) {
 				const ActionInfos &actionInfos = script->getTalkActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Talk %s\n"), actionInfo._object1Name.c_str());
+					debugPrintf(_("Talk %s\n"), convertToASCII(actionInfo._object1Name).c_str());
 				}
 			} else if (strcmp(argv[2], "U") == 0) {
 				const ActionInfos &actionInfos = script->getUseActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
 					if (actionInfo._object2Name.empty()) {
-						debugPrintf(_("Use %s\n"), actionInfo._object1Name.c_str());
+						debugPrintf(_("Use %s\n"), convertToASCII(actionInfo._object1Name).c_str());
 					} else {
-						debugPrintf(_("Use %s %s\n"), actionInfo._object1Name.c_str(), actionInfo._object2Name.c_str());
+						debugPrintf(_("Use %s %s\n"), convertToASCII(actionInfo._object1Name).c_str(), convertToASCII(actionInfo._object2Name).c_str());
 					}
 				}
 			} else {
@@ -135,7 +148,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getLookActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object1Name == argv[3]) {
+					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
 						found = true;
 						command = actionInfo._command;
 						break;
@@ -145,7 +158,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getWalkActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object1Name == argv[3]) {
+					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
 						found = true;
 						command = actionInfo._command;
 						break;
@@ -155,7 +168,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getTalkActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object1Name == argv[3]) {
+					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
 						found = true;
 						command = actionInfo._command;
 						break;
@@ -165,7 +178,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 				const ActionInfos &actionInfos = script->getUseActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object1Name == argv[3] && ((argc == 4 && actionInfo._object2Name.empty()) || (argc > 4 && actionInfo._object2Name == argv[4]))) {
+					if (convertToASCII(actionInfo._object1Name) == argv[3] && ((argc == 4 && actionInfo._object2Name.empty()) || (argc > 4 && convertToASCII(actionInfo._object2Name) == argv[4]))) {
 						found = true;
 						command = actionInfo._command;
 						break;


Commit: ae979a831091e04797f19d0c80210d745cd60f62
    https://github.com/scummvm/scummvm/commit/ae979a831091e04797f19d0c80210d745cd60f62
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for IFITEM command and fix parsing conditional commands that are right after #ELSE.

Changed paths:
  A engines/mutationofjb/commands/ifitemcommand.cpp
  A engines/mutationofjb/commands/ifitemcommand.h
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/conditionalcommand.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/inventory.cpp
    engines/mutationofjb/inventory.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index 13ea674..1e0b755 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -21,10 +21,24 @@
  */
 
 #include "mutationofjb/commands/conditionalcommand.h"
+#include "mutationofjb/script.h"
 #include "common/scummsys.h"
+#include "common/translation.h"
 
 namespace MutationOfJB {
 
+void ConditionalCommandParser::transition(ScriptParseContext &parseContext, Command *oldCommand, Command *newCommand, CommandParser *) {
+	if (!oldCommand || !newCommand) {
+		warning(_("Unexpected empty command in transition"));
+		return;
+	}
+
+	ConditionalCommand *const condCommand = static_cast<ConditionalCommand *>(oldCommand);
+	parseContext.addConditionalCommand(condCommand, _lastTag);
+	condCommand->setTrueCommand(newCommand);
+}
+
+
 ConditionalCommand::ConditionalCommand() :
 	_trueCommand(nullptr),
 	_falseCommand(nullptr),
diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
index 6f64720..4a4e7e1 100644
--- a/engines/mutationofjb/commands/conditionalcommand.h
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -28,6 +28,13 @@
 
 namespace MutationOfJB {
 
+class ConditionalCommandParser : public CommandParser {
+public:
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
+protected:
+	char _lastTag;
+};
+
 class ConditionalCommand : public Command {
 public:
 	ConditionalCommand();
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index da8efc1..44c6b18 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -31,12 +31,12 @@
 
 	IF command compares the value of the WX pseudo-register of the object in the specified scene.
 	If the values match, execution continues to the next line.
-	Otherwise execution continues after first "#ELSE" with the same <tag>.
+	Otherwise execution continues after first "#ELSE" or "=ELSE" with the same <tag>.
 	The logic can be reversed with exclamation mark at the end.
 
 	<tag> is always 1 character long, <sceneId> and <objectId> 2 characters long.
 
-	Please note that this does not work line you are used to from saner languages.
+	Please note that this does not work like you are used to from saner languages.
 	IF does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
 		IF something
 		IF something else
@@ -70,20 +70,11 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &pars
 	const uint8 value = atoi(cstr + 9);
 	const bool negative = (line.lastChar() == '!');
 
-	IfCommand *ifCommand = new IfCommand(sceneId, objectId, value, negative);
+	_lastTag = tag;
 
-	command = ifCommand;
-	parseContext.addConditionalCommand(ifCommand, tag);
-	return true;
-}
+	command = new IfCommand(sceneId, objectId, value, negative);
 
-void IfCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
-	if (!oldCommand || !newCommand) {
-		warning(_("Unexpected empty command in transition"));
-		return;
-	}
-
-	static_cast<IfCommand *>(oldCommand)->setTrueCommand(newCommand);
+	return true;
 }
 
 IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative) :
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
index 9462278..51350ce 100644
--- a/engines/mutationofjb/commands/ifcommand.h
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -30,16 +30,14 @@ namespace MutationOfJB {
 
 class ScriptParseContext;
 
-class IfCommandParser : public CommandParser {
+class IfCommandParser : public ConditionalCommandParser {
 public:
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
+private:
 };
 
 class IfCommand : public ConditionalCommand {
 public:
-	static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
-
 	IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
 	
 	virtual ExecuteResult execute(GameData &gameData) override;
@@ -50,8 +48,6 @@ private:
 	uint8 _objectId;
 	uint16 _value;
 	bool _negative;
-
-	bool _cachedResult;
 };
 
 }
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
new file mode 100644
index 0000000..6f467da
--- /dev/null
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -0,0 +1,90 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/ifitemcommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/util.h"
+#include "common/str.h"
+#include "common/translation.h"
+
+/*
+	"IFITEM" <item> ["!"]
+
+	IFITEM command tests whether an item is in the inventory.
+	If it is, execution continues to the next line.
+	Otherwise execution continues after first "#ELSE" or "=ELSE".
+	The logic can be reversed with exclamation mark at the end.
+
+	Please note that this does not work like you are used to from saner languages.
+	IFITEM does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+		IFITEM item1
+		IFITEM item2
+		#ELSE
+		...
+	This is effectively logical AND.
+*/
+
+namespace MutationOfJB {
+
+bool IfItemCommandParser::parse(const Common::String &line, ScriptParseContext &parseContext, Command *&command) {
+	if (line.size() < 8) {
+		return false;
+	}
+	
+	if (!line.hasPrefix("IFITEM")) {
+		return false;
+	}
+
+	const bool negative = (line.lastChar() == '!');
+	Common::String item(line.c_str() + 7);
+	if (negative) {
+		item.deleteLastChar(); // Remove '!'.
+	}
+
+	_lastTag = 0;
+	command = new IfItemCommand(item, negative);
+
+	return true;
+}
+
+
+IfItemCommand::IfItemCommand(const Common::String &item, bool negative) :
+	_item(item),
+	_negative(negative)
+{}
+
+Command::ExecuteResult IfItemCommand::execute(GameData &gameData) {
+	_cachedResult = gameData._inventory.hasItem(_item);
+	if (_negative) {
+		_cachedResult = !_cachedResult;
+	}
+
+	return Finished;
+}
+
+Common::String IfItemCommand::debugString() const {
+	return Common::String::format("IFITEM %s%s", _negative ? "NOT " : "", _item.c_str());
+}
+
+}
+
diff --git a/engines/mutationofjb/commands/ifitemcommand.h b/engines/mutationofjb/commands/ifitemcommand.h
new file mode 100644
index 0000000..0451786
--- /dev/null
+++ b/engines/mutationofjb/commands/ifitemcommand.h
@@ -0,0 +1,53 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_IFITEMCOMMAND_H
+#define MUTATIONOFJB_IFITEMCOMMAND_H
+
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class ScriptParseContext;
+
+class IfItemCommandParser : public ConditionalCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class IfItemCommand : public ConditionalCommand {
+public:
+	IfItemCommand(const Common::String &item, bool negative);
+	
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
+
+private:
+	Common::String _item;
+	bool _negative;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index c4eb0a3..87f0091 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -115,7 +115,7 @@ void Console::showIndent(int indentLevel) {
 void Console::showCommands(Command *command, int indentLevel) {
 	while (command) {
 		showIndent(indentLevel);
-		debugPrintf("%s\n", command->debugString().c_str());
+		debugPrintf("%s\n", convertToASCII(command->debugString()).c_str());
 
 		if (SeqCommand *const seqCmd = dynamic_cast<SeqCommand *>(command)) {
 			command = seqCmd->next();
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
index 8dcec8d..4a46548 100644
--- a/engines/mutationofjb/inventory.cpp
+++ b/engines/mutationofjb/inventory.cpp
@@ -32,6 +32,11 @@ const Inventory::Items &Inventory::getItems() const {
 	return _items;
 }
 
+bool Inventory::hasItem(const Common::String &item) const {
+	Items::const_iterator it = find(_items.begin(), _items.end(), item);
+	return (it != _items.end());
+}
+
 void Inventory::addItem(const Common::String &item) {
 	_items.push_back(item);
 
diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h
index 02cccc0..fe7ca67 100644
--- a/engines/mutationofjb/inventory.h
+++ b/engines/mutationofjb/inventory.h
@@ -34,6 +34,7 @@ public:
 	typedef Common::Array<Common::String> Items;
 
 	const Items &getItems() const;
+	bool hasItem(const Common::String &item) const;
 	void addItem(const Common::String &item);
 	void removeItem(const Common::String &item);
 	void removeAllItems();
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 27c4f4b..0dfe130 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	commands/conditionalcommand.o \
 	commands/endblockcommand.o \
 	commands/ifcommand.o \
+	commands/ifitemcommand.o \
 	commands/removeallitemscommand.o \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 1df6c91..c959f0f 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -28,6 +28,7 @@
 #include "common/debug.h"
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/ifcommand.h"
+#include "mutationofjb/commands/ifitemcommand.h"
 #include "mutationofjb/commands/endblockcommand.h"
 #include "mutationofjb/commands/changecommand.h"
 #include "mutationofjb/commands/saycommand.h"
@@ -39,6 +40,7 @@ namespace MutationOfJB {
 
 static CommandParser **getParsers() {
 	static CommandParser *parsers[] = {
+		new IfItemCommandParser,
 		new IfCommandParser,
 		new EndBlockCommandParser,
 		new ChangeDoorCommandParser,


Commit: 523f973e0a4ba16b278d546da9267a693924f957
    https://github.com/scummvm/scummvm/commit/523f973e0a4ba16b278d546da9267a693924f957
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support of IFPIGGY command.

Changed paths:
  A engines/mutationofjb/commands/ifpiggycommand.cpp
  A engines/mutationofjb/commands/ifpiggycommand.h
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
new file mode 100644
index 0000000..2ecd437
--- /dev/null
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -0,0 +1,71 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/ifpiggycommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/util.h"
+#include "common/str.h"
+#include "common/translation.h"
+
+/*
+	"IFPIGGY"
+
+	IFPIGGY command tests whether current loaded APK file (character animation) is "piggy.apk".
+	If it is, execution continues to the next line.
+	Otherwise execution continues after first "#ELSE" or "=ELSE".
+
+	Please note that this does not work like you are used to from saner languages.
+	IFPIGGY does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+		IFPIGGY
+		IFITEM someitem
+		#ELSE
+		...
+	This is effectively logical AND.
+*/
+
+namespace MutationOfJB {
+
+bool IfPiggyCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line != "IFPIGGY") {
+		return false;
+	}
+
+	_lastTag = 0;
+	command = new IfPiggyCommand();
+
+	return true;
+}
+
+
+Command::ExecuteResult IfPiggyCommand::execute(GameData &gameData) {
+	_cachedResult = gameData._currentAPK == "piggy.apk";
+
+	return Finished;
+}
+
+Common::String IfPiggyCommand::debugString() const {
+	return "IFPIGGY";
+}
+
+}
+
diff --git a/engines/mutationofjb/commands/ifpiggycommand.h b/engines/mutationofjb/commands/ifpiggycommand.h
new file mode 100644
index 0000000..3fb6826
--- /dev/null
+++ b/engines/mutationofjb/commands/ifpiggycommand.h
@@ -0,0 +1,49 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_IFPIGGYCOMMAND_H
+#define MUTATIONOFJB_IFPIGGYCOMMAND_H
+
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class ScriptParseContext;
+
+class IfPiggyCommandParser : public ConditionalCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class IfPiggyCommand : public ConditionalCommand {
+public:
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
+
+private:
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 26f4d38..6eafc79 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -153,6 +153,7 @@ public:
 
 	uint8 _currentScene;
 	Inventory _inventory;
+	Common::String _currentAPK;
 private:
 	Scene _scenes[45];
 
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 0dfe130..e66a4c8 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	commands/endblockcommand.o \
 	commands/ifcommand.o \
 	commands/ifitemcommand.o \
+	commands/ifpiggycommand.o \
 	commands/removeallitemscommand.o \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index c959f0f..771a990 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -29,6 +29,7 @@
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/ifcommand.h"
 #include "mutationofjb/commands/ifitemcommand.h"
+#include "mutationofjb/commands/ifpiggycommand.h"
 #include "mutationofjb/commands/endblockcommand.h"
 #include "mutationofjb/commands/changecommand.h"
 #include "mutationofjb/commands/saycommand.h"
@@ -40,6 +41,7 @@ namespace MutationOfJB {
 
 static CommandParser **getParsers() {
 	static CommandParser *parsers[] = {
+		new IfPiggyCommandParser,
 		new IfItemCommandParser,
 		new IfCommandParser,
 		new EndBlockCommandParser,


Commit: 54c2c0aee6e371f8058c50e0a4113f5b5492169e
    https://github.com/scummvm/scummvm/commit/54c2c0aee6e371f8058c50e0a4113f5b5492169e
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for labels and goto.

Changed paths:
  A engines/mutationofjb/commands/gotocommand.cpp
  A engines/mutationofjb/commands/gotocommand.h
  A engines/mutationofjb/commands/labelcommand.cpp
  A engines/mutationofjb/commands/labelcommand.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
new file mode 100644
index 0000000..77c474b
--- /dev/null
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -0,0 +1,80 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/gotocommand.h"
+#include "mutationofjb/commands/labelcommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+
+/*
+	GOTO <label>
+
+	Jumps to a label.
+*/
+
+namespace MutationOfJB {
+
+bool GotoCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
+	if (line.firstChar() != '_') {
+		return false;
+	}
+
+	Common::String label = line.c_str() + 1;
+	GotoCommand *gotoCmd = new GotoCommand();
+
+	if (parseCtx._labels.contains(label)) {
+		// We already have the label, set it.
+		gotoCmd->setLabelCommand(parseCtx._labels[label]);
+	} else {
+		// Label is after goto, add to pending list.
+		parseCtx._pendingGotos[label].push_back(gotoCmd);
+	}
+
+	command = gotoCmd;
+
+	return true;
+}
+
+
+void GotoCommand::setLabelCommand(LabelCommand *labelCmd) {
+	_labelCommand = labelCmd;
+}
+
+Command::ExecuteResult GotoCommand::execute(GameData &) {
+	// Intentionally empty.
+
+	return Finished;
+}
+
+Command *GotoCommand::next() const {
+	return _labelCommand;
+}
+
+Common::String GotoCommand::debugString() const {
+	if (!_labelCommand) {
+		return "GOTO (null)";
+	}
+
+	return Common::String::format("GOTO %s", _labelCommand->getName().c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/gotocommand.h b/engines/mutationofjb/commands/gotocommand.h
new file mode 100644
index 0000000..436dd44
--- /dev/null
+++ b/engines/mutationofjb/commands/gotocommand.h
@@ -0,0 +1,55 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_GOTOCOMMAND_H
+#define MUTATIONOFJB_GOTOCOMMAND_H
+
+#include "mutationofjb/commands/command.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class LabelCommand;
+
+class GotoCommandParser : public CommandParser {
+public:
+	GotoCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class GotoCommand : public Command {
+public:
+	GotoCommand() : _labelCommand(nullptr) {}
+
+	void setLabelCommand(LabelCommand *labelCmd);
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Command *next() const override;
+	virtual Common::String debugString() const override;
+private:
+	LabelCommand *_labelCommand;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
new file mode 100644
index 0000000..4540a83
--- /dev/null
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/labelcommand.h"
+#include "mutationofjb/commands/gotocommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+
+/*
+	<label> ":"
+
+	Creates a label.
+*/
+
+namespace MutationOfJB {
+
+bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
+	if (line.lastChar() != ':') {
+		return false;
+	}
+
+	Common::String label = line;
+	label.deleteLastChar();
+
+	LabelCommand *labelCmd = new LabelCommand(label);
+	if (!parseCtx._labels.contains(line)) {
+		parseCtx._labels[line] = labelCmd;
+	} else {
+		warning("Label '%s' already exists", label.c_str());
+	}
+
+	if (parseCtx._pendingGotos.contains(label)) {
+		GotoCommands &gotos = parseCtx._pendingGotos[label];
+		for (GotoCommands::const_iterator it = gotos.begin(); it != gotos.end(); ++it) {
+			(*it)->setLabelCommand(labelCmd);
+		}
+		gotos.clear();
+	}
+
+	command = labelCmd;
+	return true;
+}
+
+const Common::String &LabelCommand::getName() const
+{
+	return _name;
+}
+
+Command::ExecuteResult LabelCommand::execute(GameData &) {
+	// Intentionally empty.
+
+	return Finished;
+}
+
+Common::String LabelCommand::debugString() const {
+	return Common::String::format("LABEL %s", _name.c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/labelcommand.h b/engines/mutationofjb/commands/labelcommand.h
new file mode 100644
index 0000000..389c759
--- /dev/null
+++ b/engines/mutationofjb/commands/labelcommand.h
@@ -0,0 +1,51 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_LABELCOMMAND_H
+#define MUTATIONOFJB_LABELCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class LabelCommandParser : public SeqCommandParser {
+public:
+	LabelCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class LabelCommand : public SeqCommand {
+public:
+	LabelCommand(const Common::String &name) : _name(name) {}
+	const Common::String &getName() const;
+
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const override;
+private:
+	Common::String _name;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index e66a4c8..8ad1507 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -6,9 +6,11 @@ MODULE_OBJS := \
 	commands/command.o \
 	commands/conditionalcommand.o \
 	commands/endblockcommand.o \
+	commands/gotocommand.o \
 	commands/ifcommand.o \
 	commands/ifitemcommand.o \
 	commands/ifpiggycommand.o \
+	commands/labelcommand.o \
 	commands/removeallitemscommand.o \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 771a990..95c2bdc 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -36,6 +36,8 @@
 #include "mutationofjb/commands/additemcommand.h"
 #include "mutationofjb/commands/removeitemcommand.h"
 #include "mutationofjb/commands/removeallitemscommand.h"
+#include "mutationofjb/commands/labelcommand.h"
+#include "mutationofjb/commands/gotocommand.h"
 
 namespace MutationOfJB {
 
@@ -53,6 +55,8 @@ static CommandParser **getParsers() {
 		new AddItemCommandParser,
 		new RemoveItemCommandParser,
 		new RemoveAllItemsCommandParser,
+		new GotoCommandParser,
+		new LabelCommandParser,
 		nullptr
 	};
 
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 563dbc7..7c5e569 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -24,15 +24,19 @@
 #define MUTATIONOFJB_SCRIPT_H 
 
 #include "common/array.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
 
 namespace Common {
-	class SeekableReadStream;
-	class String;
+class SeekableReadStream;
+class String;
 }
 
 namespace MutationOfJB {
 
 class Command;
+class LabelCommand;
+class GotoCommand;
 class ConditionalCommand;
 typedef Common::Array<Command *> Commands;
 
@@ -53,6 +57,7 @@ struct ActionInfo {
 };
 
 typedef Common::Array<ActionInfo> ActionInfos;
+typedef Common::Array<GotoCommand *> GotoCommands;
 
 class ScriptParseContext
 {
@@ -71,9 +76,14 @@ public:
 		char _tag;
 	};
 	typedef Common::Array<ConditionalCommandInfo> ConditionalCommandInfos;
-
 	ConditionalCommandInfos _pendingCondCommands;
 
+	typedef Common::HashMap<Common::String, LabelCommand *> LabelMap;
+	LabelMap _labels;
+
+	typedef Common::HashMap<Common::String, GotoCommands> PendingGotoMap;
+	PendingGotoMap _pendingGotos;
+
 	ActionInfos _actionInfos;
 
 private:


Commit: fb75e483e43a00bb20329dd0b5db92a3588a2d2e
    https://github.com/scummvm/scummvm/commit/fb75e483e43a00bb20329dd0b5db92a3588a2d2e
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix GOTO command.

Changed paths:
    engines/mutationofjb/commands/gotocommand.cpp


diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
index 77c474b..92cce14 100644
--- a/engines/mutationofjb/commands/gotocommand.cpp
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -34,11 +34,11 @@
 namespace MutationOfJB {
 
 bool GotoCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
-	if (line.firstChar() != '_') {
+	if (line.size() < 6 || !line.hasPrefix("GOTO")) {
 		return false;
 	}
 
-	Common::String label = line.c_str() + 1;
+	Common::String label = line.c_str() + 6;
 	GotoCommand *gotoCmd = new GotoCommand();
 
 	if (parseCtx._labels.contains(label)) {


Commit: 1d84041508b3acaf7a47fde81e7e334c004507b8
    https://github.com/scummvm/scummvm/commit/1d84041508b3acaf7a47fde81e7e334c004507b8
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add Game class.

Changed paths:
  A engines/mutationofjb/gamedata.cpp
  A engines/mutationofjb/gamedata.h
    engines/mutationofjb/commands/additemcommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/gotocommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/ifpiggycommand.cpp
    engines/mutationofjb/commands/labelcommand.cpp
    engines/mutationofjb/commands/removeallitemscommand.cpp
    engines/mutationofjb/commands/removeitemcommand.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/additemcommand.cpp b/engines/mutationofjb/commands/additemcommand.cpp
index b0c7d21..58ec567 100644
--- a/engines/mutationofjb/commands/additemcommand.cpp
+++ b/engines/mutationofjb/commands/additemcommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/additemcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 
 /*
 	"ADDITEM" " " <item>
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index af9a608..5ba386b 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/seqcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
index 92cce14..40560f3 100644
--- a/engines/mutationofjb/commands/gotocommand.cpp
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -22,7 +22,7 @@
 
 #include "mutationofjb/commands/gotocommand.h"
 #include "mutationofjb/commands/labelcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 
 /*
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 44c6b18..f78335a 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/ifcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 #include "common/str.h"
 #include "common/translation.h"
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index 6f467da..7512ba5 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/ifitemcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
index 2ecd437..cad0a14 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.cpp
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/ifpiggycommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
index 4540a83..15a10ca 100644
--- a/engines/mutationofjb/commands/labelcommand.cpp
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -22,7 +22,6 @@
 
 #include "mutationofjb/commands/labelcommand.h"
 #include "mutationofjb/commands/gotocommand.h"
-#include "mutationofjb/game.h"
 #include "mutationofjb/script.h"
 
 /*
diff --git a/engines/mutationofjb/commands/removeallitemscommand.cpp b/engines/mutationofjb/commands/removeallitemscommand.cpp
index 8c6309f..8043864 100644
--- a/engines/mutationofjb/commands/removeallitemscommand.cpp
+++ b/engines/mutationofjb/commands/removeallitemscommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/removeallitemscommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 
 /*
 	"DELALLITEMS"
diff --git a/engines/mutationofjb/commands/removeitemcommand.cpp b/engines/mutationofjb/commands/removeitemcommand.cpp
index c6aad0e..e4d9601 100644
--- a/engines/mutationofjb/commands/removeitemcommand.cpp
+++ b/engines/mutationofjb/commands/removeitemcommand.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "mutationofjb/commands/removeitemcommand.h"
-#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 
 /*
 	"DELITEM" " " <item>
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 87f0091..e639324 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "mutationofjb/debug.h"
+#include "mutationofjb/game.h"
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/commands/command.h"
@@ -61,9 +62,9 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 	if (argc == 3) {
 		Script *script = nullptr;
 		if (strcmp(argv[1], "G") == 0) {
-			script = _vm->getGlobalScript();
+			script = _vm->getGame().getGlobalScript();
 		} else if (strcmp(argv[1], "L") == 0) {
-			script = _vm->getLocalScript();
+			script = _vm->getGame().getLocalScript();
 		}
 		if (!script) {
 			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
@@ -135,9 +136,9 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 	if (argc >= 4) {
 		Script *script = nullptr;
 		if (strcmp(argv[1], "G") == 0) {
-			script = _vm->getGlobalScript();
+			script = _vm->getGame().getGlobalScript();
 		} else if (strcmp(argv[1], "L") == 0) {
-			script = _vm->getLocalScript();
+			script = _vm->getGame().getLocalScript();
 		}
 		if (!script) {
 			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 4b6d98e..6fe6b95 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -21,171 +21,64 @@
  */
 
 #include "mutationofjb/game.h"
-#include "common/stream.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/mutationofjb.h"
+#include "mutationofjb/room.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/util.h"
 #include "common/util.h"
-#include "common/translation.h"
 
 namespace MutationOfJB {
 
-static bool readString(Common::ReadStream &stream, char *str) {
-	char buf[MAX_STR_LENGTH];
-	memset(str, 0, MAX_STR_LENGTH + 1);
+Game::Game(MutationOfJBEngine* vm) : _vm(vm) {
+	_gameData = new GameData;
+	loadGameData(false);
 
-	uint8 len = stream.readByte();
-	stream.read(buf, MAX_STR_LENGTH);
+	EncryptedFile globalScriptFile;
+	globalScriptFile.open("global.atn");
+	_globalScript = new Script;
+	_globalScript->loadFromStream(globalScriptFile);
+	globalScriptFile.close();
 
-	len = MIN(len, MAX_STR_LENGTH);
-	memcpy(str, buf, len);
+	_localScript = nullptr;
+	_room = new Room(_vm->getScreen());
 
-	return true;
-}
-
-bool Door::loadFromStream(Common::ReadStream &stream) {
-	readString(stream, _name);
-
-	_destSceneId = stream.readByte();
-	_destX = stream.readUint16LE();
-	_destY = stream.readUint16LE();
-	_x = stream.readUint16LE();
-	_y = stream.readByte();
-	_width = stream.readUint16LE();
-	_height = stream.readByte();
-	_walkToX = stream.readUint16LE();
-	_walkToY = stream.readByte();
-	_SP = stream.readByte();
-
-	return true;
+	changeScene(13, false); // Initial scene.
 }
 
-bool Object::loadFromStream(Common::ReadStream &stream) {
-	_AC = stream.readByte();
-	_FA = stream.readByte();
-	_FR = stream.readByte();
-	_NA = stream.readByte();
-	_FS = stream.readByte();
-	_unknown = stream.readByte();
-	_CA = stream.readByte();
-	_x = stream.readUint16LE();
-	_y = stream.readByte();
-	_XL = stream.readUint16LE();
-	_YL = stream.readByte();
-	_WX = stream.readUint16LE();
-	_WY = stream.readByte();
-	_SP = stream.readByte();
-
-	return true;
+GameData &Game::getGameData() {
+	return *_gameData;
 }
 
-bool Static::loadFromStream(Common::ReadStream &stream) {
-	_active = stream.readByte();
-	readString(stream, _name);
-	_x = stream.readUint16LE();
-	_y = stream.readByte();
-	_width = stream.readUint16LE();
-	_height = stream.readByte();
-	_walkToX = stream.readUint16LE();
-	_walkToY = stream.readByte();
-	_SP = stream.readByte();
-
-	return true;
+Script *Game::getGlobalScript() const {
+	return _globalScript;
 }
 
-bool Bitmap::loadFromStream(Common::ReadStream &stream) {
-	_frame = stream.readByte();
-	_isVisible = stream.readByte();
-	_x1 = stream.readUint16LE();
-	_y1 = stream.readByte();
-	_x2 = stream.readUint16LE();
-	_y2 = stream.readByte();
-
-	return true;
+Script *Game::getLocalScript() const {
+	return _localScript;
 }
 
-bool Scene::loadFromStream(Common::ReadStream &stream) {
-	int i;
-
-	_startup = stream.readByte();
-	_unknown001 = stream.readByte();
-	_unknown002 = stream.readByte();
-	_unknown003 = stream.readByte();
-	_DL = stream.readByte();
-
-	_noDoors = stream.readByte();
-	_noDoors = MIN(_noDoors, (uint8) ARRAYSIZE(_doors));
-	for (i = 0; i < ARRAYSIZE(_doors); ++i) {
-		_doors[i].loadFromStream(stream);
-	}
-
-	_noObjects = stream.readByte();
-	_noObjects = MIN(_noObjects, (uint8) ARRAYSIZE(_objects));
-	for (i = 0; i < ARRAYSIZE(_objects); ++i) {
-		_objects[i].loadFromStream(stream);
+bool Game::loadGameData(bool partB) {
+	EncryptedFile file;
+	const char *fileName = !partB ? "startup.dat" : "startupb.dat";
+	file.open(fileName);
+	if (!file.isOpen()) {
+		reportFileMissingError(fileName);
+		return false;
 	}
 
-	_noStatics = stream.readByte();
-	_noStatics = MIN(_noStatics, (uint8) ARRAYSIZE(_statics));
-	for (i = 0; i < ARRAYSIZE(_statics); ++i) {
-		_statics[i].loadFromStream(stream);
-	}
+	_gameData->loadFromStream(file);
 
-	for (i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
-		_bitmaps[i].loadFromStream(stream);
-	}
-
-	_obstacleY1 = stream.readUint16LE();
-	_palRotStart = stream.readByte();
-	_palRotEnd = stream.readByte();
-	_palRotPeriod = stream.readByte();
-	stream.read(_unknown38A, 80);
+	file.close();
 
 	return true;
 }
 
-Door *Scene::getDoor(uint8 doorId) {
-	if (doorId == 0 || doorId > _noDoors) {
-		warning(_("Door %d does not exist"), doorId);
-		return nullptr;
-	}
 
-	return &_doors[doorId - 1];
-}
-
-Object *Scene::getObject(uint8 objectId) {
-	if (objectId == 0 || objectId > _noObjects) {
-		warning(_("Object %d does not exist"), objectId);
-		return nullptr;
-	}
-
-	return &_objects[objectId - 1];
-}
-
-Static *Scene::getStatic(uint8 staticId) {
-	if (staticId == 0 || staticId > _noStatics) {
-		warning(_("Static %d does not exist"), staticId);
-		return nullptr;
-	}
-
-	return &_statics[staticId - 1];
-}
-
-
-GameData::GameData() : _currentScene(0) {}
-
-Scene *GameData::getScene(uint8 sceneId) {
-	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
-		warning(_("Scene %d does not exist"), sceneId);
-		return nullptr;
-	}
-
-	return &_scenes[sceneId - 1];
-}
-
-bool GameData::loadFromStream(Common::ReadStream &stream) {
-	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
-		_scenes[i].loadFromStream(stream);
-	}
-
-	return true;
+void Game::changeScene(uint8 sceneId, bool partB) {
+	_gameData->_currentScene = sceneId;
+	_room->load(_gameData->_currentScene, partB);
 }
 
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 6eafc79..4b4ab43 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -24,139 +24,33 @@
 #define MUTATIONOFJB_GAME_H
 
 #include "common/scummsys.h"
-#include "mutationofjb/inventory.h"
-
-namespace Common {
-	class ReadStream;
-}
 
 namespace MutationOfJB {
 
-static const uint8 MAX_STR_LENGTH = 0x14;
-
-struct Door {
-	/*
-		Door name.
-		Can be empty - deactivates door completely.
-	*/
-	char _name[MAX_STR_LENGTH + 1];
-	/*
-		Scene ID where the door leads.
-		Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
-	*/
-	uint8  _destSceneId;
-	/* X coordinate for player's position after going through the door. */
-	uint16 _destX;
-	/* Y coordinate for player's position after going through the door. */
-	uint16 _destY;
-	/* X coordinate of the door rectangle. */
-	uint16 _x;
-	/* Y coordinate of the door rectangle. */
-	uint8  _y;
-	/* Width of the door rectangle. */
-	uint16 _width;
-	/* Height of the door rectangle. */
-	uint8  _height;
-	/* X coordinate for position towards player will walk after clicking the door. */
-	uint16 _walkToX;
-	/* Y coordinate for position towards player will walk after clicking the door. */
-	uint8  _walkToY;
-	/* Unknown for now - likely not even used. */
-	uint8  _SP;
-
-	bool loadFromStream(Common::ReadStream &stream);
-};
-
-struct Object {
-	uint8  _AC;
-	uint8  _FA;
-	uint8  _FR;
-	uint8  _NA;
-	uint8  _FS;
-	uint8  _unknown;
-	uint8  _CA;
-	uint16 _x;
-	uint8  _y;
-	uint16 _XL;
-	uint8  _YL;
-	uint16 _WX;
-	uint8  _WY;
-	uint8  _SP;
-
-	bool loadFromStream(Common::ReadStream &stream);
-};
-
-struct Static {
-	uint8  _active;
-	char _name[MAX_STR_LENGTH + 1];
-	uint16 _x;
-	uint8  _y;
-	uint16 _width;
-	uint8  _height;
-	uint16 _walkToX;
-	uint8  _walkToY;
-	uint8  _SP;
-
-	bool loadFromStream(Common::ReadStream &stream);
-};
-
-struct Bitmap {
-	uint8  _frame;
-	uint8  _isVisible;
-	uint16 _x1;
-	uint8  _y1;
-	uint16 _x2;
-	uint8  _y2;
-
-	bool loadFromStream(Common::ReadStream &stream);
-};
+class MutationOfJBEngine;
+class GameData;
+class Script;
+class Room;
 
-
-struct Scene {
-
-	Door *getDoor(uint8 objectId);
-	Object *getObject(uint8 objectId);
-	Static *getStatic(uint8 staticId);
-
-	uint8 _startup;
-	uint8 _unknown001;
-	uint8 _unknown002;
-	uint8 _unknown003;
-	uint8 _DL;
-
-	uint8 _noDoors;
-	Door _doors[5];
-
-	uint8 _noObjects;
-	Object _objects[9];
-
-	uint8 _noStatics;
-	Static _statics[15];
-
-	Bitmap _bitmaps[10];
-
-	uint16 _obstacleY1;
-	uint8 _palRotStart;
-	uint8 _palRotEnd;
-	uint8 _palRotPeriod;
-	uint8 _unknown38A[80];
-
-	bool loadFromStream(Common::ReadStream &stream);
-};
-
-struct GameData {
+class Game {
 public:
-	GameData();
-	Scene *getScene(uint8 sceneId);
+	Game(MutationOfJBEngine* vm);
+	GameData &getGameData();
+
+	Script *getGlobalScript() const;
+	Script *getLocalScript() const;
 
-	bool loadFromStream(Common::ReadStream &stream);
+	void changeScene(uint8 sceneId, bool partB);
 
-	uint8 _currentScene;
-	Inventory _inventory;
-	Common::String _currentAPK;
 private:
-	Scene _scenes[45];
+	bool loadGameData(bool partB);
+
+	MutationOfJBEngine *_vm;
 
+	GameData *_gameData;
+	Script *_globalScript;
+	Script *_localScript;
+	Room *_room;
 };
 
 }
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
new file mode 100644
index 0000000..cc85da3
--- /dev/null
+++ b/engines/mutationofjb/gamedata.cpp
@@ -0,0 +1,193 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/gamedata.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/translation.h"
+
+namespace MutationOfJB {
+
+static bool readString(Common::ReadStream &stream, char *str) {
+	char buf[MAX_STR_LENGTH];
+	memset(str, 0, MAX_STR_LENGTH + 1);
+
+	uint8 len = stream.readByte();
+	stream.read(buf, MAX_STR_LENGTH);
+
+	len = MIN(len, MAX_STR_LENGTH);
+	memcpy(str, buf, len);
+
+	return true;
+}
+
+bool Door::loadFromStream(Common::ReadStream &stream) {
+	readString(stream, _name);
+
+	_destSceneId = stream.readByte();
+	_destX = stream.readUint16LE();
+	_destY = stream.readUint16LE();
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_width = stream.readUint16LE();
+	_height = stream.readByte();
+	_walkToX = stream.readUint16LE();
+	_walkToY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Object::loadFromStream(Common::ReadStream &stream) {
+	_AC = stream.readByte();
+	_FA = stream.readByte();
+	_FR = stream.readByte();
+	_NA = stream.readByte();
+	_FS = stream.readByte();
+	_unknown = stream.readByte();
+	_CA = stream.readByte();
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_XL = stream.readUint16LE();
+	_YL = stream.readByte();
+	_WX = stream.readUint16LE();
+	_WY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Static::loadFromStream(Common::ReadStream &stream) {
+	_active = stream.readByte();
+	readString(stream, _name);
+	_x = stream.readUint16LE();
+	_y = stream.readByte();
+	_width = stream.readUint16LE();
+	_height = stream.readByte();
+	_walkToX = stream.readUint16LE();
+	_walkToY = stream.readByte();
+	_SP = stream.readByte();
+
+	return true;
+}
+
+bool Bitmap::loadFromStream(Common::ReadStream &stream) {
+	_frame = stream.readByte();
+	_isVisible = stream.readByte();
+	_x1 = stream.readUint16LE();
+	_y1 = stream.readByte();
+	_x2 = stream.readUint16LE();
+	_y2 = stream.readByte();
+
+	return true;
+}
+
+bool Scene::loadFromStream(Common::ReadStream &stream) {
+	int i;
+
+	_startup = stream.readByte();
+	_unknown001 = stream.readByte();
+	_unknown002 = stream.readByte();
+	_unknown003 = stream.readByte();
+	_DL = stream.readByte();
+
+	_noDoors = stream.readByte();
+	_noDoors = MIN(_noDoors, (uint8) ARRAYSIZE(_doors));
+	for (i = 0; i < ARRAYSIZE(_doors); ++i) {
+		_doors[i].loadFromStream(stream);
+	}
+
+	_noObjects = stream.readByte();
+	_noObjects = MIN(_noObjects, (uint8) ARRAYSIZE(_objects));
+	for (i = 0; i < ARRAYSIZE(_objects); ++i) {
+		_objects[i].loadFromStream(stream);
+	}
+
+	_noStatics = stream.readByte();
+	_noStatics = MIN(_noStatics, (uint8) ARRAYSIZE(_statics));
+	for (i = 0; i < ARRAYSIZE(_statics); ++i) {
+		_statics[i].loadFromStream(stream);
+	}
+
+	for (i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
+		_bitmaps[i].loadFromStream(stream);
+	}
+
+	_obstacleY1 = stream.readUint16LE();
+	_palRotStart = stream.readByte();
+	_palRotEnd = stream.readByte();
+	_palRotPeriod = stream.readByte();
+	stream.read(_unknown38A, 80);
+
+	return true;
+}
+
+Door *Scene::getDoor(uint8 doorId) {
+	if (doorId == 0 || doorId > _noDoors) {
+		warning(_("Door %d does not exist"), doorId);
+		return nullptr;
+	}
+
+	return &_doors[doorId - 1];
+}
+
+Object *Scene::getObject(uint8 objectId) {
+	if (objectId == 0 || objectId > _noObjects) {
+		warning(_("Object %d does not exist"), objectId);
+		return nullptr;
+	}
+
+	return &_objects[objectId - 1];
+}
+
+Static *Scene::getStatic(uint8 staticId) {
+	if (staticId == 0 || staticId > _noStatics) {
+		warning(_("Static %d does not exist"), staticId);
+		return nullptr;
+	}
+
+	return &_statics[staticId - 1];
+}
+
+
+GameData::GameData()
+	: _currentScene(0),
+	_partB(false) {}
+
+Scene *GameData::getScene(uint8 sceneId) {
+	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
+		warning(_("Scene %d does not exist"), sceneId);
+		return nullptr;
+	}
+
+	return &_scenes[sceneId - 1];
+}
+
+bool GameData::loadFromStream(Common::ReadStream &stream) {
+	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
+		_scenes[i].loadFromStream(stream);
+	}
+
+	return true;
+}
+
+}
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
new file mode 100644
index 0000000..23e238a
--- /dev/null
+++ b/engines/mutationofjb/gamedata.h
@@ -0,0 +1,165 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_GAMEDATA_H
+#define MUTATIONOFJB_GAMEDATA_H
+
+#include "common/scummsys.h"
+#include "mutationofjb/inventory.h"
+
+namespace Common {
+class ReadStream;
+}
+
+namespace MutationOfJB {
+
+static const uint8 MAX_STR_LENGTH = 0x14;
+
+struct Door {
+	/*
+		Door name.
+		Can be empty - deactivates door completely.
+	*/
+	char _name[MAX_STR_LENGTH + 1];
+	/*
+		Scene ID where the door leads.
+		Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
+	*/
+	uint8  _destSceneId;
+	/* X coordinate for player's position after going through the door. */
+	uint16 _destX;
+	/* Y coordinate for player's position after going through the door. */
+	uint16 _destY;
+	/* X coordinate of the door rectangle. */
+	uint16 _x;
+	/* Y coordinate of the door rectangle. */
+	uint8  _y;
+	/* Width of the door rectangle. */
+	uint16 _width;
+	/* Height of the door rectangle. */
+	uint8  _height;
+	/* X coordinate for position towards player will walk after clicking the door. */
+	uint16 _walkToX;
+	/* Y coordinate for position towards player will walk after clicking the door. */
+	uint8  _walkToY;
+	/* Unknown for now - likely not even used. */
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Object {
+	uint8  _AC;
+	uint8  _FA;
+	uint8  _FR;
+	uint8  _NA;
+	uint8  _FS;
+	uint8  _unknown;
+	uint8  _CA;
+	uint16 _x;
+	uint8  _y;
+	uint16 _XL;
+	uint8  _YL;
+	uint16 _WX;
+	uint8  _WY;
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Static {
+	uint8  _active;
+	char _name[MAX_STR_LENGTH + 1];
+	uint16 _x;
+	uint8  _y;
+	uint16 _width;
+	uint8  _height;
+	uint16 _walkToX;
+	uint8  _walkToY;
+	uint8  _SP;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct Bitmap {
+	uint8  _frame;
+	uint8  _isVisible;
+	uint16 _x1;
+	uint8  _y1;
+	uint16 _x2;
+	uint8  _y2;
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+
+struct Scene {
+
+	Door *getDoor(uint8 objectId);
+	Object *getObject(uint8 objectId);
+	Static *getStatic(uint8 staticId);
+
+	uint8 _startup;
+	uint8 _unknown001;
+	uint8 _unknown002;
+	uint8 _unknown003;
+	uint8 _DL;
+
+	uint8 _noDoors;
+	Door _doors[5];
+
+	uint8 _noObjects;
+	Object _objects[9];
+
+	uint8 _noStatics;
+	Static _statics[15];
+
+	Bitmap _bitmaps[10];
+
+	uint16 _obstacleY1;
+	uint8 _palRotStart;
+	uint8 _palRotEnd;
+	uint8 _palRotPeriod;
+	uint8 _unknown38A[80];
+
+	bool loadFromStream(Common::ReadStream &stream);
+};
+
+struct GameData {
+public:
+	GameData();
+	Scene *getScene(uint8 sceneId);
+
+	bool loadFromStream(Common::ReadStream &stream);
+
+	uint8 _currentScene;
+	bool _partB;
+	Inventory _inventory;
+	Common::String _currentAPK;
+private:
+	Scene _scenes[45];
+
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 8ad1507..4137581 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -19,6 +19,7 @@ MODULE_OBJS := \
 	detection.o \
 	encryptedfile.o \
 	game.o \
+	gamedata.o \
 	inventory.o \
 	mutationofjb.o \
 	room.o \
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 17ff7b5..66de1f7 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -32,11 +32,8 @@
 #include "engines/util.h"
 
 #include "mutationofjb/mutationofjb.h"
-#include "mutationofjb/room.h"
 #include "mutationofjb/game.h"
-#include "mutationofjb/encryptedfile.h"
-#include "mutationofjb/util.h"
-#include "mutationofjb/script.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/debug.h"
 
 namespace MutationOfJB {
@@ -44,10 +41,7 @@ namespace MutationOfJB {
 MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 : Engine(syst),
  _console(nullptr),
- _room(nullptr),
- _screen(nullptr),
- _globalScript(nullptr),
- _localScript(nullptr) {
+ _screen(nullptr) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -55,21 +49,6 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 	debug("MutationOfJBEngine::~MutationOfJBEngine");
 }
 
-bool MutationOfJBEngine::loadGameData(bool partB) {
-	EncryptedFile file;
-	const char *fileName = !partB ? "startup.dat" : "startupb.dat";
-	file.open(fileName);
-	if (!file.isOpen()) {
-		reportFileMissingError(fileName);
-		return false;
-	}
-
-	_gameData->loadFromStream(file);
-
-	file.close();
-
-	return true;
-}
 
 void MutationOfJBEngine::setupCursor() {
 	const uint8 white[] = {0xFF, 0xFF, 0xFF};
@@ -82,27 +61,24 @@ void MutationOfJBEngine::setupCursor() {
 	CursorMan.showMouse(true);
 }
 
+Graphics::Screen *MutationOfJBEngine::getScreen() const {
+	return _screen;
+}
+
+Game &MutationOfJBEngine::getGame() {
+	return *_game;
+}
+
 Common::Error MutationOfJBEngine::run() {
 	debug("MutationOfJBEngine::run");
 
 	initGraphics(320, 200);
 
 	_console = new Console(this);
-	_screen = new Graphics::Screen;
-	setupCursor();
-
-	_gameData = new GameData;
-	_gameData->_currentScene = 13;
-	loadGameData(false);
+	_screen = new Graphics::Screen();
+	_game = new Game(this);
 
-	_room = new Room(_screen);
-	_room->load(_gameData->_currentScene, false);
-
-	EncryptedFile globalScriptFile;
-	globalScriptFile.open("global.atn");
-	_globalScript = new Script;
-	_globalScript->loadFromStream(globalScriptFile);
-	globalScriptFile.close();
+	setupCursor();
 
 	while(!shouldQuit()) {
 		Common::Event event;
@@ -118,13 +94,12 @@ Common::Error MutationOfJBEngine::run() {
 			}
 			case Common::EVENT_LBUTTONDOWN:
 			{
-				const Scene *const scene = _gameData->getScene(_gameData->_currentScene);
+				const Scene *const scene = _game->getGameData().getScene(_game->getGameData()._currentScene);
 				if (scene) {
 					for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
 						const Door &door = scene->_doors[i];
 						if ((event.mouse.x >= door._x) && (event.mouse.x < door._x + door._width) && (event.mouse.y >= door._y) && (event.mouse.y < door._y + door._height)) {
-							_gameData->_currentScene = door._destSceneId;
-							_room->load(_gameData->_currentScene, false);
+							_game->changeScene(door._destSceneId, false);
 						}
 					}
 				}
@@ -143,12 +118,4 @@ Common::Error MutationOfJBEngine::run() {
 	return Common::kNoError;
 }
 
-Script *MutationOfJBEngine::getGlobalScript() {
-	return _globalScript;
-}
-
-Script *MutationOfJBEngine::getLocalScript() {
-	return _localScript;
-}
-
 }
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index efd8898..893cca9 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -32,9 +32,7 @@ namespace Graphics {
 namespace MutationOfJB {
 
 class Console;
-class Room;
-struct GameData;
-class Script;
+class Game;
 
 class MutationOfJBEngine : public Engine {
 public:
@@ -42,19 +40,16 @@ public:
 	~MutationOfJBEngine();
 
 	virtual Common::Error run();
-	Script *getGlobalScript();
-	Script *getLocalScript();
+	Graphics::Screen *getScreen() const;
+	Game &getGame();
 
 private:
 	bool loadGameData(bool partB);
 	void setupCursor();
 
 	Console *_console;
-	Room *_room;
-	GameData *_gameData;
 	Graphics::Screen *_screen;
-	Script *_globalScript;
-	Script *_localScript;
+	Game *_game;
 };
 
 
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 7c5e569..66d137c 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -59,13 +59,12 @@ struct ActionInfo {
 typedef Common::Array<ActionInfo> ActionInfos;
 typedef Common::Array<GotoCommand *> GotoCommands;
 
-class ScriptParseContext
-{
+class ScriptParseContext {
 public:
 	ScriptParseContext(Common::SeekableReadStream &stream);
 	bool readLine(Common::String &line);
 	void addConditionalCommand(ConditionalCommand *command, char tag);
-	void addLookSection(const Common::String & item, bool walkTo);
+	void addLookSection(const Common::String &item, bool walkTo);
 
 	Common::SeekableReadStream &_stream;
 	Command *_currentCommand;


Commit: 7a081f0605f2282fdce907bedfc9cae55dc67ab7
    https://github.com/scummvm/scummvm/commit/7a081f0605f2282fdce907bedfc9cae55dc67ab7
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Load local (room) scripts.

Changed paths:
    engines/mutationofjb/commands/command.cpp
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/conditionalcommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/commands/gotocommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/util.cpp


diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
index 943986e..6c4dc47 100644
--- a/engines/mutationofjb/commands/command.cpp
+++ b/engines/mutationofjb/commands/command.cpp
@@ -26,6 +26,7 @@
 namespace MutationOfJB {
 
 void CommandParser::transition(ScriptParseContext &, Command *, Command *, CommandParser *) {}
+void CommandParser::finish(ScriptParseContext &parseCtx) {}
 CommandParser::~CommandParser() {}
 
 Command::~Command() {}
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index ccfdea2..c6fce1e 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -43,6 +43,9 @@ public:
 
 	/* Old command - created by this parser. */
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
+
+	/* Called after parsing. */
+	virtual void finish(ScriptParseContext &parseCtx);
 };
 
 class Command {
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index 1e0b755..aa42d1c 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -38,6 +38,10 @@ void ConditionalCommandParser::transition(ScriptParseContext &parseContext, Comm
 	condCommand->setTrueCommand(newCommand);
 }
 
+void ConditionalCommandParser::finish(ScriptParseContext &) {
+	_lastTag = 0;
+}
+
 
 ConditionalCommand::ConditionalCommand() :
 	_trueCommand(nullptr),
diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
index 4a4e7e1..d64efbf 100644
--- a/engines/mutationofjb/commands/conditionalcommand.h
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -30,7 +30,9 @@ namespace MutationOfJB {
 
 class ConditionalCommandParser : public CommandParser {
 public:
+	ConditionalCommandParser() : _lastTag(0) {}
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
+	virtual void finish(ScriptParseContext &parseCtx) override;
 protected:
 	char _lastTag;
 };
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 019c3b5..2d2ebe3 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -121,6 +121,16 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 	}
 }
 
+void EndBlockCommandParser::finish(ScriptParseContext &) {
+	_elseFound = false;
+	_ifTag = 0;
+
+	if (!_pendingActionInfos.empty()) {
+		debug("Problem: Pending action infos from end block parser is not empty!");
+	}
+	_pendingActionInfos.clear();
+}
+
 Command::ExecuteResult EndBlockCommand::execute(GameData &) {
 	return Finished;
 }
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 24c4e5c..3af86a3 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -35,9 +35,9 @@ class EndBlockCommandParser : public CommandParser {
 public:
 	EndBlockCommandParser() : _elseFound(false), _ifTag(0) {}
 
-	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
-	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
-
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
+	virtual void finish(ScriptParseContext &parseCtx) override;
 private:
 	bool _elseFound;
 	char _ifTag;
diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
index 40560f3..a1d1a9d 100644
--- a/engines/mutationofjb/commands/gotocommand.cpp
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -26,7 +26,7 @@
 #include "mutationofjb/script.h"
 
 /*
-	GOTO <label>
+	"GOTO " <label>
 
 	Jumps to a label.
 */
@@ -38,7 +38,7 @@ bool GotoCommandParser::parse(const Common::String &line, ScriptParseContext &pa
 		return false;
 	}
 
-	Common::String label = line.c_str() + 6;
+	Common::String label = line.c_str() + 5;
 	GotoCommand *gotoCmd = new GotoCommand();
 
 	if (parseCtx._labels.contains(label)) {
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 6fe6b95..cc7dab7 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -79,6 +79,28 @@ bool Game::loadGameData(bool partB) {
 void Game::changeScene(uint8 sceneId, bool partB) {
 	_gameData->_currentScene = sceneId;
 	_room->load(_gameData->_currentScene, partB);
+
+	if (_localScript) {
+		delete _localScript;
+		_localScript = nullptr;
+	}
+
+	EncryptedFile scriptFile;
+	Common::String fileName = Common::String::format("scrn%d%s.atn", sceneId, partB ? "b" : "");
+	scriptFile.open(fileName);
+	if (!scriptFile.isOpen()) {
+		reportFileMissingError(fileName.c_str());
+		return;
+	}
+
+	// TODO Actually parse this.
+	Common::String dummy;
+	dummy = scriptFile.readLine(); // Skip first line.
+	scriptFile.seek(126, SEEK_CUR); // Skip 126 bytes.
+
+	_localScript = new Script;
+	_localScript->loadFromStream(scriptFile);
+	scriptFile.close();
 }
 
 }
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 95c2bdc..3c34660 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -130,6 +130,10 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 		lastParser = currentParser;
 	}
 
+	for (CommandParser **parser = parsers; *parser; ++parser) {
+		(*parser)->finish(parseCtx);
+	}
+
 	for (ActionInfos::iterator it = parseCtx._actionInfos.begin(); it != parseCtx._actionInfos.end(); ++it) {
 		if (it->_action == ActionInfo::Look) {
 			_lookActionInfos.push_back(*it);
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 0878748..1a56a62 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -28,7 +28,7 @@
 namespace MutationOfJB {
 
 void reportFileMissingError(const char *fileName) {
-	Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file."), fileName);
+	Common::String errorMessage = Common::String::format(_("Unable to locate the '%s' engine data file"), fileName);
 	GUIErrorMessage(errorMessage);
 	warning("%s", errorMessage.c_str());
 }


Commit: 3928c52c0ee2a930431a807d0b4262440ab75725
    https://github.com/scummvm/scummvm/commit/3928c52c0ee2a930431a807d0b4262440ab75725
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for CAMEFROM command.

Changed paths:
  A engines/mutationofjb/commands/camefromcommand.cpp
  A engines/mutationofjb/commands/camefromcommand.h
    engines/mutationofjb/commands/command.cpp
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/conditionalcommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/commands/labelcommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/camefromcommand.cpp b/engines/mutationofjb/commands/camefromcommand.cpp
new file mode 100644
index 0000000..2c232c1
--- /dev/null
+++ b/engines/mutationofjb/commands/camefromcommand.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/camefromcommand.h"
+#include "mutationofjb/gamedata.h"
+#include "common/str.h"
+
+/*
+	"CAMEFROM" <sceneId>
+
+	This command tests whether last scene (the scene player came from) is sceneId.
+	If true, the execution continues after this command.
+	Otherwise the execution continues after first '#' found.
+*/
+
+namespace MutationOfJB {
+
+bool CameFromCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 10 || !line.hasPrefix("CAMEFROM")) {
+		return false;
+	}
+
+	const uint8 sceneId = atoi(line.c_str() + 9);
+	command = new CameFromCommand(sceneId);
+	return true;
+}
+
+Command::ExecuteResult CameFromCommand::execute(GameData &gameData) {
+	_cachedResult = (gameData._lastScene == _sceneId);
+
+	return Finished;
+}
+
+Common::String CameFromCommand::debugString() const {
+	return Common::String::format("CAMEFROM %d", _sceneId);
+}
+
+}
diff --git a/engines/mutationofjb/commands/camefromcommand.h b/engines/mutationofjb/commands/camefromcommand.h
new file mode 100644
index 0000000..c097ca1
--- /dev/null
+++ b/engines/mutationofjb/commands/camefromcommand.h
@@ -0,0 +1,49 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_CAMEFROMCOMMAND_H
+#define MUTATIONOFJB_CAMEFROMCOMMAND_H
+
+#include "mutationofjb/commands/conditionalcommand.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class CameFromCommandParser : public ConditionalCommandParser {
+public:
+	CameFromCommandParser() : ConditionalCommandParser(true) {}
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+};
+
+class CameFromCommand : public ConditionalCommand {
+public:
+	CameFromCommand(uint8 sceneId) : _sceneId(sceneId) {}
+	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual Common::String debugString() const;
+private:
+	uint8 _sceneId;
+};
+
+}
+
+#endif
+
diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
index 6c4dc47..d1e92f8 100644
--- a/engines/mutationofjb/commands/command.cpp
+++ b/engines/mutationofjb/commands/command.cpp
@@ -26,7 +26,7 @@
 namespace MutationOfJB {
 
 void CommandParser::transition(ScriptParseContext &, Command *, Command *, CommandParser *) {}
-void CommandParser::finish(ScriptParseContext &parseCtx) {}
+void CommandParser::finish(ScriptParseContext &) {}
 CommandParser::~CommandParser() {}
 
 Command::~Command() {}
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index aa42d1c..27b2714 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -34,7 +34,7 @@ void ConditionalCommandParser::transition(ScriptParseContext &parseContext, Comm
 	}
 
 	ConditionalCommand *const condCommand = static_cast<ConditionalCommand *>(oldCommand);
-	parseContext.addConditionalCommand(condCommand, _lastTag);
+	parseContext.addConditionalCommand(condCommand, _lastTag, _firstHash);
 	condCommand->setTrueCommand(newCommand);
 }
 
diff --git a/engines/mutationofjb/commands/conditionalcommand.h b/engines/mutationofjb/commands/conditionalcommand.h
index d64efbf..ea1a66a 100644
--- a/engines/mutationofjb/commands/conditionalcommand.h
+++ b/engines/mutationofjb/commands/conditionalcommand.h
@@ -30,11 +30,13 @@ namespace MutationOfJB {
 
 class ConditionalCommandParser : public CommandParser {
 public:
-	ConditionalCommandParser() : _lastTag(0) {}
+	ConditionalCommandParser(bool firstHash = false) : _lastTag(0), _firstHash(firstHash) {}
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
 	virtual void finish(ScriptParseContext &parseCtx) override;
 protected:
 	char _lastTag;
+private:
+	bool _firstHash;
 };
 
 class ConditionalCommand : public Command {
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 2d2ebe3..c8adaaa 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -87,18 +87,22 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 		}
 	}
 
+	if (firstChar == '#') {
+		_hashFound = true;
+	}
+
 	command = new EndBlockCommand();
 
 	return true;
 }
 
 void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *newCommandParser) {
-	if (_elseFound) {
+	if (_elseFound || _hashFound) {
 		if (newCommand) {
 			ScriptParseContext::ConditionalCommandInfos::iterator it = parseCtx._pendingCondCommands.begin();
 
 			while (it != parseCtx._pendingCondCommands.end()) {
-				if (it->_tag == _ifTag) {
+				if ((it->_firstHash && _hashFound) || (!it->_firstHash && it->_tag == _ifTag)) {
 					it->_command->setFalseCommand(newCommand);
 					it = parseCtx._pendingCondCommands.erase(it);
 				} else {
@@ -108,6 +112,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		}
 
 		_elseFound = false;
+		_hashFound = false;
 		_ifTag = 0;
 	}
 
@@ -123,6 +128,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 
 void EndBlockCommandParser::finish(ScriptParseContext &) {
 	_elseFound = false;
+	_hashFound = false;
 	_ifTag = 0;
 
 	if (!_pendingActionInfos.empty()) {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 3af86a3..140fb21 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -33,13 +33,14 @@ class ActionInfo;
 
 class EndBlockCommandParser : public CommandParser {
 public:
-	EndBlockCommandParser() : _elseFound(false), _ifTag(0) {}
+	EndBlockCommandParser() : _elseFound(false), _hashFound(false), _ifTag(0) {}
 
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
 	virtual void finish(ScriptParseContext &parseCtx) override;
 private:
 	bool _elseFound;
+	bool _hashFound;
 	char _ifTag;
 
 	Common::Array<uint> _pendingActionInfos;
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
index 15a10ca..87c78f9 100644
--- a/engines/mutationofjb/commands/labelcommand.cpp
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -41,8 +41,8 @@ bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &p
 	label.deleteLastChar();
 
 	LabelCommand *labelCmd = new LabelCommand(label);
-	if (!parseCtx._labels.contains(line)) {
-		parseCtx._labels[line] = labelCmd;
+	if (!parseCtx._labels.contains(label)) {
+		parseCtx._labels[label] = labelCmd;
 	} else {
 		warning("Label '%s' already exists", label.c_str());
 	}
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index cc7dab7..397b86c 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -77,6 +77,7 @@ bool Game::loadGameData(bool partB) {
 
 
 void Game::changeScene(uint8 sceneId, bool partB) {
+	_gameData->_lastScene = _gameData->_currentScene;
 	_gameData->_currentScene = sceneId;
 	_room->load(_gameData->_currentScene, partB);
 
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index cc85da3..a181510 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -171,6 +171,7 @@ Static *Scene::getStatic(uint8 staticId) {
 
 GameData::GameData()
 	: _currentScene(0),
+	_lastScene(0),
 	_partB(false) {}
 
 Scene *GameData::getScene(uint8 sceneId) {
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 23e238a..ca70cd7 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -152,6 +152,7 @@ public:
 	bool loadFromStream(Common::ReadStream &stream);
 
 	uint8 _currentScene;
+	uint8 _lastScene;
 	bool _partB;
 	Inventory _inventory;
 	Common::String _currentAPK;
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 4137581..a81e2a3 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
 	commands/additemcommand.o \
+	commands/camefromcommand.o \
 	commands/changecommand.o \
 	commands/command.o \
 	commands/conditionalcommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 3c34660..dfb6886 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -38,6 +38,7 @@
 #include "mutationofjb/commands/removeallitemscommand.h"
 #include "mutationofjb/commands/labelcommand.h"
 #include "mutationofjb/commands/gotocommand.h"
+#include "mutationofjb/commands/camefromcommand.h"
 
 namespace MutationOfJB {
 
@@ -46,6 +47,7 @@ static CommandParser **getParsers() {
 		new IfPiggyCommandParser,
 		new IfItemCommandParser,
 		new IfCommandParser,
+		new CameFromCommandParser,
 		new EndBlockCommandParser,
 		new ChangeDoorCommandParser,
 		new ChangeObjectCommandParser,
@@ -88,8 +90,8 @@ bool ScriptParseContext::readLine(Common::String &line) {
 	return false;
 }
 
-void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char tag) {
-	ConditionalCommandInfo cmi = {command, tag};
+void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char tag, bool firstHash) {
+	ConditionalCommandInfo cmi = {command, tag, firstHash};
 	_pendingCondCommands.push_back(cmi);
 }
 
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 66d137c..9589968 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -63,7 +63,7 @@ class ScriptParseContext {
 public:
 	ScriptParseContext(Common::SeekableReadStream &stream);
 	bool readLine(Common::String &line);
-	void addConditionalCommand(ConditionalCommand *command, char tag);
+	void addConditionalCommand(ConditionalCommand *command, char tag, bool firstHash);
 	void addLookSection(const Common::String &item, bool walkTo);
 
 	Common::SeekableReadStream &_stream;
@@ -73,6 +73,7 @@ public:
 	struct ConditionalCommandInfo {
 		ConditionalCommand *_command;
 		char _tag;
+		bool _firstHash;
 	};
 	typedef Common::Array<ConditionalCommandInfo> ConditionalCommandInfos;
 	ConditionalCommandInfos _pendingCondCommands;


Commit: 63c0dac9613caef3778a4cb9765bb8b628e5a1c2
    https://github.com/scummvm/scummvm/commit/63c0dac9613caef3778a4cb9765bb8b628e5a1c2
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for macro definitions.

Changed paths:
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index c6fce1e..1303242 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -24,7 +24,7 @@
 #define MUTATIONOFJB_COMMAND_H
 
 namespace Common {
-	class String;
+class String;
 }
 
 namespace MutationOfJB {
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index c8adaaa..4a6e608 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -26,6 +26,26 @@
 #include "common/str.h"
 #include "common/debug.h"
 
+/*
+	("#L " | "-L ") <object>
+	("#W " | "-W ") <object>
+	("#T " | "-T ") <object>
+	("#U " | "-U ") <object1> [<object2>]
+	("#ELSE" | "-ELSE") [<tag>]
+	"#MACRO " <name>
+
+	If a line starts with '#', '=', '-', it is treated as the end of a section.
+	However, at the same time it can also start a new section depending on what follows.
+
+	#L (look), #W (walk), #T (talk), #U (use) sections are executed
+	when the user starts corresponding action on the object or in case of "use" up to two objects.
+	The difference between '#' and '-' version is whether the player walks towards the object ('#') or not ('-').
+
+	#ELSE is used by conditional commands (see comments for IfCommand and others).
+
+	#MACRO starts a new macro. Global script can call macros from local script and vice versa.
+*/
+
 namespace MutationOfJB {
 
 bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
@@ -85,6 +105,8 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 		if (line.size() >= 6) {
 			_ifTag = line[5];
 		}
+	} else if (line.size() >= 8 && line.hasPrefix("#MACRO")) {
+		_foundMacro = line.c_str() + 7;
 	}
 
 	if (firstChar == '#') {
@@ -116,6 +138,13 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		_ifTag = 0;
 	}
 
+	if (!_foundMacro.empty() && newCommand) {
+		if (!parseCtx._macros.contains(_foundMacro)) {
+			parseCtx._macros[_foundMacro] = newCommand;
+		}
+		_foundMacro = "";
+	}
+
 	if (newCommandParser != this) {
 		if (!_pendingActionInfos.empty()) {
 			for (Common::Array<uint>::iterator it = _pendingActionInfos.begin(); it != _pendingActionInfos.end(); ++it) {
@@ -135,6 +164,7 @@ void EndBlockCommandParser::finish(ScriptParseContext &) {
 		debug("Problem: Pending action infos from end block parser is not empty!");
 	}
 	_pendingActionInfos.clear();
+	_foundMacro = "";
 }
 
 Command::ExecuteResult EndBlockCommand::execute(GameData &) {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 140fb21..1b22d75 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -44,6 +44,7 @@ private:
 	char _ifTag;
 
 	Common::Array<uint> _pendingActionInfos;
+	Common::String _foundMacro;
 };
 
 class EndBlockCommand : public Command {
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index e639324..99c4c7b 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -204,4 +204,55 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 	return true;
 }
 
+bool Console::cmd_listmacros(int argc, const char **argv) {
+	if (argc == 2) {
+		Script *script = nullptr;
+		if (strcmp(argv[1], "G") == 0) {
+			script = _vm->getGame().getGlobalScript();
+		} else if (strcmp(argv[1], "L") == 0) {
+			script = _vm->getGame().getLocalScript();
+		}
+		if (!script) {
+			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+		} else {
+			const Macros &macros = script->getMacros();
+			for (Macros::const_iterator it = macros.begin(); it != macros.end(); ++it) {
+				debugPrintf("%s\n", it->_key.c_str());
+			}
+		}
+	} else {
+		debugPrintf(_("listmacros <G|L>\n"));
+	}
+
+	return true;
+}
+
+bool Console::cmd_showmacro(int argc, const char **argv) {
+	if (argc == 3) {
+		Script *script = nullptr;
+		if (strcmp(argv[1], "G") == 0) {
+			script = _vm->getGame().getGlobalScript();
+		} else if (strcmp(argv[1], "L") == 0) {
+			script = _vm->getGame().getLocalScript();
+		}
+		if (!script) {
+			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+		} else {
+			const Macros &macros = script->getMacros();
+			Macros::const_iterator itMacro = macros.find(argv[2]);
+			if (itMacro != macros.end()) {
+				if (itMacro->_value) {
+					showCommands(itMacro->_value);
+				}
+			} else {
+				debugPrintf("Macro not found.\n");
+			}
+		}
+	} else {
+		debugPrintf(_("showmacro <G|L> <macroname>\n"));
+	}
+
+	return true;
+}
+
 }
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index ee187cb..1dcc0fb 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -37,6 +37,8 @@ public:
 private:
 	bool cmd_listsections(int argc, const char **argv);
 	bool cmd_showsection(int argc, const char **argv);
+	bool cmd_listmacros(int argc, const char **argv);
+	bool cmd_showmacro(int argc, const char **argv);
 
 	void showIndent(int indentLevel);
 	void showCommands(Command *command, int indentLevel = 0);
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index dfb6886..7a39127 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -151,8 +151,7 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 		}
 	}
 
-	Common::HashMap<Common::String, Command *> macros;
-	Common::HashMap<Common::String, Command *> labels;
+	_macros = parseCtx._macros;
 
 	return true;
 }
@@ -184,4 +183,8 @@ const ActionInfos &Script::getUseActionInfos() const {
 	return _useActionInfos;
 }
 
+const Macros &Script::getMacros() const {
+	return _macros;
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 9589968..64adda8 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -58,6 +58,7 @@ struct ActionInfo {
 
 typedef Common::Array<ActionInfo> ActionInfos;
 typedef Common::Array<GotoCommand *> GotoCommands;
+typedef Common::HashMap<Common::String, Command *> Macros;
 
 class ScriptParseContext {
 public:
@@ -85,6 +86,7 @@ public:
 	PendingGotoMap _pendingGotos;
 
 	ActionInfos _actionInfos;
+	Macros _macros;
 
 private:
 };
@@ -98,6 +100,7 @@ public:
 	const ActionInfos &getWalkActionInfos() const;
 	const ActionInfos &getTalkActionInfos() const;
 	const ActionInfos &getUseActionInfos() const;
+	const Macros &getMacros() const;
 
 private:
 	void destroy();
@@ -106,6 +109,7 @@ private:
 	ActionInfos _walkActionInfos;
 	ActionInfos _talkActionInfos;
 	ActionInfos _useActionInfos;
+	Macros _macros;
 };
 
 }


Commit: e1d173ed7541f9da79f60a65d974da3ebbb29e7a
    https://github.com/scummvm/scummvm/commit/e1d173ed7541f9da79f60a65d974da3ebbb29e7a
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add changescene debug command and fix macro debug commands.

Changed paths:
    engines/mutationofjb/commands/additemcommand.cpp
    engines/mutationofjb/commands/additemcommand.h
    engines/mutationofjb/commands/camefromcommand.cpp
    engines/mutationofjb/commands/camefromcommand.h
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/command.cpp
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/commands/gotocommand.cpp
    engines/mutationofjb/commands/gotocommand.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifcommand.h
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.h
    engines/mutationofjb/commands/ifpiggycommand.cpp
    engines/mutationofjb/commands/ifpiggycommand.h
    engines/mutationofjb/commands/labelcommand.cpp
    engines/mutationofjb/commands/labelcommand.h
    engines/mutationofjb/commands/removeallitemscommand.cpp
    engines/mutationofjb/commands/removeallitemscommand.h
    engines/mutationofjb/commands/removeitemcommand.cpp
    engines/mutationofjb/commands/removeitemcommand.h
    engines/mutationofjb/commands/saycommand.cpp
    engines/mutationofjb/commands/saycommand.h
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/additemcommand.cpp b/engines/mutationofjb/commands/additemcommand.cpp
index 58ec567..1a670f7 100644
--- a/engines/mutationofjb/commands/additemcommand.cpp
+++ b/engines/mutationofjb/commands/additemcommand.cpp
@@ -22,6 +22,7 @@
 
 #include "mutationofjb/commands/additemcommand.h"
 #include "mutationofjb/gamedata.h"
+#include "mutationofjb/script.h"
 
 /*
 	"ADDITEM" " " <item>
@@ -40,8 +41,8 @@ bool AddItemCommandParser::parse(const Common::String &line, ScriptParseContext
 	return true;
 }
 
-Command::ExecuteResult AddItemCommand::execute(GameData &gameData) {
-	gameData._inventory.addItem(_item);
+Command::ExecuteResult AddItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	scriptExecCtx.getGameData()._inventory.addItem(_item);
 	return Finished;
 }
 
diff --git a/engines/mutationofjb/commands/additemcommand.h b/engines/mutationofjb/commands/additemcommand.h
index cb4c131..4e2ea2b 100644
--- a/engines/mutationofjb/commands/additemcommand.h
+++ b/engines/mutationofjb/commands/additemcommand.h
@@ -39,7 +39,7 @@ class AddItemCommand : public SeqCommand {
 public:
 	AddItemCommand(const Common::String &item) : _item(item) {}
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const override;
 private:
 	Common::String _item;
diff --git a/engines/mutationofjb/commands/camefromcommand.cpp b/engines/mutationofjb/commands/camefromcommand.cpp
index 2c232c1..0583187 100644
--- a/engines/mutationofjb/commands/camefromcommand.cpp
+++ b/engines/mutationofjb/commands/camefromcommand.cpp
@@ -22,6 +22,7 @@
 
 #include "mutationofjb/commands/camefromcommand.h"
 #include "mutationofjb/gamedata.h"
+#include "mutationofjb/script.h"
 #include "common/str.h"
 
 /*
@@ -44,8 +45,8 @@ bool CameFromCommandParser::parse(const Common::String &line, ScriptParseContext
 	return true;
 }
 
-Command::ExecuteResult CameFromCommand::execute(GameData &gameData) {
-	_cachedResult = (gameData._lastScene == _sceneId);
+Command::ExecuteResult CameFromCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	_cachedResult = (scriptExecCtx.getGameData()._lastScene == _sceneId);
 
 	return Finished;
 }
diff --git a/engines/mutationofjb/commands/camefromcommand.h b/engines/mutationofjb/commands/camefromcommand.h
index c097ca1..c4048cb 100644
--- a/engines/mutationofjb/commands/camefromcommand.h
+++ b/engines/mutationofjb/commands/camefromcommand.h
@@ -37,7 +37,7 @@ public:
 class CameFromCommand : public ConditionalCommand {
 public:
 	CameFromCommand(uint8 sceneId) : _sceneId(sceneId) {}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 private:
 	uint8 _sceneId;
diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index e4699eb..a4316bd 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -21,6 +21,8 @@
  */
 
 #include "mutationofjb/commands/changecommand.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/gamedata.h"
 #include "common/translation.h"
 
 namespace MutationOfJB {
@@ -295,8 +297,8 @@ const char *ChangeCommand::getOperationAsString() const {
 	}
 }
 
-Command::ExecuteResult ChangeDoorCommand::execute(GameData &gameData) {
-	Scene *const scene = gameData.getScene(_sceneId);
+Command::ExecuteResult ChangeDoorCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
@@ -352,8 +354,8 @@ Common::String ChangeDoorCommand::debugString() const {
 	return Common::String::format("scene%d.door%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
-Command::ExecuteResult ChangeObjectCommand::execute(GameData &gameData) {
-	Scene *const scene = gameData.getScene(_sceneId);
+Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
@@ -415,8 +417,8 @@ Common::String ChangeObjectCommand::debugString() const {
 	return Common::String::format("scene%d.object%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
-Command::ExecuteResult ChangeStaticCommand::execute(GameData &gameData) {
-	Scene *const scene = gameData.getScene(_sceneId);
+Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
@@ -466,8 +468,8 @@ Common::String ChangeStaticCommand::debugString() const {
 	return Common::String::format("scene%d.static%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
-Command::ExecuteResult ChangeSceneCommand::execute(GameData &gameData) {
-	Scene *const scene = gameData.getScene(_sceneId);
+Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index 5ba386b..dde5cd1 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -113,7 +113,7 @@ public:
 	ChangeDoorCommand(uint8 sceneId, uint8 doorId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, doorId, reg, op, val)
 	{}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 };
 
@@ -122,7 +122,7 @@ public:
 	ChangeObjectCommand(uint8 sceneId, uint8 objectId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, objectId, reg, op, val)
 	{}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 };
 
@@ -131,7 +131,7 @@ public:
 	ChangeStaticCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, staticId, reg, op, val)
 	{}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 };
 
@@ -140,7 +140,7 @@ public:
 	ChangeSceneCommand(uint8 sceneId, uint8 staticId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val)
 		: ChangeCommand(sceneId, staticId, reg, op, val)
 	{}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 };
 
diff --git a/engines/mutationofjb/commands/command.cpp b/engines/mutationofjb/commands/command.cpp
index d1e92f8..1e546ff 100644
--- a/engines/mutationofjb/commands/command.cpp
+++ b/engines/mutationofjb/commands/command.cpp
@@ -31,8 +31,4 @@ CommandParser::~CommandParser() {}
 
 Command::~Command() {}
 
-SeqCommand *Command::asSeqCommand() {
-	return nullptr;
-}
-
 }
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index 1303242..0133d52 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -29,12 +29,9 @@ class String;
 
 namespace MutationOfJB {
 
-class GameData;
-class SeqCommand;
-class IfCommand;
-class CallMacroCommand;
-class ScriptParseContext;
 class Command;
+class ScriptExecutionContext;
+class ScriptParseContext;
 
 class CommandParser {
 public:
@@ -58,10 +55,9 @@ public:
 
 	virtual ~Command();
 
-	virtual ExecuteResult execute(GameData &gameData) = 0;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) = 0;
 	virtual Command *next() const = 0;
 
-	virtual SeqCommand *asSeqCommand();
 	virtual Common::String debugString() const = 0;
 };
 }
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 4a6e608..53ea74a 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -167,12 +167,13 @@ void EndBlockCommandParser::finish(ScriptParseContext &) {
 	_foundMacro = "";
 }
 
-Command::ExecuteResult EndBlockCommand::execute(GameData &) {
+Command::ExecuteResult EndBlockCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	_nextCmd = scriptExecCtx.popReturnCommand();
 	return Finished;
 }
 
 Command *EndBlockCommand::next() const {
-	return nullptr;
+	return _nextCmd;
 }
 
 Common::String EndBlockCommand::debugString() const {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 1b22d75..4c0d23b 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -49,11 +49,14 @@ private:
 
 class EndBlockCommand : public Command {
 public:
+	EndBlockCommand() : _nextCmd(nullptr) {}
 	static bool ParseFunc(const Common::String &line, ScriptParseContext &parseContext, Command *&command);
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Command *next() const override;
 	virtual Common::String debugString() const;
+private:
+	Command *_nextCmd;
 };
 
 }
diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
index a1d1a9d..bc24e2c 100644
--- a/engines/mutationofjb/commands/gotocommand.cpp
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -59,7 +59,7 @@ void GotoCommand::setLabelCommand(LabelCommand *labelCmd) {
 	_labelCommand = labelCmd;
 }
 
-Command::ExecuteResult GotoCommand::execute(GameData &) {
+Command::ExecuteResult GotoCommand::execute(ScriptExecutionContext &) {
 	// Intentionally empty.
 
 	return Finished;
diff --git a/engines/mutationofjb/commands/gotocommand.h b/engines/mutationofjb/commands/gotocommand.h
index 436dd44..09d426f 100644
--- a/engines/mutationofjb/commands/gotocommand.h
+++ b/engines/mutationofjb/commands/gotocommand.h
@@ -43,7 +43,7 @@ public:
 
 	void setLabelCommand(LabelCommand *labelCmd);
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Command *next() const override;
 	virtual Common::String debugString() const override;
 private:
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index f78335a..b5f03fc 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -47,7 +47,7 @@
 
 namespace MutationOfJB {
 
-bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &parseContext, Command *&command) {
+bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
 	// IFtss oo val!
 	// <t>   1B Tag.
 	// <ss>  2B Scene.
@@ -84,8 +84,8 @@ IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative)
 	_negative(negative)
 {}
 
-Command::ExecuteResult IfCommand::execute(GameData &gameData) {
-	Scene *const scene = gameData.getScene(_sceneId);
+Command::ExecuteResult IfCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
 	if (!scene) {
 		return Finished;
 	}
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
index 51350ce..b04d8a3 100644
--- a/engines/mutationofjb/commands/ifcommand.h
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -40,7 +40,7 @@ class IfCommand : public ConditionalCommand {
 public:
 	IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
 	
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
 private:
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index 7512ba5..7a58cee 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -73,8 +73,8 @@ IfItemCommand::IfItemCommand(const Common::String &item, bool negative) :
 	_negative(negative)
 {}
 
-Command::ExecuteResult IfItemCommand::execute(GameData &gameData) {
-	_cachedResult = gameData._inventory.hasItem(_item);
+Command::ExecuteResult IfItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	_cachedResult = scriptExecCtx.getGameData()._inventory.hasItem(_item);
 	if (_negative) {
 		_cachedResult = !_cachedResult;
 	}
diff --git a/engines/mutationofjb/commands/ifitemcommand.h b/engines/mutationofjb/commands/ifitemcommand.h
index 0451786..f11ba7c 100644
--- a/engines/mutationofjb/commands/ifitemcommand.h
+++ b/engines/mutationofjb/commands/ifitemcommand.h
@@ -40,7 +40,7 @@ class IfItemCommand : public ConditionalCommand {
 public:
 	IfItemCommand(const Common::String &item, bool negative);
 	
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
 private:
diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
index cad0a14..e30213a 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.cpp
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -57,8 +57,8 @@ bool IfPiggyCommandParser::parse(const Common::String &line, ScriptParseContext
 }
 
 
-Command::ExecuteResult IfPiggyCommand::execute(GameData &gameData) {
-	_cachedResult = gameData._currentAPK == "piggy.apk";
+Command::ExecuteResult IfPiggyCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	_cachedResult = scriptExecCtx.getGameData()._currentAPK == "piggy.apk";
 
 	return Finished;
 }
diff --git a/engines/mutationofjb/commands/ifpiggycommand.h b/engines/mutationofjb/commands/ifpiggycommand.h
index 3fb6826..d10788c 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.h
+++ b/engines/mutationofjb/commands/ifpiggycommand.h
@@ -38,7 +38,7 @@ public:
 
 class IfPiggyCommand : public ConditionalCommand {
 public:
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
 private:
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
index 87c78f9..de6de02 100644
--- a/engines/mutationofjb/commands/labelcommand.cpp
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -64,7 +64,7 @@ const Common::String &LabelCommand::getName() const
 	return _name;
 }
 
-Command::ExecuteResult LabelCommand::execute(GameData &) {
+Command::ExecuteResult LabelCommand::execute(ScriptExecutionContext &) {
 	// Intentionally empty.
 
 	return Finished;
diff --git a/engines/mutationofjb/commands/labelcommand.h b/engines/mutationofjb/commands/labelcommand.h
index 389c759..f4acad6 100644
--- a/engines/mutationofjb/commands/labelcommand.h
+++ b/engines/mutationofjb/commands/labelcommand.h
@@ -40,7 +40,7 @@ public:
 	LabelCommand(const Common::String &name) : _name(name) {}
 	const Common::String &getName() const;
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const override;
 private:
 	Common::String _name;
diff --git a/engines/mutationofjb/commands/removeallitemscommand.cpp b/engines/mutationofjb/commands/removeallitemscommand.cpp
index 8043864..d9ebe45 100644
--- a/engines/mutationofjb/commands/removeallitemscommand.cpp
+++ b/engines/mutationofjb/commands/removeallitemscommand.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "mutationofjb/commands/removeallitemscommand.h"
+#include "mutationofjb/script.h"
 #include "mutationofjb/gamedata.h"
 
 /*
@@ -40,8 +41,8 @@ bool RemoveAllItemsCommandParser::parse(const Common::String &line, ScriptParseC
 	return true;
 }
 
-Command::ExecuteResult RemoveAllItemsCommand::execute(GameData &gameData) {
-	gameData._inventory.removeAllItems();
+Command::ExecuteResult RemoveAllItemsCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	scriptExecCtx.getGameData()._inventory.removeAllItems();
 	return Finished;
 }
 
diff --git a/engines/mutationofjb/commands/removeallitemscommand.h b/engines/mutationofjb/commands/removeallitemscommand.h
index 166aed8..92fe93f 100644
--- a/engines/mutationofjb/commands/removeallitemscommand.h
+++ b/engines/mutationofjb/commands/removeallitemscommand.h
@@ -38,7 +38,7 @@ class RemoveAllItemsCommand : public SeqCommand {
 public:
 	RemoveAllItemsCommand() {}
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const override;
 private:
 };
diff --git a/engines/mutationofjb/commands/removeitemcommand.cpp b/engines/mutationofjb/commands/removeitemcommand.cpp
index e4d9601..48dbda9 100644
--- a/engines/mutationofjb/commands/removeitemcommand.cpp
+++ b/engines/mutationofjb/commands/removeitemcommand.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "mutationofjb/commands/removeitemcommand.h"
+#include "mutationofjb/script.h"
 #include "mutationofjb/gamedata.h"
 
 /*
@@ -40,8 +41,8 @@ bool RemoveItemCommandParser::parse(const Common::String &line, ScriptParseConte
 	return true;
 }
 
-Command::ExecuteResult RemoveItemCommand::execute(GameData &gameData) {
-	gameData._inventory.removeItem(_item);
+Command::ExecuteResult RemoveItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	scriptExecCtx.getGameData()._inventory.removeItem(_item);
 	return Finished;
 }
 
diff --git a/engines/mutationofjb/commands/removeitemcommand.h b/engines/mutationofjb/commands/removeitemcommand.h
index 452a3b2..0aa13f5 100644
--- a/engines/mutationofjb/commands/removeitemcommand.h
+++ b/engines/mutationofjb/commands/removeitemcommand.h
@@ -39,7 +39,7 @@ class RemoveItemCommand : public SeqCommand {
 public:
 	RemoveItemCommand(const Common::String &item) : _item(item) {}
 
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const override;
 private:
 	Common::String _item;
diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
index a0c9c79..854c957 100644
--- a/engines/mutationofjb/commands/saycommand.cpp
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -140,7 +140,7 @@ bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &par
 }
 
 
-Command::ExecuteResult SayCommand::execute(GameData &) {
+Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &) {
 	// TODO: Actual implementation.
 	debug("%s [%s]", _lineToSay.c_str(), _voiceFile.c_str());
 	return Finished;
diff --git a/engines/mutationofjb/commands/saycommand.h b/engines/mutationofjb/commands/saycommand.h
index e2a1207..e41d10f 100644
--- a/engines/mutationofjb/commands/saycommand.h
+++ b/engines/mutationofjb/commands/saycommand.h
@@ -42,7 +42,7 @@ public:
 		_voiceFile(voiceFile),
 		_waitForPrevious(waitForPrevious),
 		_talkingAnimation(talkingAnimation) {}
-	virtual ExecuteResult execute(GameData &gameData) override;
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const override;
 private:
 	Common::String _lineToSay;
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index 03d9538..02164dc 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -43,8 +43,4 @@ Command *SeqCommand::next() const {
 	return _nextCommand;
 }
 
-SeqCommand *SeqCommand::asSeqCommand() {
-	return this;
-}
-
 }
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index c3455ce..241932a 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -37,7 +37,6 @@ class SeqCommand : public Command {
 public:
 	void setNextCommand(Command *nextCommand);
 	virtual Command *next() const override;
-	virtual SeqCommand *asSeqCommand();
 
 private:
 	Command *_nextCommand;
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 99c4c7b..867a021 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -56,6 +56,9 @@ static Common::String convertToASCII(const Common::String &str) {
 Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
 	registerCmd("listsections", WRAP_METHOD(Console, cmd_listsections));
 	registerCmd("showsection", WRAP_METHOD(Console, cmd_showsection));
+	registerCmd("listmacros", WRAP_METHOD(Console, cmd_listmacros));
+	registerCmd("showmacro", WRAP_METHOD(Console, cmd_showmacro));
+	registerCmd("changescene", WRAP_METHOD(Console, cmd_changescene));
 }
 
 bool Console::cmd_listsections(int argc, const char **argv) {
@@ -255,4 +258,17 @@ bool Console::cmd_showmacro(int argc, const char **argv) {
 	return true;
 }
 
+bool Console::cmd_changescene(int argc, const char **argv) {
+	if (argc == 2) {
+		const uint8 sceneId = atoi(argv[1]);
+		const bool partB = argv[1][strlen(argv[1]) - 1] == 'B';
+
+		_vm->getGame().changeScene(sceneId, partB);
+	} else {
+		debugPrintf(_("changescene <scenename>\n"));
+	}
+
+	return true;
+}
+
 }
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 1dcc0fb..2f35a9c 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -39,6 +39,7 @@ private:
 	bool cmd_showsection(int argc, const char **argv);
 	bool cmd_listmacros(int argc, const char **argv);
 	bool cmd_showmacro(int argc, const char **argv);
+	bool cmd_changescene(int argc, const char **argv);
 
 	void showIndent(int indentLevel);
 	void showCommands(Command *command, int indentLevel = 0);
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 7a39127..699daeb 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "script.h"
+#include "mutationofjb/script.h"
 
 #include "common/hashmap.h"
 #include "common/hash-str.h"
@@ -39,6 +39,7 @@
 #include "mutationofjb/commands/labelcommand.h"
 #include "mutationofjb/commands/gotocommand.h"
 #include "mutationofjb/commands/camefromcommand.h"
+#include "mutationofjb/game.h"
 
 namespace MutationOfJB {
 
@@ -95,6 +96,24 @@ void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char
 	_pendingCondCommands.push_back(cmi);
 }
 
+
+void ScriptExecutionContext::pushReturnCommand(Command *cmd) {
+	_stack.push(cmd);
+}
+
+Command *ScriptExecutionContext::popReturnCommand() {
+	if (_stack.empty()) {
+		return nullptr;
+	}
+
+	return _stack.pop();
+}
+
+GameData &ScriptExecutionContext::getGameData() {
+	return _game.getGameData();
+}
+
+
 bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 	destroy();
 
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 64adda8..477181b 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -26,6 +26,7 @@
 #include "common/array.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
+#include "common/stack.h"
 
 namespace Common {
 class SeekableReadStream;
@@ -36,6 +37,8 @@ namespace MutationOfJB {
 
 class Command;
 class LabelCommand;
+class Game;
+class GameData;
 class GotoCommand;
 class ConditionalCommand;
 typedef Common::Array<Command *> Commands;
@@ -91,6 +94,18 @@ public:
 private:
 };
 
+class ScriptExecutionContext {
+public:
+	ScriptExecutionContext(Game &game) : _game(game) {}
+	void pushReturnCommand(Command *);
+	Command *popReturnCommand();
+	GameData &getGameData();
+
+private:
+	Game &_game;
+	Common::Stack<Command *> _stack;
+};
+
 class Script {
 public:
 	bool loadFromStream(Common::SeekableReadStream &stream);


Commit: 938f222d4857b45e0f1f7e4726040ab0bf1d9b67
    https://github.com/scummvm/scummvm/commit/938f222d4857b45e0f1f7e4726040ab0bf1d9b67
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for calling macros.

Changed paths:
  A engines/mutationofjb/commands/callmacrocommand.cpp
  A engines/mutationofjb/commands/callmacrocommand.h
    engines/mutationofjb/commands/additemcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/additemcommand.h b/engines/mutationofjb/commands/additemcommand.h
index 4e2ea2b..d740d35 100644
--- a/engines/mutationofjb/commands/additemcommand.h
+++ b/engines/mutationofjb/commands/additemcommand.h
@@ -32,7 +32,7 @@ class AddItemCommandParser : public SeqCommandParser {
 public:
 	AddItemCommandParser() {}
 
-	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 };
 
 class AddItemCommand : public SeqCommand {
diff --git a/engines/mutationofjb/commands/callmacrocommand.cpp b/engines/mutationofjb/commands/callmacrocommand.cpp
new file mode 100644
index 0000000..b1c59eb
--- /dev/null
+++ b/engines/mutationofjb/commands/callmacrocommand.cpp
@@ -0,0 +1,85 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/callmacrocommand.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/game.h"
+#include "common/translation.h"
+
+/*
+	"_" <name>
+
+	Calls macro with the specified name.
+*/
+
+namespace MutationOfJB {
+
+bool CallMacroCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 2 || line.firstChar() != '_') {
+		return false;
+	}
+
+	const Common::String macroName = line.c_str() + 1;
+	command = new CallMacroCommand(macroName);
+	return true;
+}
+
+void CallMacroCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
+	if (!oldCommand || !newCommand) {
+		warning(_("Unexpected empty command in transition"));
+		return;
+	}
+
+	static_cast<CallMacroCommand *>(oldCommand)->setReturnCommand(newCommand);
+}
+
+
+void CallMacroCommand::setReturnCommand(Command *cmd) {
+	_returnCommand = cmd;
+}
+
+Command *CallMacroCommand::getReturnCommand() const {
+	return _returnCommand;
+}
+
+Command::ExecuteResult CallMacroCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Game &game = scriptExecCtx.getGame();
+	_callCommand = game.getMacro(_macroName);
+	if (_callCommand) {
+		scriptExecCtx.pushReturnCommand(_returnCommand);
+	} else {
+		warning("Macro '%s' not found.", _macroName.c_str());
+	}
+
+	return Finished;
+}
+
+Command *CallMacroCommand::next() const {
+	return _callCommand;
+}
+
+Common::String CallMacroCommand::debugString() const {
+	return Common::String::format("CALL '%s'", _macroName.c_str());
+}
+
+}
+
diff --git a/engines/mutationofjb/commands/callmacrocommand.h b/engines/mutationofjb/commands/callmacrocommand.h
new file mode 100644
index 0000000..0486bcd
--- /dev/null
+++ b/engines/mutationofjb/commands/callmacrocommand.h
@@ -0,0 +1,58 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_CALLMACROCOMMAND_H
+#define MUTATIONOFJB_CALLMACROCOMMAND_H
+
+#include "mutationofjb/commands/command.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class CallMacroCommandParser : public CommandParser {
+public:
+	CallMacroCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
+};
+
+class CallMacroCommand : public Command {
+public:
+	CallMacroCommand(const Common::String &macroName) : _macroName(macroName), _returnCommand(nullptr), _callCommand(nullptr) {}
+	void setReturnCommand(Command *);
+
+	Command *getReturnCommand() const;
+
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	Command *next() const override;
+	virtual Common::String debugString() const override;
+private:
+	Common::String _macroName;
+	Command *_returnCommand;
+	Command *_callCommand;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 867a021..b4c2e77 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -27,6 +27,7 @@
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/seqcommand.h"
 #include "mutationofjb/commands/conditionalcommand.h"
+#include "mutationofjb/commands/callmacrocommand.h"
 #include "common/debug-channels.h"
 #include "common/translation.h"
 #include "common/scummsys.h"
@@ -129,6 +130,8 @@ void Console::showCommands(Command *command, int indentLevel) {
 			debugPrintf("ELSE\n");
 			showCommands(condCmd->getFalseCommand(), indentLevel + 1);
 			command = nullptr;
+		} else if (CallMacroCommand* const callMacroCmd = dynamic_cast<CallMacroCommand *>(command)) {
+			command = callMacroCmd->getReturnCommand();
 		} else {
 			command = nullptr;
 		}
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 397b86c..047971b 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -28,10 +28,11 @@
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
 #include "common/util.h"
+#include "common/str.h"
 
 namespace MutationOfJB {
 
-Game::Game(MutationOfJBEngine* vm) : _vm(vm) {
+Game::Game(MutationOfJBEngine *vm) : _vm(vm) {
 	_gameData = new GameData;
 	loadGameData(false);
 
@@ -104,4 +105,18 @@ void Game::changeScene(uint8 sceneId, bool partB) {
 	scriptFile.close();
 }
 
+Command *Game::getMacro(const Common::String &name) const {
+	Command *cmd = nullptr;
+
+	if (_localScript) {
+		cmd = _localScript->getMacro(name);
+	}
+
+	if (!cmd && _globalScript) {
+		cmd = _globalScript->getMacro(name);
+	}
+
+	return cmd;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 4b4ab43..5ed65f1 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -25,8 +25,13 @@
 
 #include "common/scummsys.h"
 
+namespace Common {
+class String;
+}
+
 namespace MutationOfJB {
 
+class Command;
 class MutationOfJBEngine;
 class GameData;
 class Script;
@@ -34,7 +39,7 @@ class Room;
 
 class Game {
 public:
-	Game(MutationOfJBEngine* vm);
+	Game(MutationOfJBEngine *vm);
 	GameData &getGameData();
 
 	Script *getGlobalScript() const;
@@ -42,6 +47,8 @@ public:
 
 	void changeScene(uint8 sceneId, bool partB);
 
+	Command *getMacro(const Common::String &name) const;
+
 private:
 	bool loadGameData(bool partB);
 
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index a81e2a3..447f9a9 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/mutationofjb
 
 MODULE_OBJS := \
 	commands/additemcommand.o \
+	commands/callmacrocommand.o \
 	commands/camefromcommand.o \
 	commands/changecommand.o \
 	commands/command.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 699daeb..1291bd8 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -39,6 +39,7 @@
 #include "mutationofjb/commands/labelcommand.h"
 #include "mutationofjb/commands/gotocommand.h"
 #include "mutationofjb/commands/camefromcommand.h"
+#include "mutationofjb/commands/callmacrocommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -49,6 +50,7 @@ static CommandParser **getParsers() {
 		new IfItemCommandParser,
 		new IfCommandParser,
 		new CameFromCommandParser,
+		new CallMacroCommandParser,
 		new EndBlockCommandParser,
 		new ChangeDoorCommandParser,
 		new ChangeObjectCommandParser,
@@ -109,6 +111,10 @@ Command *ScriptExecutionContext::popReturnCommand() {
 	return _stack.pop();
 }
 
+Game &ScriptExecutionContext::getGame() {
+	return _game;
+}
+
 GameData &ScriptExecutionContext::getGameData() {
 	return _game.getGameData();
 }
@@ -206,4 +212,13 @@ const Macros &Script::getMacros() const {
 	return _macros;
 }
 
+Command *Script::getMacro(const Common::String &name) const {
+	Macros::const_iterator it = _macros.find(name);
+	if (it == _macros.end()) {
+		return nullptr;
+	}
+
+	return it->_value;
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 477181b..940a274 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -99,6 +99,7 @@ public:
 	ScriptExecutionContext(Game &game) : _game(game) {}
 	void pushReturnCommand(Command *);
 	Command *popReturnCommand();
+	Game &getGame();
 	GameData &getGameData();
 
 private:
@@ -116,6 +117,7 @@ public:
 	const ActionInfos &getTalkActionInfos() const;
 	const ActionInfos &getUseActionInfos() const;
 	const Macros &getMacros() const;
+	Command *getMacro(const Common::String &name) const;
 
 private:
 	void destroy();


Commit: 7a1898730155dce824451d98bbe65b430832d575
    https://github.com/scummvm/scummvm/commit/7a1898730155dce824451d98bbe65b430832d575
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Support for running commands.

Changed paths:
    engines/mutationofjb/commands/callmacrocommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/callmacrocommand.cpp b/engines/mutationofjb/commands/callmacrocommand.cpp
index b1c59eb..49b948c 100644
--- a/engines/mutationofjb/commands/callmacrocommand.cpp
+++ b/engines/mutationofjb/commands/callmacrocommand.cpp
@@ -62,8 +62,7 @@ Command *CallMacroCommand::getReturnCommand() const {
 }
 
 Command::ExecuteResult CallMacroCommand::execute(ScriptExecutionContext &scriptExecCtx) {
-	Game &game = scriptExecCtx.getGame();
-	_callCommand = game.getMacro(_macroName);
+	_callCommand = scriptExecCtx.getMacro(_macroName);
 	if (_callCommand) {
 		scriptExecCtx.pushReturnCommand(_returnCommand);
 	} else {
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 047971b..6436c7a 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -27,12 +27,15 @@
 #include "mutationofjb/room.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
+#include "mutationofjb/commands/command.h"
 #include "common/util.h"
 #include "common/str.h"
+#include "common/translation.h"
 
 namespace MutationOfJB {
 
-Game::Game(MutationOfJBEngine *vm) : _vm(vm) {
+Game::Game(MutationOfJBEngine *vm)
+: _vm(vm), _scriptExecCtx(*this) {
 	_gameData = new GameData;
 	loadGameData(false);
 
@@ -105,18 +108,9 @@ void Game::changeScene(uint8 sceneId, bool partB) {
 	scriptFile.close();
 }
 
-Command *Game::getMacro(const Common::String &name) const {
-	Command *cmd = nullptr;
 
-	if (_localScript) {
-		cmd = _localScript->getMacro(name);
-	}
-
-	if (!cmd && _globalScript) {
-		cmd = _globalScript->getMacro(name);
-	}
-
-	return cmd;
+void Game::update() {
+	_scriptExecCtx.runActiveCommand();
 }
 
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 5ed65f1..b44929d 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -24,6 +24,7 @@
 #define MUTATIONOFJB_GAME_H
 
 #include "common/scummsys.h"
+#include "mutationofjb/script.h"
 
 namespace Common {
 class String;
@@ -47,10 +48,12 @@ public:
 
 	void changeScene(uint8 sceneId, bool partB);
 
-	Command *getMacro(const Common::String &name) const;
+	void update();
 
 private:
 	bool loadGameData(bool partB);
+	void runActiveCommand();
+	void startCommand(Command *cmd);
 
 	MutationOfJBEngine *_vm;
 
@@ -58,6 +61,8 @@ private:
 	Script *_globalScript;
 	Script *_localScript;
 	Room *_room;
+
+	ScriptExecutionContext _scriptExecCtx;
 };
 
 }
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 66de1f7..1bd08dd 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -111,6 +111,7 @@ Common::Error MutationOfJBEngine::run() {
 		}
 
 		_console->onFrame();
+		_game->update();
 		_system->delayMillis(40);
 		_screen->update();
 	}
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 1291bd8..e3aa3e6 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -26,6 +26,7 @@
 #include "common/hash-str.h"
 #include "common/stream.h"
 #include "common/debug.h"
+#include "common/translation.h"
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/ifcommand.h"
 #include "mutationofjb/commands/ifitemcommand.h"
@@ -100,15 +101,15 @@ void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char
 
 
 void ScriptExecutionContext::pushReturnCommand(Command *cmd) {
-	_stack.push(cmd);
+	_callStack.push(cmd);
 }
 
 Command *ScriptExecutionContext::popReturnCommand() {
-	if (_stack.empty()) {
+	if (_callStack.empty()) {
 		return nullptr;
 	}
 
-	return _stack.pop();
+	return _callStack.pop();
 }
 
 Game &ScriptExecutionContext::getGame() {
@@ -119,6 +120,49 @@ GameData &ScriptExecutionContext::getGameData() {
 	return _game.getGameData();
 }
 
+void ScriptExecutionContext::clear() {
+	_callStack.clear();
+}
+
+Command::ExecuteResult ScriptExecutionContext::runActiveCommand() {
+	while (_activeCommand) {
+		const Command::ExecuteResult result = _activeCommand->execute(*this);
+		if (result == Command::Finished) {
+			_activeCommand = _activeCommand->next();
+		} else {
+			return result;
+		}
+	}
+
+	return Command::Finished;
+}
+
+Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
+	if (_activeCommand) {
+		warning(_("Trying to start command while another one is running."));
+		return Command::Finished;
+	}
+	clear();
+	_activeCommand = cmd;
+	return runActiveCommand();
+}
+
+Command *ScriptExecutionContext::getMacro(const Common::String &name) const {
+	Command *cmd = nullptr;
+
+	Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
+	Script *const globalScript = _game.getGlobalScript();
+
+	if (localScript) {
+		cmd = localScript->getMacro(name);
+	}
+
+	if (!cmd && globalScript) {
+		cmd = globalScript->getMacro(name);
+	}
+
+	return cmd;
+}
 
 bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 	destroy();
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 940a274..8232106 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -23,6 +23,7 @@
 #ifndef MUTATIONOFJB_SCRIPT_H
 #define MUTATIONOFJB_SCRIPT_H 
 
+#include "mutationofjb/commands/command.h"
 #include "common/array.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
@@ -41,6 +42,7 @@ class Game;
 class GameData;
 class GotoCommand;
 class ConditionalCommand;
+class Script;
 typedef Common::Array<Command *> Commands;
 
 
@@ -96,15 +98,23 @@ private:
 
 class ScriptExecutionContext {
 public:
-	ScriptExecutionContext(Game &game) : _game(game) {}
+	ScriptExecutionContext(Game &game, Script *localScriptOverride = nullptr) : _game(game), _activeCommand(nullptr), _localScriptOverride(localScriptOverride) {}
+	void clear();
+
+	Command::ExecuteResult runActiveCommand();
+	Command::ExecuteResult startCommand(Command *cmd);
+
 	void pushReturnCommand(Command *);
 	Command *popReturnCommand();
 	Game &getGame();
 	GameData &getGameData();
+	Command *getMacro(const Common::String &name) const;
 
 private:
 	Game &_game;
-	Common::Stack<Command *> _stack;
+	Command *_activeCommand;
+	Common::Stack<Command *> _callStack;
+	Script *_localScriptOverride;
 };
 
 class Script {


Commit: e93e20dbe869f2d3906ec1d0a151a21de29e9714
    https://github.com/scummvm/scummvm/commit/e93e20dbe869f2d3906ec1d0a151a21de29e9714
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Parse startup sections in scripts and fix change scene command.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index a4316bd..f2639b6 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -37,16 +37,30 @@ namespace MutationOfJB {
 // <ii>  2B  Entity ID.
 // <val> VL  Value.
 
-bool ChangeCommandParser::parseValueString(const Common::String &valueString, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
-	if (valueString.size() < 8) {
-		return false;
+bool ChangeCommandParser::parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
+	if (changeEntity) {
+		if (valueString.size() < 8) {
+			return false;
+		}
+	} else {
+		if (valueString.size() < 7) {
+			return false;
+		}
 	}
 
 	sceneId = atoi(valueString.c_str() + 3);
-	entityId = atoi(valueString.c_str() + 6);
+	if (changeEntity) {
+		entityId = atoi(valueString.c_str() + 6);
+	}
 	const char *val = nullptr;
-	if (valueString.size() >= 9) {
-		val = valueString.c_str() + 9;
+	if (changeEntity) {
+		if (valueString.size() >= 9) {
+			val = valueString.c_str() + 9;
+		}
+	} else {
+		if (valueString.size() >= 6) {
+			val = valueString.c_str() + 6;
+		}
 	}
 
 	if (valueString.hasPrefix("NM")) {
@@ -137,7 +151,7 @@ bool ChangeDoorCommandParser::parse(const Common::String &line, ScriptParseConte
 	ChangeCommand::ChangeRegister reg;
 	ChangeCommand::ChangeOperation op;
 	ChangeCommandValue val;
-	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
 		return false;
 	}
 
@@ -154,7 +168,7 @@ bool ChangeObjectCommandParser::parse(const Common::String &line, ScriptParseCon
 	ChangeCommand::ChangeRegister reg;
 	ChangeCommand::ChangeOperation op;
 	ChangeCommandValue val;
-	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
 		return false;
 	}
 
@@ -171,7 +185,7 @@ bool ChangeStaticCommandParser::parse(const Common::String &line, ScriptParseCon
 	ChangeCommand::ChangeRegister reg;
 	ChangeCommand::ChangeOperation op;
 	ChangeCommandValue val;
-	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
 		return false;
 	}
 
@@ -188,7 +202,7 @@ bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseCont
 	ChangeCommand::ChangeRegister reg;
 	ChangeCommand::ChangeOperation op;
 	ChangeCommandValue val;
-	if (!parseValueString(line.c_str() + 8, sceneId, objectId, reg, op, val)) {
+	if (!parseValueString(line.c_str() + 7, false, sceneId, objectId, reg, op, val)) {
 		return false;
 	}
 
@@ -351,7 +365,7 @@ Command::ExecuteResult ChangeDoorCommand::execute(ScriptExecutionContext &script
 }
 
 Common::String ChangeDoorCommand::debugString() const {
-	return Common::String::format("scene%d.door%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+	return Common::String::format("SCENE%d.DOOR%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
 Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scriptExecCtx) {
@@ -414,7 +428,7 @@ Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scri
 }
 
 Common::String ChangeObjectCommand::debugString() const {
-	return Common::String::format("scene%d.object%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+	return Common::String::format("SCENE%d.OBJECT%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
 Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scriptExecCtx) {
@@ -465,7 +479,7 @@ Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scri
 }
 
 Common::String ChangeStaticCommand::debugString() const {
-	return Common::String::format("scene%d.static%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+	return Common::String::format("SCENE%d.STATIC%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 
 Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scriptExecCtx) {
@@ -508,6 +522,6 @@ Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scrip
 }
 
 Common::String ChangeSceneCommand::debugString() const {
-	return Common::String::format("scene%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
+	return Common::String::format("SCENE%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
 }
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index dde5cd1..f5d7cf5 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -84,7 +84,7 @@ protected:
 
 class ChangeCommandParser : public SeqCommandParser {
 protected:
-	bool parseValueString(const Common::String &valueString, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv);
+	bool parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv);
 	int parseInteger(const char *val, ChangeCommand::ChangeOperation &op);
 };
 
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 53ea74a..492a424 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -25,6 +25,7 @@
 #include "mutationofjb/commands/conditionalcommand.h"
 #include "common/str.h"
 #include "common/debug.h"
+#include "common/translation.h"
 
 /*
 	("#L " | "-L ") <object>
@@ -107,6 +108,8 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 		}
 	} else if (line.size() >= 8 && line.hasPrefix("#MACRO")) {
 		_foundMacro = line.c_str() + 7;
+	} else if (line.size() >= 10 && line.hasPrefix("#STARTUP")) {
+		_foundStartup = line.c_str() + 9;
 	}
 
 	if (firstChar == '#') {
@@ -138,11 +141,26 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		_ifTag = 0;
 	}
 
-	if (!_foundMacro.empty() && newCommand) {
-		if (!parseCtx._macros.contains(_foundMacro)) {
-			parseCtx._macros[_foundMacro] = newCommand;
+	if (!_foundMacro.empty()) {
+		if (newCommand) {
+			if (!parseCtx._macros.contains(_foundMacro)) {
+				parseCtx._macros[_foundMacro] = newCommand;
+			} else {
+				warning(_("Macro '%s' already exists."), _foundMacro.c_str());
+			}
+		}
+		_foundMacro.clear();
+	}
+	if (!_foundStartup.empty()) {
+		if (newCommand) {
+			const uint8 startupId = atoi(_foundStartup.c_str());
+			if (!parseCtx._startups.contains(startupId)) {
+				parseCtx._startups[startupId] = newCommand;
+			} else {
+				warning(_("Startup %u already exists."), (unsigned int) startupId);
+			}
 		}
-		_foundMacro = "";
+		_foundStartup.clear();
 	}
 
 	if (newCommandParser != this) {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 4c0d23b..eb77f43 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -45,6 +45,7 @@ private:
 
 	Common::Array<uint> _pendingActionInfos;
 	Common::String _foundMacro;
+	Common::String _foundStartup;
 };
 
 class EndBlockCommand : public Command {
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index b4c2e77..e533339 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -55,24 +55,37 @@ static Common::String convertToASCII(const Common::String &str) {
 }
 
 Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
+	registerCmd("showallcommands", WRAP_METHOD(Console, cmd_showallcommands));
 	registerCmd("listsections", WRAP_METHOD(Console, cmd_listsections));
 	registerCmd("showsection", WRAP_METHOD(Console, cmd_showsection));
 	registerCmd("listmacros", WRAP_METHOD(Console, cmd_listmacros));
 	registerCmd("showmacro", WRAP_METHOD(Console, cmd_showmacro));
+	registerCmd("liststartups", WRAP_METHOD(Console, cmd_liststartups));
+	registerCmd("showstartup", WRAP_METHOD(Console, cmd_showstartup));
 	registerCmd("changescene", WRAP_METHOD(Console, cmd_changescene));
 }
 
+bool Console::cmd_showallcommands(int argc, const char **argv) {
+	if (argc == 2) {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
+			const Commands &commands = script->getAllCommands();
+
+			for (Commands::const_iterator it = commands.begin(); it != commands.end(); ++it) {
+				debugPrintf("%s\n", convertToASCII((*it)->debugString()).c_str());
+			}
+		}
+	} else {
+		debugPrintf(_("showallcommands <G|L>\n"));
+	}
+
+	return true;
+}
+
 bool Console::cmd_listsections(int argc, const char **argv) {
 	if (argc == 3) {
-		Script *script = nullptr;
-		if (strcmp(argv[1], "G") == 0) {
-			script = _vm->getGame().getGlobalScript();
-		} else if (strcmp(argv[1], "L") == 0) {
-			script = _vm->getGame().getLocalScript();
-		}
-		if (!script) {
-			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
-		} else {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
 			if (strcmp(argv[2], "L") == 0) {
 				const ActionInfos &actionInfos = script->getLookActionInfos();
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
@@ -140,15 +153,8 @@ void Console::showCommands(Command *command, int indentLevel) {
 
 bool Console::cmd_showsection(int argc, const char **argv) {
 	if (argc >= 4) {
-		Script *script = nullptr;
-		if (strcmp(argv[1], "G") == 0) {
-			script = _vm->getGame().getGlobalScript();
-		} else if (strcmp(argv[1], "L") == 0) {
-			script = _vm->getGame().getLocalScript();
-		}
-		if (!script) {
-			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
-		} else {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
 			Command *command = nullptr;
 			bool found = false;
 			if (strcmp(argv[2], "L") == 0) {
@@ -212,15 +218,8 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 
 bool Console::cmd_listmacros(int argc, const char **argv) {
 	if (argc == 2) {
-		Script *script = nullptr;
-		if (strcmp(argv[1], "G") == 0) {
-			script = _vm->getGame().getGlobalScript();
-		} else if (strcmp(argv[1], "L") == 0) {
-			script = _vm->getGame().getLocalScript();
-		}
-		if (!script) {
-			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
-		} else {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
 			const Macros &macros = script->getMacros();
 			for (Macros::const_iterator it = macros.begin(); it != macros.end(); ++it) {
 				debugPrintf("%s\n", it->_key.c_str());
@@ -261,6 +260,43 @@ bool Console::cmd_showmacro(int argc, const char **argv) {
 	return true;
 }
 
+bool Console::cmd_liststartups(int argc, const char **argv) {
+	if (argc == 2) {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
+			const Startups &startups = script->getStartups();
+			for (Startups::const_iterator it = startups.begin(); it != startups.end(); ++it) {
+				debugPrintf("%u\n", (unsigned int) it->_key);
+			}
+		}
+	} else {
+		debugPrintf(_("liststartups <G|L>\n"));
+	}
+
+	return true;
+}
+
+bool Console::cmd_showstartup(int argc, const char **argv) {
+	if (argc == 3) {
+		Script *const script = getScriptFromArg(argv[1]);
+		if (script) {
+			const Startups &startups = script->getStartups();
+			Startups::const_iterator itMacro = startups.find((uint8) atoi(argv[2]));
+			if (itMacro != startups.end()) {
+				if (itMacro->_value) {
+					showCommands(itMacro->_value);
+				}
+			} else {
+				debugPrintf("Startup not found.\n");
+			}
+		}
+	} else {
+		debugPrintf(_("showstartup <G|L> <startupid>\n"));
+	}
+
+	return true;
+}
+
 bool Console::cmd_changescene(int argc, const char **argv) {
 	if (argc == 2) {
 		const uint8 sceneId = atoi(argv[1]);
@@ -274,4 +310,18 @@ bool Console::cmd_changescene(int argc, const char **argv) {
 	return true;
 }
 
+Script *Console::getScriptFromArg(const char *arg) {
+	Script *script = nullptr;
+	if (strcmp(arg, "G") == 0) {
+		script = _vm->getGame().getGlobalScript();
+	} else if (strcmp(arg, "L") == 0) {
+		script = _vm->getGame().getLocalScript();
+	}
+	if (!script) {
+		debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+	}
+
+	return script;
+}
+
 }
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 2f35a9c..8df5167 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -29,20 +29,25 @@ namespace MutationOfJB {
 
 class MutationOfJBEngine;
 class Command;
+class Script;
 
 class Console : public GUI::Debugger {
 public:
 	Console(MutationOfJBEngine *vm);
 	virtual ~Console(void) {}
 private:
+	bool cmd_showallcommands(int argc, const char **argv);
 	bool cmd_listsections(int argc, const char **argv);
 	bool cmd_showsection(int argc, const char **argv);
 	bool cmd_listmacros(int argc, const char **argv);
 	bool cmd_showmacro(int argc, const char **argv);
+	bool cmd_liststartups(int argc, const char **argv);
+	bool cmd_showstartup(int argc, const char **argv);
 	bool cmd_changescene(int argc, const char **argv);
 
 	void showIndent(int indentLevel);
 	void showCommands(Command *command, int indentLevel = 0);
+	Script *getScriptFromArg(const char *arg);
 
 	MutationOfJBEngine *_vm;
 };
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index e3aa3e6..8dc5e40 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -221,6 +221,7 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 	}
 
 	_macros = parseCtx._macros;
+	_startups = parseCtx._startups;
 
 	return true;
 }
@@ -252,6 +253,10 @@ const ActionInfos &Script::getUseActionInfos() const {
 	return _useActionInfos;
 }
 
+const Commands &Script::getAllCommands() const {
+	return _allCommands;
+}
+
 const Macros &Script::getMacros() const {
 	return _macros;
 }
@@ -265,4 +270,17 @@ Command *Script::getMacro(const Common::String &name) const {
 	return it->_value;
 }
 
+const Startups &Script::getStartups() const {
+	return _startups;
+}
+
+Command *Script::getStartup(uint8 startupId) const {
+	Startups::const_iterator it = _startups.find(startupId);
+	if (it == _startups.end()) {
+		return nullptr;
+	}
+
+	return it->_value;
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 8232106..316aab5 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -64,6 +64,7 @@ struct ActionInfo {
 typedef Common::Array<ActionInfo> ActionInfos;
 typedef Common::Array<GotoCommand *> GotoCommands;
 typedef Common::HashMap<Common::String, Command *> Macros;
+typedef Common::HashMap<uint8, Command *> Startups;
 
 class ScriptParseContext {
 public:
@@ -92,6 +93,7 @@ public:
 
 	ActionInfos _actionInfos;
 	Macros _macros;
+	Startups _startups;
 
 private:
 };
@@ -126,8 +128,11 @@ public:
 	const ActionInfos &getWalkActionInfos() const;
 	const ActionInfos &getTalkActionInfos() const;
 	const ActionInfos &getUseActionInfos() const;
+	const Commands &getAllCommands() const;
 	const Macros &getMacros() const;
+	const Startups &getStartups() const;
 	Command *getMacro(const Common::String &name) const;
+	Command *getStartup(uint8 startupId) const;
 
 private:
 	void destroy();
@@ -137,6 +142,7 @@ private:
 	ActionInfos _talkActionInfos;
 	ActionInfos _useActionInfos;
 	Macros _macros;
+	Startups _startups;
 };
 
 }


Commit: 574bb83b9760ff7a92da2b43146d245c0331d8ad
    https://github.com/scummvm/scummvm/commit/574bb83b9760ff7a92da2b43146d245c0331d8ad
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for NEWROOM command.

Changed paths:
  A engines/mutationofjb/commands/newroomcommand.cpp
  A engines/mutationofjb/commands/newroomcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/newroomcommand.cpp b/engines/mutationofjb/commands/newroomcommand.cpp
new file mode 100644
index 0000000..818ef7d
--- /dev/null
+++ b/engines/mutationofjb/commands/newroomcommand.cpp
@@ -0,0 +1,83 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/newroomcommand.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "common/str.h"
+
+/*
+	"NEWROOM " <sceneId> " " <x> " " <y> " " <frame>
+
+	NEWROOM changes the current scene. While doing that, it also executes STARTUP section for the new room.
+	However, after that, the execution goes back to the old script to finish commands after NEWROOM.
+
+	All parameters are supposed to be 3 characters long.
+	SceneId is the scene to load, x and y are the player's new position and frame is the player's new frame (orientation).
+*/
+
+namespace MutationOfJB {
+
+bool NewRoomCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 23 || !line.hasPrefix("NEWROOM")) {
+		return false;
+	}
+
+	const uint8 sceneId = atoi(line.c_str() + 8);
+	const uint16 x = atoi(line.c_str() + 12);
+	const uint16 y = atoi(line.c_str() + 16);
+	const uint8 frame = atoi(line.c_str() + 20);
+	command = new NewRoomCommand(sceneId, x, y, frame);
+	return true;
+}
+
+
+NewRoomCommand::NewRoomCommand(uint8 sceneId, uint16 x, uint16 y, uint8 frame) : _sceneId(sceneId), _x(x), _y(y), _frame(frame), _innerExecCtx(nullptr) {}
+
+Command::ExecuteResult NewRoomCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Game &game = scriptExecCtx.getGame();
+
+	// Execute new startup section.
+	ExecuteResult res;
+	if (!_innerExecCtx) {
+		Script *newScript = game.changeSceneDelayScript(_sceneId, game.getGameData()._partB);
+		_innerExecCtx = new ScriptExecutionContext(scriptExecCtx.getGame(), newScript);
+		res =_innerExecCtx->startStartupSection();
+	} else {
+		res = _innerExecCtx->runActiveCommand();
+	}
+
+	if (res == Finished) {
+		delete _innerExecCtx;
+		_innerExecCtx = nullptr;
+	}
+
+	return res;
+}
+
+Common::String NewRoomCommand::debugString() const {
+	return Common::String::format("NEWROOM %u %u %u %u", (unsigned int) _sceneId, (unsigned int) _x, (unsigned int) _y, (unsigned int) _frame);
+}
+
+}
+
diff --git a/engines/mutationofjb/commands/newroomcommand.h b/engines/mutationofjb/commands/newroomcommand.h
new file mode 100644
index 0000000..f224e44
--- /dev/null
+++ b/engines/mutationofjb/commands/newroomcommand.h
@@ -0,0 +1,56 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_NEWROOMCOMMAND_H
+#define MUTATIONOFJB_NEWROOMCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+
+namespace MutationOfJB {
+
+class ScriptExecutionContext;
+
+class NewRoomCommandParser : public SeqCommandParser {
+public:
+	NewRoomCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class NewRoomCommand : public SeqCommand {
+public:
+	NewRoomCommand(uint8 sceneId, uint16 x, uint16 y, uint8 frame);
+
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Common::String debugString() const override;
+private:
+	uint8 _sceneId;
+	uint16 _x;
+	uint16 _y;
+	uint8 _frame;
+
+	ScriptExecutionContext *_innerExecCtx;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 6436c7a..c5fa7b3 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -35,7 +35,7 @@
 namespace MutationOfJB {
 
 Game::Game(MutationOfJBEngine *vm)
-: _vm(vm), _scriptExecCtx(*this) {
+: _vm(vm), _delayedLocalScript(nullptr), _scriptExecCtx(*this) {
 	_gameData = new GameData;
 	loadGameData(false);
 
@@ -79,23 +79,18 @@ bool Game::loadGameData(bool partB) {
 	return true;
 }
 
-
-void Game::changeScene(uint8 sceneId, bool partB) {
+Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
 	_gameData->_lastScene = _gameData->_currentScene;
 	_gameData->_currentScene = sceneId;
+	_gameData->_partB = partB;
 	_room->load(_gameData->_currentScene, partB);
 
-	if (_localScript) {
-		delete _localScript;
-		_localScript = nullptr;
-	}
-
 	EncryptedFile scriptFile;
 	Common::String fileName = Common::String::format("scrn%d%s.atn", sceneId, partB ? "b" : "");
 	scriptFile.open(fileName);
 	if (!scriptFile.isOpen()) {
 		reportFileMissingError(fileName.c_str());
-		return;
+		return nullptr;
 	}
 
 	// TODO Actually parse this.
@@ -103,14 +98,37 @@ void Game::changeScene(uint8 sceneId, bool partB) {
 	dummy = scriptFile.readLine(); // Skip first line.
 	scriptFile.seek(126, SEEK_CUR); // Skip 126 bytes.
 
-	_localScript = new Script;
-	_localScript->loadFromStream(scriptFile);
+	Script *localScript = new Script;
+	localScript->loadFromStream(scriptFile);
 	scriptFile.close();
+
+	return localScript;
 }
 
+void Game::changeScene(uint8 sceneId, bool partB) {
+	if (_localScript) {
+		delete _localScript;
+		_localScript = nullptr;
+	}
+
+	_localScript = changeSceneLoadScript(sceneId, partB);
+	if (_localScript) {
+		_scriptExecCtx.startStartupSection();
+	}
+}
+
+Script *Game::changeSceneDelayScript(uint8 sceneId, bool partB) {
+	_delayedLocalScript = changeSceneLoadScript(sceneId, partB);
+	return _delayedLocalScript;
+}
 
 void Game::update() {
-	_scriptExecCtx.runActiveCommand();
+	Command::ExecuteResult res = _scriptExecCtx.runActiveCommand();
+	if (res == Command::Finished && _delayedLocalScript) {
+		delete _localScript;
+		_localScript = _delayedLocalScript;
+		_delayedLocalScript = nullptr;
+	}
 }
 
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index b44929d..2855704 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -47,6 +47,7 @@ public:
 	Script *getLocalScript() const;
 
 	void changeScene(uint8 sceneId, bool partB);
+	Script *changeSceneDelayScript(uint8 sceneId, bool partB);
 
 	void update();
 
@@ -54,12 +55,14 @@ private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
 	void startCommand(Command *cmd);
+	Script *changeSceneLoadScript(uint8 sceneId, bool partB);
 
 	MutationOfJBEngine *_vm;
 
 	GameData *_gameData;
 	Script *_globalScript;
 	Script *_localScript;
+	Script *_delayedLocalScript;
 	Room *_room;
 
 	ScriptExecutionContext _scriptExecCtx;
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index a181510..314eb56 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -183,6 +183,10 @@ Scene *GameData::getScene(uint8 sceneId) {
 	return &_scenes[sceneId - 1];
 }
 
+Scene *GameData::getCurrentScene() {
+	return getScene(_currentScene);
+}
+
 bool GameData::loadFromStream(Common::ReadStream &stream) {
 	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
 		_scenes[i].loadFromStream(stream);
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index ca70cd7..eb474f8 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -148,6 +148,7 @@ struct GameData {
 public:
 	GameData();
 	Scene *getScene(uint8 sceneId);
+	Scene *getCurrentScene();
 
 	bool loadFromStream(Common::ReadStream &stream);
 
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 447f9a9..917ab3c 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	commands/ifitemcommand.o \
 	commands/ifpiggycommand.o \
 	commands/labelcommand.o \
+	commands/newroomcommand.o \
 	commands/removeallitemscommand.o \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 8dc5e40..b9b1515 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -41,6 +41,7 @@
 #include "mutationofjb/commands/gotocommand.h"
 #include "mutationofjb/commands/camefromcommand.h"
 #include "mutationofjb/commands/callmacrocommand.h"
+#include "mutationofjb/commands/newroomcommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -61,6 +62,7 @@ static CommandParser **getParsers() {
 		new AddItemCommandParser,
 		new RemoveItemCommandParser,
 		new RemoveAllItemsCommandParser,
+		new NewRoomCommandParser,
 		new GotoCommandParser,
 		new LabelCommandParser,
 		nullptr
@@ -147,6 +149,19 @@ Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
 	return runActiveCommand();
 }
 
+Command::ExecuteResult ScriptExecutionContext::startStartupSection() {
+	Script *localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
+
+	if (localScript) {
+		Command *const startupCmd = localScript->getStartup(_game.getGameData().getCurrentScene()->_startup);
+		if (startupCmd) {
+			return startCommand(startupCmd);
+		}
+	}
+
+	return Command::Finished;
+}
+
 Command *ScriptExecutionContext::getMacro(const Common::String &name) const {
 	Command *cmd = nullptr;
 
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 316aab5..09aa942 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -105,6 +105,7 @@ public:
 
 	Command::ExecuteResult runActiveCommand();
 	Command::ExecuteResult startCommand(Command *cmd);
+	Command::ExecuteResult startStartupSection();
 
 	void pushReturnCommand(Command *);
 	Command *popReturnCommand();


Commit: 128d30c91c114d771e7777963716d3625be8cfeb
    https://github.com/scummvm/scummvm/commit/128d30c91c114d771e7777963716d3625be8cfeb
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Run action when clicking on static or door.

Changed paths:
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index e533339..f533a94 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -22,6 +22,7 @@
 
 #include "mutationofjb/debug.h"
 #include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/commands/command.h"
@@ -63,6 +64,9 @@ Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
 	registerCmd("liststartups", WRAP_METHOD(Console, cmd_liststartups));
 	registerCmd("showstartup", WRAP_METHOD(Console, cmd_showstartup));
 	registerCmd("changescene", WRAP_METHOD(Console, cmd_changescene));
+	registerCmd("dumpsceneinfo", WRAP_METHOD(Console, cmd_dumpsceneinfo));
+	registerCmd("dumpobjectinfo", WRAP_METHOD(Console, cmd_dumpobjectinfo));
+	registerCmd("dumpstaticinfo", WRAP_METHOD(Console, cmd_dumpstaticinfo));
 }
 
 bool Console::cmd_showallcommands(int argc, const char **argv) {
@@ -86,40 +90,40 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 	if (argc == 3) {
 		Script *const script = getScriptFromArg(argv[1]);
 		if (script) {
+			ActionInfo::Action action;
+			const char *word = nullptr;
 			if (strcmp(argv[2], "L") == 0) {
-				const ActionInfos &actionInfos = script->getLookActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Look %s\n"), convertToASCII(actionInfo._object1Name).c_str());
-				}
+				action = ActionInfo::Look;
+				word = _("Look");
 			} else if (strcmp(argv[2], "W") == 0) {
-				const ActionInfos &actionInfos = script->getWalkActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Walk %s\n"), convertToASCII(actionInfo._object1Name).c_str());
-				}
+				action = ActionInfo::Walk;
+				word = _("Walk");
 			} else if (strcmp(argv[2], "T") == 0) {
-				const ActionInfos &actionInfos = script->getTalkActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					debugPrintf(_("Talk %s\n"), convertToASCII(actionInfo._object1Name).c_str());
-				}
+				action = ActionInfo::Talk;
+				word = _("Talk");
 			} else if (strcmp(argv[2], "U") == 0) {
-				const ActionInfos &actionInfos = script->getUseActionInfos();
+				action = ActionInfo::Use;
+				word = _("Use");
+			} else if (strcmp(argv[2], "P") == 0) {
+				action = ActionInfo::PickUp;
+				word = _("Pick up");
+			} else {
+				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n"));
+			}
+			if (word) {
+				const ActionInfos &actionInfos = script->getActionInfos(action);
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (actionInfo._object2Name.empty()) {
-						debugPrintf(_("Use %s\n"), convertToASCII(actionInfo._object1Name).c_str());
+					if (action != ActionInfo::Use || actionInfo._entity2Name.empty()) {
+						debugPrintf("%s %s\n", word, convertToASCII(actionInfo._entity1Name).c_str());
 					} else {
-						debugPrintf(_("Use %s %s\n"), convertToASCII(actionInfo._object1Name).c_str(), convertToASCII(actionInfo._object2Name).c_str());
+						debugPrintf("%s %s %s\n", word, convertToASCII(actionInfo._entity1Name).c_str(), convertToASCII(actionInfo._entity2Name).c_str());
 					}
 				}
-			} else {
-				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk) or 'U' (use).\n"));
 			}
 		}
 	} else {
-		debugPrintf(_("listsections <G|L> <L|W|T|U>\n"));
+		debugPrintf(_("listsections <G|L> <L|W|T|U|P>\n"));
 	}
 	return true;
 }
@@ -156,61 +160,47 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 		Script *const script = getScriptFromArg(argv[1]);
 		if (script) {
 			Command *command = nullptr;
+			ActionInfo::Action action;
+			bool correctAction = true;
 			bool found = false;
+
 			if (strcmp(argv[2], "L") == 0) {
-				const ActionInfos &actionInfos = script->getLookActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
-						found = true;
-						command = actionInfo._command;
-						break;
-					}
-				}
+				action = ActionInfo::Look;
 			} else if (strcmp(argv[2], "W") == 0) {
-				const ActionInfos &actionInfos = script->getWalkActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
-						found = true;
-						command = actionInfo._command;
-						break;
-					}
-				}
+				action = ActionInfo::Walk;
 			} else if (strcmp(argv[2], "T") == 0) {
-				const ActionInfos &actionInfos = script->getTalkActionInfos();
-				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
-					const ActionInfo &actionInfo = *it;
-					if (convertToASCII(actionInfo._object1Name) == argv[3]) {
-						found = true;
-						command = actionInfo._command;
-						break;
-					}
-				}
+				action = ActionInfo::Talk;
 			} else if (strcmp(argv[2], "U") == 0) {
-				const ActionInfos &actionInfos = script->getUseActionInfos();
+				action = ActionInfo::Use;
+			} else if (strcmp(argv[2], "P") == 0) {
+				action = ActionInfo::PickUp;
+			} else {
+				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n"));
+				correctAction = false;
+			}
+
+			if (correctAction) {
+				const ActionInfos &actionInfos = script->getActionInfos(action);
 				for (ActionInfos::const_iterator it = actionInfos.begin(); it != actionInfos.end(); ++it) {
 					const ActionInfo &actionInfo = *it;
-					if (convertToASCII(actionInfo._object1Name) == argv[3] && ((argc == 4 && actionInfo._object2Name.empty()) || (argc > 4 && convertToASCII(actionInfo._object2Name) == argv[4]))) {
+					if (convertToASCII(actionInfo._entity1Name) == argv[3] && (action != ActionInfo::Use || ((argc == 4 && actionInfo._entity2Name.empty()) || (argc > 4 && convertToASCII(actionInfo._entity2Name) == argv[4])))) {
 						found = true;
 						command = actionInfo._command;
 						break;
 					}
 				}
-			} else {
-				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk) or 'U' (use).\n"));
-			}
 
-			if (found) {
-				if (command) {
-					showCommands(command);
+				if (found) {
+					if (command) {
+						showCommands(command);
+					}
+				} else {
+					debugPrintf("Section not found.\n");
 				}
-			} else {
-				debugPrintf("Section not found.\n");
 			}
 		}
 	} else {
-		debugPrintf(_("showsection <G|L> <L|W|T|U> <sectionname>\n"));
+		debugPrintf(_("showsection <G|L> <L|W|T|U|P> <sectionname>\n"));
 	}
 
 	return true;
@@ -310,6 +300,97 @@ bool Console::cmd_changescene(int argc, const char **argv) {
 	return true;
 }
 
+bool Console::cmd_dumpsceneinfo(int argc, const char **argv) {
+	if (argc == 2) {
+		const uint8 sceneId = atoi(argv[1]);
+		Scene *scene = _vm->getGame().getGameData().getScene(sceneId);
+		if (scene) {
+			debugPrintf("Startup: %u\n", (unsigned int) scene->_startup);
+			debugPrintf("Delay: %u\n", (unsigned int) scene->_DL);
+			debugPrintf("Doors: %u\n", (unsigned int) scene->_noDoors);
+			debugPrintf("Objects: %u\n", (unsigned int) scene->_noObjects);
+			debugPrintf("Statics: %u\n", (unsigned int) scene->_noStatics);
+			debugPrintf("ObstacleY1: %u\n", (unsigned int) scene->_obstacleY1);
+			debugPrintf("PalRotStart: %u\n", (unsigned int) scene->_palRotStart);
+			debugPrintf("PalRotEnd: %u\n", (unsigned int) scene->_palRotEnd);
+			debugPrintf("PalRotPeriod: %u\n", (unsigned int) scene->_palRotPeriod);
+		} else {
+			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+		}
+	} else {
+		debugPrintf(_("dumpsceneinfo <sceneid>\n"));
+	}
+
+	return true;
+}
+
+bool Console::cmd_dumpobjectinfo(int argc, const char **argv) {
+	if (argc == 3) {
+		const uint8 sceneId = atoi(argv[1]);
+		const uint8 objectId = atoi(argv[2]);
+
+		Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
+		if (scene) {
+			Object *const object = scene->getObject(objectId);
+			if (object) {
+				debugPrintf("AC: %u\n", (unsigned int) object->_AC);
+				debugPrintf("FA: %u\n", (unsigned int) object->_FA);
+				debugPrintf("FR: %u\n", (unsigned int) object->_FR);
+				debugPrintf("NA: %u\n", (unsigned int) object->_NA);
+				debugPrintf("FS: %u\n", (unsigned int) object->_FS);
+				debugPrintf("Unknown: %u\n", (unsigned int) object->_unknown);
+				debugPrintf("CA: %u\n", (unsigned int) object->_CA);
+				debugPrintf("X: %u\n", (unsigned int) object->_x);
+				debugPrintf("Y: %u\n", (unsigned int) object->_y);
+				debugPrintf("XL: %u\n", (unsigned int) object->_XL);
+				debugPrintf("YL: %u\n", (unsigned int) object->_YL);
+				debugPrintf("WX: %u\n", (unsigned int) object->_WX);
+				debugPrintf("WY: %u\n", (unsigned int) object->_WY);
+				debugPrintf("SP: %u\n", (unsigned int) object->_SP);
+			} else {
+				debugPrintf(_("Object %u not found.\n"), (unsigned int) objectId);
+			}
+		} else {
+			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+		}
+	} else {
+		debugPrintf(_("dumpobjectinfo <sceneid> <objectid>\n"));
+	}
+
+	return true;
+}
+
+bool Console::cmd_dumpstaticinfo(int argc, const char **argv) {
+	if (argc == 3) {
+		const uint8 sceneId = atoi(argv[1]);
+		const uint8 staticId = atoi(argv[2]);
+
+		Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
+		if (scene) {
+			Static *const stat = scene->getStatic(staticId, true);
+			if (stat) {
+				debugPrintf("Active: %u\n", (unsigned int) stat->_active);
+				debugPrintf("Name: '%s'\n", convertToASCII(stat->_name).c_str());
+				debugPrintf("X: %u\n", (unsigned int) stat->_x);
+				debugPrintf("Y: %u\n", (unsigned int) stat->_y);
+				debugPrintf("Width: %u\n", (unsigned int) stat->_width);
+				debugPrintf("Height: %u\n", (unsigned int) stat->_height);
+				debugPrintf("WalkToX: %u\n", (unsigned int) stat->_walkToY);
+				debugPrintf("WalkToY: %u\n", (unsigned int) stat->_walkToX);
+				debugPrintf("WalkToFrame: %u\n", (unsigned int) stat->_SP);
+			} else {
+				debugPrintf(_("Static %u not found.\n"), (unsigned int) staticId);
+			}
+		} else {
+			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+		}
+	} else {
+		debugPrintf(_("dumpstaticinfo <sceneid> <staticid>\n"));
+	}
+
+	return true;
+}
+
 Script *Console::getScriptFromArg(const char *arg) {
 	Script *script = nullptr;
 	if (strcmp(arg, "G") == 0) {
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 8df5167..e57b787 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -44,6 +44,9 @@ private:
 	bool cmd_liststartups(int argc, const char **argv);
 	bool cmd_showstartup(int argc, const char **argv);
 	bool cmd_changescene(int argc, const char **argv);
+	bool cmd_dumpsceneinfo(int argc, const char **argv);
+	bool cmd_dumpobjectinfo(int argc, const char **argv);
+	bool cmd_dumpstaticinfo(int argc, const char **argv);
 
 	void showIndent(int indentLevel);
 	void showCommands(Command *command, int indentLevel = 0);
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index c5fa7b3..156c020 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -122,6 +122,64 @@ Script *Game::changeSceneDelayScript(uint8 sceneId, bool partB) {
 	return _delayedLocalScript;
 }
 
+Door *Game::findDoor(int16 x, int16 y) {
+	Scene *scene = _gameData->getCurrentScene();
+	if (!scene)
+		return nullptr;
+
+	for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
+		Door &door = scene->_doors[i];
+		if ((x >= door._x) && (x < door._x + door._width) && (y >= door._y) && (y < door._y + door._height)) {
+			return &door;
+		}
+	}
+
+	return nullptr;
+}
+
+Static *Game::findStatic(int16 x, int16 y) {
+	Scene *scene = _gameData->getCurrentScene();
+	if (!scene)
+		return nullptr;
+
+	for (int i = 0; i < MIN(ARRAYSIZE(scene->_statics), (int) scene->_noStatics); ++i) {
+		Static &stat = scene->_statics[i];
+		if ((x >= stat._x) && (x < stat._x + stat._width) && (y >= stat._y) && (y < stat._y + stat._height)) {
+			return &stat;
+		}
+	}
+
+	return nullptr;
+}
+
+static Command *findActionInfoCommand(const ActionInfos &infos, const Common::String &entity1Name, const Common::String &entity2Name = Common::String()) {
+	for (ActionInfos::const_iterator it = infos.begin(); it != infos.end(); ++it) {
+		if (it->_entity1Name == entity1Name && it->_entity2Name == entity2Name) {
+			return it->_command;
+		}
+	}
+	return nullptr;
+}
+
+bool Game::startActionSection(ActionInfo::Action action, const Common::String &entity1Name, const Common::String &entity2Name) {
+	Script *const localScript = getLocalScript();
+	Script *const globalScript = getGlobalScript();
+
+	Command *command = nullptr;
+	if (localScript) {
+		command = findActionInfoCommand(localScript->getActionInfos(action), entity1Name, entity2Name);
+	}
+	if (!command && globalScript) {
+		command = findActionInfoCommand(globalScript->getActionInfos(action), entity1Name, entity2Name);
+	}
+	if (command) {
+		_scriptExecCtx.startCommand(command);
+		return true;
+	}
+
+	return false;
+}
+
 void Game::update() {
 	Command::ExecuteResult res = _scriptExecCtx.runActiveCommand();
 	if (res == Command::Finished && _delayedLocalScript) {
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 2855704..503cde9 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -37,6 +37,8 @@ class MutationOfJBEngine;
 class GameData;
 class Script;
 class Room;
+class Door;
+class Static;
 
 class Game {
 public:
@@ -49,6 +51,10 @@ public:
 	void changeScene(uint8 sceneId, bool partB);
 	Script *changeSceneDelayScript(uint8 sceneId, bool partB);
 
+	Door *findDoor(int16 x, int16 y);
+	Static *findStatic(int16 x, int16 y);
+	bool startActionSection(ActionInfo::Action action, const Common::String &entity1Name, const Common::String &entity2Name = Common::String());
+
 	void update();
 
 private:
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index 314eb56..acca555 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -150,8 +150,8 @@ Door *Scene::getDoor(uint8 doorId) {
 	return &_doors[doorId - 1];
 }
 
-Object *Scene::getObject(uint8 objectId) {
-	if (objectId == 0 || objectId > _noObjects) {
+Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
+	if (objectId == 0 || objectId > (!ignoreNo ? MIN(_noObjects, (uint8) ARRAYSIZE(_objects)) : ARRAYSIZE(_objects))) {
 		warning(_("Object %d does not exist"), objectId);
 		return nullptr;
 	}
@@ -159,8 +159,8 @@ Object *Scene::getObject(uint8 objectId) {
 	return &_objects[objectId - 1];
 }
 
-Static *Scene::getStatic(uint8 staticId) {
-	if (staticId == 0 || staticId > _noStatics) {
+Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
+	if (staticId == 0 || staticId > (!ignoreNo ? MIN(_noStatics, (uint8) ARRAYSIZE(_statics)) : ARRAYSIZE(_statics))) {
 		warning(_("Static %d does not exist"), staticId);
 		return nullptr;
 	}
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index eb474f8..4735753 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -34,6 +34,14 @@ namespace MutationOfJB {
 
 static const uint8 MAX_STR_LENGTH = 0x14;
 
+/*
+	There are 4 types of entities present in the game data:
+	- Door
+	- Object
+	- Static
+	- Bitmap
+*/
+
 struct Door {
 	/*
 		Door name.
@@ -115,8 +123,8 @@ struct Bitmap {
 struct Scene {
 
 	Door *getDoor(uint8 objectId);
-	Object *getObject(uint8 objectId);
-	Static *getStatic(uint8 staticId);
+	Object *getObject(uint8 objectId, bool ignoreNo = false);
+	Static *getStatic(uint8 staticId, bool ignoreNo = false);
 
 	uint8 _startup;
 	uint8 _unknown001;
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 1bd08dd..622be67 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -77,6 +77,7 @@ Common::Error MutationOfJBEngine::run() {
 	_console = new Console(this);
 	_screen = new Graphics::Screen();
 	_game = new Game(this);
+	ActionInfo::Action currentAction = ActionInfo::Walk;
 
 	setupCursor();
 
@@ -94,17 +95,35 @@ Common::Error MutationOfJBEngine::run() {
 			}
 			case Common::EVENT_LBUTTONDOWN:
 			{
-				const Scene *const scene = _game->getGameData().getScene(_game->getGameData()._currentScene);
-				if (scene) {
-					for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
-						const Door &door = scene->_doors[i];
-						if ((event.mouse.x >= door._x) && (event.mouse.x < door._x + door._width) && (event.mouse.y >= door._y) && (event.mouse.y < door._y + door._height)) {
-							_game->changeScene(door._destSceneId, false);
-						}
+				if (Door *const door = _game->findDoor(event.mouse.x, event.mouse.y)) {
+					if (!_game->startActionSection(currentAction, door->_name) && currentAction == ActionInfo::Walk && door->_destSceneId != 0) {
+						_game->changeScene(door->_destSceneId, _game->getGameData()._partB);
 					}
+				} else if (Static *const stat = _game->findStatic(event.mouse.x, event.mouse.y)) {
+					_game->startActionSection(currentAction, stat->_name);
 				}
 				break;
 			}
+			case Common::EVENT_KEYUP:
+			{
+				switch (event.kbd.ascii) {
+				case 'g':
+					currentAction = ActionInfo::Walk;
+					break;
+				case 'r':
+					currentAction = ActionInfo::Talk;
+					break;
+				case 's':
+					currentAction = ActionInfo::Look;
+					break;
+				case 'b':
+					currentAction = ActionInfo::Use;
+					break;
+				case 'n':
+					currentAction = ActionInfo::PickUp;
+					break;
+				}
+			}
 			default:
 				break;
 			}
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index b9b1515..f213de9 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -221,18 +221,7 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 	}
 
 	for (ActionInfos::iterator it = parseCtx._actionInfos.begin(); it != parseCtx._actionInfos.end(); ++it) {
-		if (it->_action == ActionInfo::Look) {
-			_lookActionInfos.push_back(*it);
-		}
-		if (it->_action == ActionInfo::Walk) {
-			_walkActionInfos.push_back(*it);
-		}
-		if (it->_action == ActionInfo::Talk) {
-			_talkActionInfos.push_back(*it);
-		}
-		if (it->_action == ActionInfo::Use) {
-			_useActionInfos.push_back(*it);
-		}
+		_actionInfos[it->_action].push_back(*it);
 	}
 
 	_macros = parseCtx._macros;
@@ -252,20 +241,8 @@ Script::~Script() {
 	destroy();
 }
 
-const ActionInfos &Script::getLookActionInfos() const {
-	return _lookActionInfos;
-}
-
-const ActionInfos &Script::getWalkActionInfos() const {
-	return _walkActionInfos;
-}
-
-const ActionInfos &Script::getTalkActionInfos() const {
-	return _talkActionInfos;
-}
-
-const ActionInfos &Script::getUseActionInfos() const {
-	return _useActionInfos;
+const ActionInfos &Script::getActionInfos(ActionInfo::Action action) {
+	return _actionInfos[action];
 }
 
 const Commands &Script::getAllCommands() const {
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 09aa942..8a15a48 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -48,15 +48,16 @@ typedef Common::Array<Command *> Commands;
 
 struct ActionInfo {
 	enum Action {
+		Look,
 		Walk,
 		Talk,
-		Look,
-		Use
+		Use,
+		PickUp
 	};
 
 	Action _action;
-	Common::String _object1Name;
-	Common::String _object2Name;
+	Common::String _entity1Name;
+	Common::String _entity2Name;
 	bool _walkTo;
 	Command *_command;
 };
@@ -125,10 +126,7 @@ public:
 	bool loadFromStream(Common::SeekableReadStream &stream);
 	~Script();
 
-	const ActionInfos &getLookActionInfos() const;
-	const ActionInfos &getWalkActionInfos() const;
-	const ActionInfos &getTalkActionInfos() const;
-	const ActionInfos &getUseActionInfos() const;
+	const ActionInfos &getActionInfos(ActionInfo::Action action);
 	const Commands &getAllCommands() const;
 	const Macros &getMacros() const;
 	const Startups &getStartups() const;
@@ -138,10 +136,7 @@ public:
 private:
 	void destroy();
 	Commands _allCommands;
-	ActionInfos _lookActionInfos;
-	ActionInfos _walkActionInfos;
-	ActionInfos _talkActionInfos;
-	ActionInfos _useActionInfos;
+	ActionInfos _actionInfos[5];
 	Macros _macros;
 	Startups _startups;
 };


Commit: 2b94873694619272f79f6b358d4a18c8bce13cd8
    https://github.com/scummvm/scummvm/commit/2b94873694619272f79f6b358d4a18c8bce13cd8
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Introduce better animation loader that supports diff frames.

Changed paths:
  A engines/mutationofjb/animationdecoder.cpp
  A engines/mutationofjb/animationdecoder.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/room.cpp
    engines/mutationofjb/room.h


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
new file mode 100644
index 0000000..585ad91
--- /dev/null
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/animationdecoder.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/util.h"
+#include "common/debug.h"
+#include "common/translation.h"
+
+namespace MutationOfJB {
+
+AnimationDecoder::AnimationDecoder(const Common::String &fileName) : _fileName(fileName) {
+	_surface.create(IMAGE_WIDTH, IMAGE_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
+	EncryptedFile file;
+	file.open(_fileName);
+
+	if (!file.isOpen()) {
+		reportFileMissingError(_fileName.c_str());
+
+		return false;
+	}
+
+	file.seek(0, SEEK_END);
+	const int32 endPos = file.pos();
+
+	// Skip header - we don't need it anyway.
+	file.seek(0x80);
+
+	int frameNo = 0;
+
+	while (file.pos() != endPos) {
+		// Record.
+		const uint32 length = file.readUint32LE();
+		const uint16 recordId = file.readUint16LE();
+		const uint16 subrecords = file.readUint16LE();
+
+		// Skip 8 empty bytes.
+		file.seek(8, SEEK_CUR);
+
+		// Subrecords.
+		if (recordId == 0xF1FA) {
+			for (int i = 0; i < subrecords; ++i) {
+				int32 filePos = file.pos();
+
+				const uint32 subLength = file.readUint32LE();
+				const uint16 type = file.readUint16LE();
+
+				if (type == 0x0B) {
+					loadPalette(file);
+					if (callback) {
+						callback->onPaletteUpdated(_palette);
+					}
+				} else if (type == 0x0F) {
+					loadFullFrame(file, subLength - 6);
+					if (callback) {
+						callback->onFrame(frameNo, _surface);
+					}
+				} else if (type == 0x0C) {
+					loadDiffFrame(file, subLength - 6);
+					if (callback) {
+						callback->onFrame(frameNo, _surface);
+					}
+				} else {
+					debug(_("Unsupported record type %02X."), type);
+					file.seek(subLength - 6, SEEK_CUR);
+				}
+
+				// Makes decoding more robust, because for some reason records might have extra data at the end.
+				file.seek(filePos + subLength, SEEK_SET);
+			}
+			frameNo++;
+		} else {
+			file.seek(length - 16, SEEK_CUR);
+		}
+	}
+	file.close();
+
+	return true;
+}
+
+void AnimationDecoder::loadPalette(Common::SeekableReadStream &file) {
+	uint16 packets = file.readUint16LE();
+	const uint8 skipCount = file.readByte();
+	int copyCount = file.readByte();
+	if (copyCount == 0) {
+		copyCount = PALETTE_COLORS;
+	}
+
+	while(packets--) {
+		file.read(_palette + skipCount * 3, copyCount * 3);
+
+		for (int j = skipCount * 3; j < (skipCount + copyCount) * 3; ++j) {
+			_palette[j] <<= 2; // Uses 6-bit colors.
+		}
+	}
+}
+
+void AnimationDecoder::loadFullFrame(EncryptedFile &file, uint32 size) {
+	uint8 *const pixels = reinterpret_cast<uint8 *>(_surface.getPixels());
+	uint8 *ptr = pixels;
+	uint32 readBytes = 0;
+	uint32 lines = 0;
+
+	while (readBytes != size) {
+		if (lines == 200) {
+			// Some full frames have an unknown byte at the end,
+			// so break when we encounter all 200 lines.
+			break;
+		}
+
+		uint8 no = file.readByte();
+		readBytes++;
+		while (no--) {
+			uint8 n = file.readByte();
+			readBytes++;
+			if (n < 0x80) {
+				// RLE - Copy color n times.
+				uint8 color = file.readByte();
+				readBytes++;
+				while(n--) {
+					*ptr++ = color;
+				}
+			} else {
+				// Take next 0x100 - n bytes as they are.
+				const uint32 rawlen = 0x100 - n;
+				file.read(ptr, rawlen);
+				readBytes += rawlen;
+				ptr += rawlen;
+			}
+		}
+		lines++;
+	}
+}
+
+void AnimationDecoder::loadDiffFrame(EncryptedFile &file, uint32) {
+	const uint16 firstLine = file.readUint16LE();
+	const uint16 numLines = file.readUint16LE();
+
+	for (uint16 line = firstLine; line < firstLine + numLines; ++line) {
+		uint8 *imageData = reinterpret_cast<uint8 *>(_surface.getBasePtr(0, firstLine));
+
+		uint8 runs = file.readByte();
+		while (runs--) {
+			uint8 localOffset = file.readByte();
+			uint8 num = file.readByte();
+
+			imageData += localOffset;
+			if (num == 0) {
+				// Ignore?
+				debug("Zero RLE number found.");
+			} else if (num < 0x80) {
+				file.read(imageData, num);
+				imageData += num;
+			} else {
+				const uint8 color = file.readByte();
+				const int no = 0x100 - num;
+				memset(imageData, color, no);
+				imageData += no;
+			}
+
+		}
+	}
+}
+
+AnimationDecoder::~AnimationDecoder() {
+	_surface.free();
+}
+
+}
diff --git a/engines/mutationofjb/animationdecoder.h b/engines/mutationofjb/animationdecoder.h
new file mode 100644
index 0000000..050aa3b
--- /dev/null
+++ b/engines/mutationofjb/animationdecoder.h
@@ -0,0 +1,69 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_ANIMATIONDECODER_H
+#define MUTATIONOFJB_ANIMATIONDECODER_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+#include "mutationofjb/encryptedfile.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace MutationOfJB {
+
+enum {
+	PALETTE_COLORS = 256,
+	PALETTE_SIZE = PALETTE_COLORS * 3,
+	IMAGE_WIDTH = 320,
+	IMAGE_HEIGHT = 200
+};
+
+class AnimationDecoderCallback {
+public:
+	virtual void onFrame(int frameNo, Graphics::Surface &surface) = 0;
+	virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) = 0;
+	virtual ~AnimationDecoderCallback() {}
+};
+
+class AnimationDecoder {
+public:
+	AnimationDecoder(const Common::String &fileName);
+	~AnimationDecoder();
+	bool decode(AnimationDecoderCallback *callback);
+
+private:
+	void loadPalette(Common::SeekableReadStream &stream);
+	void loadFullFrame(EncryptedFile &file, uint32 size);
+	void loadDiffFrame(EncryptedFile &file, uint32 size);
+
+	Common::String _fileName;
+	Graphics::Surface _surface;
+	byte _palette[PALETTE_SIZE];
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 917ab3c..b796698 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -18,6 +18,7 @@ MODULE_OBJS := \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
+	animationdecoder.o \
 	debug.o \
 	detection.o \
 	encryptedfile.o \
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 8d8fbdb..547cda4 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "mutationofjb/room.h"
+#include "mutationofjb/animationdecoder.h"
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
@@ -29,114 +30,34 @@
 
 namespace MutationOfJB {
 
-Room::Room(Graphics::Screen *screen) : _screen(screen) {}
-
-bool Room::load(uint8 roomNumber, bool roomB) {
-	EncryptedFile file;
-	Common::String fileName = Common::String::format("room%d%s.dat", roomNumber, roomB ? "b" : "");
-
-	file.open(fileName);
-
-	if (!file.isOpen()) {
-		reportFileMissingError(fileName.c_str());
-
-		return false;
-	}
-
-	file.seek(0x80);
-
-	while (!file.eos()) {
-		// Record.
-		const uint32 length = file.readUint32LE();
-		uint8 info[4] = {0};
-		file.read(info, 4);
-
-		// TODO Find out what these are.
-		uint32 unknown;
-		unknown = file.readUint32LE();
-		unknown = file.readUint32LE();
-
-		// Subrecords.
-		if (info[0] == 0xFA && info[1] == 0xF1) {
-			for (int i = 0; i < info[2]; ++i) {
-				const uint32 subLength = file.readUint32LE();
-				const uint16 type = file.readUint16LE();
-
-				if (type == 0x0B) {
-					loadPalette(file);
-				} else if (type == 0x0F) {
-					loadBackground(file, subLength - 6);
-				} else {
-					debug(_("Unsupported record type %02X."), type);
-					file.seek(subLength - 6, SEEK_CUR);
-				}
-			}
-		}
-	}
-
-	file.close();
-
-	return true;
+class RoomAnimationDecoderCallback : public AnimationDecoderCallback {
+public:
+	RoomAnimationDecoderCallback(Room &room) : _room(room) {}
+	virtual void onFrame(int frameNo, Graphics::Surface &surface) override;
+	virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
+private:
+	Room &_room;
+};
+
+void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE]) {
+	_room._screen->setPalette(palette, 0x00, 0xC0); // Load only 0xC0 colors.
 }
 
-void Room::loadPalette(EncryptedFile &file) {
-	uint32 unknown;
-
-	// TODO Find out what this is.
-	unknown = file.readUint32LE();
-
-	uint8 palette[PALETTE_SIZE];
-	file.read(palette, PALETTE_SIZE);
-
-	for (int j = 0; j < PALETTE_SIZE; ++j) {
-		palette[j] <<= 2; // Uses 6-bit colors.
+void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
+	if (frameNo != 0) {
+		return;
 	}
 
-	_screen->setPalette(palette, 0x00, 0xC0); // Load only 0xC0 colors.
+	_room._screen->blitFrom(surface);
 }
 
-void Room::loadBackground(EncryptedFile &file, uint32 size) {
-	_screen->clear();
-
-	uint8 *const pixels = static_cast<uint8 *>(_screen->getPixels());
-	uint8 *ptr = pixels;
-	uint32 readBytes = 0;
-	uint32 lines = 0;
-
-	while (readBytes != size) {
-		if (lines == 200) {
-			// Some background files have an unknown byte at the end,
-			// so break when we encounter all 200 lines.
-			break;
-		}
-
-		uint8 no = file.readByte();
-		readBytes++;
-		while (no--) {
-			uint8 n = file.readByte();
-			readBytes++;
-			if (n < 0x80) {
-				// RLE - Copy color n times.
-				uint8 color = file.readByte();
-				readBytes++;
-				while(n--) {
-					*ptr++ = color;
-				}
-			} else {
-				// Take next 0x100 - n bytes as they are.
-				const uint32 rawlen = 0x100 - n;
-				file.read(ptr, rawlen);
-				readBytes += rawlen;
-				ptr += rawlen;
-			}
-		}
-		lines++;
-	}
-	if (readBytes < size) {
-		file.seek(size - readBytes, SEEK_CUR);
-	}
+Room::Room(Graphics::Screen *screen) : _screen(screen) {}
 
-	_screen->update();
+bool Room::load(uint8 roomNumber, bool roomB) {
+	const Common::String fileName = Common::String::format("room%d%s.dat", roomNumber, roomB ? "b" : "");
+	AnimationDecoder decoder(fileName);
+	RoomAnimationDecoderCallback callback(*this);
+	return decoder.decode(&callback);
 }
 
 }
diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h
index 7158580..e8f4440 100644
--- a/engines/mutationofjb/room.h
+++ b/engines/mutationofjb/room.h
@@ -35,12 +35,11 @@ class EncryptedFile;
 
 class Room {
 public:
+	friend class RoomAnimationDecoderCallback;
+
 	Room(Graphics::Screen *screen);
 	bool load(uint8 roomNumber, bool roomB);
 private:
-	void loadPalette(EncryptedFile &file);
-	void loadBackground(EncryptedFile &file, uint32 size);
-
 	Graphics::Screen *_screen;
 };
 


Commit: 99d9055e201b0518baad741f4f46cb86c2f7e172
    https://github.com/scummvm/scummvm/commit/99d9055e201b0518baad741f4f46cb86c2f7e172
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Load object frames and implement special handling for map scenes.

Changed paths:
    engines/mutationofjb/animationdecoder.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/room.cpp
    engines/mutationofjb/room.h


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
index 585ad91..5b20153 100644
--- a/engines/mutationofjb/animationdecoder.cpp
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -159,7 +159,7 @@ void AnimationDecoder::loadDiffFrame(EncryptedFile &file, uint32) {
 	const uint16 numLines = file.readUint16LE();
 
 	for (uint16 line = firstLine; line < firstLine + numLines; ++line) {
-		uint8 *imageData = reinterpret_cast<uint8 *>(_surface.getBasePtr(0, firstLine));
+		uint8 *imageData = reinterpret_cast<uint8 *>(_surface.getBasePtr(0, line));
 
 		uint8 runs = file.readByte();
 		while (runs--) {
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index f533a94..4ce2418 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -65,6 +65,7 @@ Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
 	registerCmd("showstartup", WRAP_METHOD(Console, cmd_showstartup));
 	registerCmd("changescene", WRAP_METHOD(Console, cmd_changescene));
 	registerCmd("dumpsceneinfo", WRAP_METHOD(Console, cmd_dumpsceneinfo));
+	registerCmd("dumpdoorinfo", WRAP_METHOD(Console, cmd_dumpdoorinfo));
 	registerCmd("dumpobjectinfo", WRAP_METHOD(Console, cmd_dumpobjectinfo));
 	registerCmd("dumpstaticinfo", WRAP_METHOD(Console, cmd_dumpstaticinfo));
 }
@@ -324,6 +325,38 @@ bool Console::cmd_dumpsceneinfo(int argc, const char **argv) {
 	return true;
 }
 
+bool Console::cmd_dumpdoorinfo(int argc, const char **argv) {
+	if (argc == 3) {
+		const uint8 sceneId = atoi(argv[1]);
+		const uint8 doorId = atoi(argv[2]);
+
+		Scene *const scene = _vm->getGame().getGameData().getScene(sceneId);
+		if (scene) {
+			Door *const door = scene->getDoor(doorId);
+			if (door) {
+				debugPrintf("Name: '%s'\n", convertToASCII(door->_name).c_str());
+				debugPrintf("DestSceneId: %u\n", (unsigned int) door->_destSceneId);
+				debugPrintf("DestX: %u\n", (unsigned int) door->_destX);
+				debugPrintf("DestY: %u\n", (unsigned int) door->_destY);
+				debugPrintf("X: %u\n", (unsigned int) door->_x);
+				debugPrintf("Y: %u\n", (unsigned int) door->_y);
+				debugPrintf("Width: %u\n", (unsigned int) door->_width);
+				debugPrintf("Height: %u\n", (unsigned int) door->_height);
+				debugPrintf("WalkToX: %u\n", (unsigned int) door->_walkToX);
+				debugPrintf("WalkToY: %u\n", (unsigned int) door->_walkToY);
+				debugPrintf("SP: %u\n", (unsigned int) door->_SP);
+			} else {
+				debugPrintf(_("Door %u not found.\n"), (unsigned int) doorId);
+			}
+		} else {
+			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+		}
+	} else {
+		debugPrintf(_("dumpdoorinfo <sceneid> <doorid>\n"));
+	}
+
+	return true;
+}
 bool Console::cmd_dumpobjectinfo(int argc, const char **argv) {
 	if (argc == 3) {
 		const uint8 sceneId = atoi(argv[1]);
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index e57b787..0cd7257 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -45,6 +45,7 @@ private:
 	bool cmd_showstartup(int argc, const char **argv);
 	bool cmd_changescene(int argc, const char **argv);
 	bool cmd_dumpsceneinfo(int argc, const char **argv);
+	bool cmd_dumpdoorinfo(int argc, const char **argv);
 	bool cmd_dumpobjectinfo(int argc, const char **argv);
 	bool cmd_dumpstaticinfo(int argc, const char **argv);
 
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 156c020..a46ca63 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -46,7 +46,7 @@ Game::Game(MutationOfJBEngine *vm)
 	globalScriptFile.close();
 
 	_localScript = nullptr;
-	_room = new Room(_vm->getScreen());
+	_room = new Room(this, _vm->getScreen());
 
 	changeScene(13, false); // Initial scene.
 }
@@ -55,6 +55,10 @@ GameData &Game::getGameData() {
 	return *_gameData;
 }
 
+Room &Game::getRoom() {
+	return *_room;
+}
+
 Script *Game::getGlobalScript() const {
 	return _globalScript;
 }
@@ -122,36 +126,6 @@ Script *Game::changeSceneDelayScript(uint8 sceneId, bool partB) {
 	return _delayedLocalScript;
 }
 
-Door *Game::findDoor(int16 x, int16 y) {
-	Scene *scene = _gameData->getCurrentScene();
-	if (!scene)
-		return nullptr;
-
-	for (int i = 0; i < MIN(ARRAYSIZE(scene->_doors), (int) scene->_noDoors); ++i) {
-		Door &door = scene->_doors[i];
-		if ((x >= door._x) && (x < door._x + door._width) && (y >= door._y) && (y < door._y + door._height)) {
-			return &door;
-		}
-	}
-
-	return nullptr;
-}
-
-Static *Game::findStatic(int16 x, int16 y) {
-	Scene *scene = _gameData->getCurrentScene();
-	if (!scene)
-		return nullptr;
-
-	for (int i = 0; i < MIN(ARRAYSIZE(scene->_statics), (int) scene->_noStatics); ++i) {
-		Static &stat = scene->_statics[i];
-		if ((x >= stat._x) && (x < stat._x + stat._width) && (y >= stat._y) && (y < stat._y + stat._height)) {
-			return &stat;
-		}
-	}
-
-	return nullptr;
-}
-
 static Command *findActionInfoCommand(const ActionInfos &infos, const Common::String &entity1Name, const Common::String &entity2Name = Common::String()) {
 	for (ActionInfos::const_iterator it = infos.begin(); it != infos.end(); ++it) {
 		if (it->_entity1Name == entity1Name && it->_entity2Name == entity2Name) {
@@ -180,6 +154,10 @@ bool Game::startActionSection(ActionInfo::Action action, const Common::String &e
 	return false;
 }
 
+bool Game::isCurrentSceneMap() const {
+	return _gameData->_currentScene == 12;
+}
+
 void Game::update() {
 	Command::ExecuteResult res = _scriptExecCtx.runActiveCommand();
 	if (res == Command::Finished && _delayedLocalScript) {
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 503cde9..c71b5f1 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -39,22 +39,23 @@ class Script;
 class Room;
 class Door;
 class Static;
+class Bitmap;
 
 class Game {
 public:
 	Game(MutationOfJBEngine *vm);
 	GameData &getGameData();
-
+	Room &getRoom();
 	Script *getGlobalScript() const;
 	Script *getLocalScript() const;
 
 	void changeScene(uint8 sceneId, bool partB);
 	Script *changeSceneDelayScript(uint8 sceneId, bool partB);
 
-	Door *findDoor(int16 x, int16 y);
-	Static *findStatic(int16 x, int16 y);
 	bool startActionSection(ActionInfo::Action action, const Common::String &entity1Name, const Common::String &entity2Name = Common::String());
 
+	bool isCurrentSceneMap() const;
+
 	void update();
 
 private:
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index acca555..fb9e642 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -151,7 +151,7 @@ Door *Scene::getDoor(uint8 doorId) {
 }
 
 Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
-	if (objectId == 0 || objectId > (!ignoreNo ? MIN(_noObjects, (uint8) ARRAYSIZE(_objects)) : ARRAYSIZE(_objects))) {
+	if (objectId == 0 || objectId > getNoObjects(ignoreNo))  {
 		warning(_("Object %d does not exist"), objectId);
 		return nullptr;
 	}
@@ -168,6 +168,60 @@ Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
 	return &_statics[staticId - 1];
 }
 
+uint8 Scene::getNoDoors(bool ignoreNo) const {
+	return (!ignoreNo ? MIN(_noDoors, (uint8) ARRAYSIZE(_doors)) : ARRAYSIZE(_doors));
+}
+
+uint8 Scene::getNoObjects(bool ignoreNo) const {
+	return (!ignoreNo ? MIN(_noObjects, (uint8) ARRAYSIZE(_objects)) : ARRAYSIZE(_objects));
+}
+
+uint8 Scene::getNoStatics(bool ignoreNo) const {
+	return (!ignoreNo ? MIN(_noStatics, (uint8) ARRAYSIZE(_statics)) : ARRAYSIZE(_statics));
+}
+
+Door *Scene::findDoor(int16 x, int16 y, int *index) {
+	for (int i = 0; i < getNoDoors(); ++i) {
+		Door &door = _doors[i];
+		if ((x >= door._x) && (x < door._x + door._width) && (y >= door._y) && (y < door._y + door._height)) {
+			if (index) {
+				*index = i + 1;
+			}
+			return &door;
+		}
+	}
+
+	return nullptr;
+}
+
+Static *Scene::findStatic(int16 x, int16 y, int *index) {
+	for (int i = 0; i < getNoStatics(); ++i) {
+		Static &stat = _statics[i];
+		if ((x >= stat._x) && (x < stat._x + stat._width) && (y >= stat._y) && (y < stat._y + stat._height)) {
+			if (index) {
+				*index = i + 1;
+			}
+			return &stat;
+		}
+	}
+
+	return nullptr;
+}
+
+Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) {
+	for (int i = 0; i < ARRAYSIZE(_bitmaps); ++i) {
+		Bitmap &bitmap = _bitmaps[i];
+		if ((x >= bitmap._x1) && (x <= bitmap._x2) && (y >= bitmap._y1) && (y <= bitmap._y2)) {
+			if (index) {
+				*index = i + 1;
+			}
+			return &bitmap;
+		}
+	}
+
+	return nullptr;
+}
+
 
 GameData::GameData()
 	: _currentScene(0),
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 4735753..b0c64c5 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -121,11 +121,18 @@ struct Bitmap {
 
 
 struct Scene {
-
 	Door *getDoor(uint8 objectId);
 	Object *getObject(uint8 objectId, bool ignoreNo = false);
 	Static *getStatic(uint8 staticId, bool ignoreNo = false);
 
+	uint8 getNoDoors(bool ignoreNo = false) const;
+	uint8 getNoObjects(bool ignoreNo = false) const;
+	uint8 getNoStatics(bool ignoreNo = false) const;
+
+	Door *findDoor(int16 x, int16 y, int *index = nullptr);
+	Static *findStatic(int16 x, int16 y, int *index = nullptr);
+	Bitmap *findBitmap(int16 x, int16 y, int *index = nullptr);
+
 	uint8 _startup;
 	uint8 _unknown001;
 	uint8 _unknown002;
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 622be67..df8ad11 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -35,13 +35,16 @@
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/debug.h"
+#include "mutationofjb/room.h"
 
 namespace MutationOfJB {
 
 MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 : Engine(syst),
  _console(nullptr),
- _screen(nullptr) {
+ _screen(nullptr),
+ _currentAction(ActionInfo::Walk),
+ _mapObjectId(0) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -69,6 +72,94 @@ Game &MutationOfJBEngine::getGame() {
 	return *_game;
 }
 
+void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
+	Scene *const scene = _game->getGameData().getCurrentScene();
+
+	switch (event.type) {
+	case Common::EVENT_LBUTTONDOWN:
+	{
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+
+		if (Door *const door = scene->findDoor(x, y)) {
+			if (!_game->startActionSection(_currentAction, door->_name) && _currentAction == ActionInfo::Walk && door->_destSceneId != 0) {
+				_game->changeScene(door->_destSceneId, _game->getGameData()._partB);
+			}
+		} else if (Static *const stat = scene->findStatic(x, y)) {
+			if (stat->_active == 1) {
+				_game->startActionSection(_currentAction, stat->_name);
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+/*
+	Special handling for map scenes.
+
+	Bitmaps define mouse clickable areas.
+	Statics are used to start actions.
+	Objects are used for showing labels.
+
+*/
+void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
+	Scene *const scene = _game->getGameData().getCurrentScene();
+
+	switch (event.type) {
+	case Common::EVENT_LBUTTONDOWN:
+	{
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+
+		int index = 0;
+		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index))	{
+			Static *const stat = scene->getStatic(index);
+			if (stat && stat->_active == 1) {
+				_game->startActionSection(ActionInfo::Walk, stat->_name);
+			}
+		}
+		break;
+	}
+	case Common::EVENT_MOUSEMOVE:
+	{
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+
+		int index = 0;
+		bool found = false;
+		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index))	{
+			Static *const stat = scene->getStatic(index);
+			if (stat && stat->_active == 1) {
+				Object *const object = scene->getObject(index);
+				if (object) {
+					found = true;
+					if (index != _mapObjectId) {
+						if (_mapObjectId) {
+							_game->getRoom().drawObjectAnimation(_mapObjectId, 1);
+							_mapObjectId = 0;
+						}
+
+						_mapObjectId = index;
+						_game->getRoom().drawObjectAnimation(_mapObjectId, 0);
+					}
+				}
+			}
+		}
+
+		if (!found && _mapObjectId != 0) {
+			_game->getRoom().drawObjectAnimation(_mapObjectId, 1);
+			_mapObjectId = 0;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+
 Common::Error MutationOfJBEngine::run() {
 	debug("MutationOfJBEngine::run");
 
@@ -77,11 +168,10 @@ Common::Error MutationOfJBEngine::run() {
 	_console = new Console(this);
 	_screen = new Graphics::Screen();
 	_game = new Game(this);
-	ActionInfo::Action currentAction = ActionInfo::Walk;
 
 	setupCursor();
 
-	while(!shouldQuit()) {
+	while (!shouldQuit()) {
 		Common::Event event;
 		while (_eventMan->pollEvent(event)) {
 			switch (event.type) {
@@ -93,45 +183,40 @@ Common::Error MutationOfJBEngine::run() {
 				}
 				break;
 			}
-			case Common::EVENT_LBUTTONDOWN:
-			{
-				if (Door *const door = _game->findDoor(event.mouse.x, event.mouse.y)) {
-					if (!_game->startActionSection(currentAction, door->_name) && currentAction == ActionInfo::Walk && door->_destSceneId != 0) {
-						_game->changeScene(door->_destSceneId, _game->getGameData()._partB);
-					}
-				} else if (Static *const stat = _game->findStatic(event.mouse.x, event.mouse.y)) {
-					_game->startActionSection(currentAction, stat->_name);
-				}
-				break;
-			}
 			case Common::EVENT_KEYUP:
 			{
 				switch (event.kbd.ascii) {
 				case 'g':
-					currentAction = ActionInfo::Walk;
+					_currentAction = ActionInfo::Walk;
 					break;
 				case 'r':
-					currentAction = ActionInfo::Talk;
+					_currentAction = ActionInfo::Talk;
 					break;
 				case 's':
-					currentAction = ActionInfo::Look;
+					_currentAction = ActionInfo::Look;
 					break;
 				case 'b':
-					currentAction = ActionInfo::Use;
+					_currentAction = ActionInfo::Use;
 					break;
 				case 'n':
-					currentAction = ActionInfo::PickUp;
+					_currentAction = ActionInfo::PickUp;
 					break;
 				}
 			}
 			default:
 				break;
 			}
+
+			if (!_game->isCurrentSceneMap()) {
+				handleNormalScene(event);
+			} else {
+				handleMapScene(event);
+			}
 		}
 
 		_console->onFrame();
 		_game->update();
-		_system->delayMillis(40);
+		_system->delayMillis(10);
 		_screen->update();
 	}
 
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 893cca9..ef0c973 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -24,9 +24,14 @@
 #define MUTATIONOFJB_MUTATIONOFJB_H
 
 #include "engines/engine.h"
+#include "mutationofjb/script.h"
+
+namespace Common {
+class Event;
+}
 
 namespace Graphics {
-	class Screen;
+class Screen;
 }
 
 namespace MutationOfJB {
@@ -46,10 +51,14 @@ public:
 private:
 	bool loadGameData(bool partB);
 	void setupCursor();
+	void handleNormalScene(const Common::Event &event);
+	void handleMapScene(const Common::Event &event);
 
 	Console *_console;
 	Graphics::Screen *_screen;
 	Game *_game;
+	ActionInfo::Action _currentAction;
+	uint8 _mapObjectId;
 };
 
 
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 547cda4..66da3ac 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -23,6 +23,8 @@
 #include "mutationofjb/room.h"
 #include "mutationofjb/animationdecoder.h"
 #include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
 #include "common/translation.h"
@@ -44,20 +46,75 @@ void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE])
 }
 
 void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
-	if (frameNo != 0) {
-		return;
+	if (frameNo == 0) {
+		_room._screen->blitFrom(surface);
 	}
 
-	_room._screen->blitFrom(surface);
+	const int frameNo1 = frameNo + 1;
+
+	Scene *scene = _room._game->getGameData().getCurrentScene();
+	if (scene) {
+		const uint8 noObjects = scene->getNoObjects();
+		for (int i = 0; i < noObjects; ++i) {
+			Object &object = scene->_objects[i];
+			const uint16 startFrame = (object._WY << 8) + object._FS;
+			if (frameNo1 >= startFrame && frameNo1 < startFrame + object._NA) {
+				const int x = object._x;
+				const int y = object._y;
+				const int w = object._XL / 4 * 4;
+				const int h = object._YL / 4 * 4;
+				Common::Rect rect(x, y, x + w, y + h);
+
+				const Graphics::Surface sharedSurface = surface.getSubArea(rect);
+				Graphics::Surface outSurface;
+				outSurface.copyFrom(sharedSurface);
+				_room._surfaces[_room._objectsStart[i] + frameNo1 - startFrame] = outSurface;
+			}
+		}
+	}
 }
 
-Room::Room(Graphics::Screen *screen) : _screen(screen) {}
+Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen) {}
 
 bool Room::load(uint8 roomNumber, bool roomB) {
+	_objectsStart.clear();
+
+	Scene *const scene = _game->getGameData().getCurrentScene();
+	if (scene) {
+		const uint8 noObjects = scene->getNoObjects();
+		for (int i = 0; i < noObjects; ++i) {
+			uint8 firstIndex = 0;
+			if (i != 0) {
+				firstIndex = _objectsStart[i - 1] + scene->_objects[i - 1]._NA;
+			}
+			_objectsStart.push_back(firstIndex);
+
+			uint8 numAnims = scene->_objects[i]._NA;
+			while (numAnims--) {
+				_surfaces.push_back(Graphics::Surface());
+			}
+		}
+	}
+
 	const Common::String fileName = Common::String::format("room%d%s.dat", roomNumber, roomB ? "b" : "");
 	AnimationDecoder decoder(fileName);
 	RoomAnimationDecoderCallback callback(*this);
 	return decoder.decode(&callback);
 }
 
+void Room::drawObjectAnimation(uint8 objectId, int animOffset) {
+	Scene *const scene = _game->getGameData().getCurrentScene();
+	if (!scene) {
+		return;
+	}
+	Object *const object = scene->getObject(objectId);
+	if (!object) {
+		return;
+	}
+
+	const int startFrame = _objectsStart[objectId - 1];
+	const int animFrame = startFrame + animOffset;
+	_screen->blitFrom(_surfaces[animFrame], Common::Point(object->_x, object->_y));
+}
+
 }
diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h
index e8f4440..a2be2fc 100644
--- a/engines/mutationofjb/room.h
+++ b/engines/mutationofjb/room.h
@@ -24,23 +24,30 @@
 #define MUTATIONOFJB_ROOM_H
 
 #include "common/scummsys.h"
+#include "common/array.h"
+#include "graphics/surface.h"
 
 namespace Graphics {
-	class Screen;
+class Screen;
 }
 
 namespace MutationOfJB {
 
 class EncryptedFile;
+class Game;
 
 class Room {
 public:
 	friend class RoomAnimationDecoderCallback;
 
-	Room(Graphics::Screen *screen);
+	Room(Game *game, Graphics::Screen *screen);
 	bool load(uint8 roomNumber, bool roomB);
+	void drawObjectAnimation(uint8 objectId, int animOffset);
 private:
+	Game *_game;
 	Graphics::Screen *_screen;
+	Common::Array<Graphics::Surface> _surfaces;
+	Common::Array<int> _objectsStart;
 };
 
 }


Commit: 9a3a66ab685c33dab1a85cf2aae73d1df7e45c29
    https://github.com/scummvm/scummvm/commit/9a3a66ab685c33dab1a85cf2aae73d1df7e45c29
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix object animatation loader.

Changed paths:
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 66da3ac..5ed6ca3 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -61,8 +61,8 @@ void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surfa
 			if (frameNo1 >= startFrame && frameNo1 < startFrame + object._NA) {
 				const int x = object._x;
 				const int y = object._y;
-				const int w = object._XL / 4 * 4;
-				const int h = object._YL / 4 * 4;
+				const int w = (object._XL + 3) / 4 * 4; // Original code uses this to round up width to a multiple of 4.
+				const int h = object._YL;
 				Common::Rect rect(x, y, x + w, y + h);
 
 				const Graphics::Surface sharedSurface = surface.getSubArea(rect);


Commit: 9af3d8a2381fe7c7440330a9aa338f51cd734990
    https://github.com/scummvm/scummvm/commit/9af3d8a2381fe7c7440330a9aa338f51cd734990
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement UI for inventory.

Changed paths:
  A engines/mutationofjb/gui.cpp
  A engines/mutationofjb/gui.h
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/debug.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/inventory.cpp
    engines/mutationofjb/inventory.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 241932a..04731c2 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -35,6 +35,7 @@ public:
 
 class SeqCommand : public Command {
 public:
+	SeqCommand() : _nextCommand(nullptr) {}
 	void setNextCommand(Command *nextCommand);
 	virtual Command *next() const override;
 
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 4ce2418..171eca5 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -23,6 +23,7 @@
 #include "mutationofjb/debug.h"
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
+#include "mutationofjb/inventory.h"
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/commands/command.h"
@@ -68,6 +69,7 @@ Console::Console(MutationOfJBEngine *vm) : _vm(vm) {
 	registerCmd("dumpdoorinfo", WRAP_METHOD(Console, cmd_dumpdoorinfo));
 	registerCmd("dumpobjectinfo", WRAP_METHOD(Console, cmd_dumpobjectinfo));
 	registerCmd("dumpstaticinfo", WRAP_METHOD(Console, cmd_dumpstaticinfo));
+	registerCmd("listinventory", WRAP_METHOD(Console, cmd_listinventory));
 }
 
 bool Console::cmd_showallcommands(int argc, const char **argv) {
@@ -438,4 +440,13 @@ Script *Console::getScriptFromArg(const char *arg) {
 	return script;
 }
 
+bool Console::cmd_listinventory(int, const char **) {
+	Inventory &inventory =_vm->getGame().getGameData().getInventory();
+	const Inventory::Items &items = inventory.getItems();
+	for (Inventory::Items::const_iterator it = items.begin(); it != items.end(); ++it) {
+		debugPrintf("%s\n", convertToASCII(*it).c_str());
+	}
+	return true;
+}
+
 }
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 0cd7257..24b1e95 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -48,6 +48,7 @@ private:
 	bool cmd_dumpdoorinfo(int argc, const char **argv);
 	bool cmd_dumpobjectinfo(int argc, const char **argv);
 	bool cmd_dumpstaticinfo(int argc, const char **argv);
+	bool cmd_listinventory(int argc, const char **argv);
 
 	void showIndent(int indentLevel);
 	void showCommands(Command *command, int indentLevel = 0);
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index a46ca63..b7f1893 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -35,7 +35,7 @@
 namespace MutationOfJB {
 
 Game::Game(MutationOfJBEngine *vm)
-: _vm(vm), _delayedLocalScript(nullptr), _scriptExecCtx(*this) {
+: _vm(vm), _delayedLocalScript(nullptr), _gui(*this, _vm->getScreen()), _scriptExecCtx(*this) {
 	_gameData = new GameData;
 	loadGameData(false);
 
@@ -48,6 +48,8 @@ Game::Game(MutationOfJBEngine *vm)
 	_localScript = nullptr;
 	_room = new Room(this, _vm->getScreen());
 
+	_gui.init();
+
 	changeScene(13, false); // Initial scene.
 }
 
@@ -84,6 +86,10 @@ bool Game::loadGameData(bool partB) {
 }
 
 Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
+	if (isCurrentSceneMap()) {
+		_gui.markInventoryDirty();
+	}
+
 	_gameData->_lastScene = _gameData->_currentScene;
 	_gameData->_currentScene = sceneId;
 	_gameData->_partB = partB;
@@ -165,6 +171,12 @@ void Game::update() {
 		_localScript = _delayedLocalScript;
 		_delayedLocalScript = nullptr;
 	}
+
+	_gui.update();
+}
+
+Gui &Game::getGui() {
+	return _gui;
 }
 
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index c71b5f1..71dadf2 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "mutationofjb/script.h"
+#include "mutationofjb/gui.h"
 
 namespace Common {
 class String;
@@ -58,6 +59,8 @@ public:
 
 	void update();
 
+	Gui &getGui();
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
@@ -71,6 +74,7 @@ private:
 	Script *_localScript;
 	Script *_delayedLocalScript;
 	Room *_room;
+	Gui _gui;
 
 	ScriptExecutionContext _scriptExecCtx;
 };
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index fb9e642..099cea7 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -226,7 +226,9 @@ Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) {
 GameData::GameData()
 	: _currentScene(0),
 	_lastScene(0),
-	_partB(false) {}
+	_partB(false),
+	_inventory()
+	{}
 
 Scene *GameData::getScene(uint8 sceneId) {
 	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
@@ -241,6 +243,10 @@ Scene *GameData::getCurrentScene() {
 	return getScene(_currentScene);
 }
 
+Inventory &GameData::getInventory() {
+	return _inventory;
+}
+
 bool GameData::loadFromStream(Common::ReadStream &stream) {
 	for (int i = 0; i < ARRAYSIZE(_scenes); ++i) {
 		_scenes[i].loadFromStream(stream);
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index b0c64c5..64de01e 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -164,6 +164,7 @@ public:
 	GameData();
 	Scene *getScene(uint8 sceneId);
 	Scene *getCurrentScene();
+	Inventory &getInventory();
 
 	bool loadFromStream(Common::ReadStream &stream);
 
@@ -174,7 +175,6 @@ public:
 	Common::String _currentAPK;
 private:
 	Scene _scenes[45];
-
 };
 
 }
diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
new file mode 100644
index 0000000..5ceed67
--- /dev/null
+++ b/engines/mutationofjb/gui.cpp
@@ -0,0 +1,181 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/gui.h"
+#include "mutationofjb/animationdecoder.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/inventory.h"
+#include "mutationofjb/util.h"
+#include "common/rect.h"
+#include "graphics/screen.h"
+
+namespace MutationOfJB {
+
+enum ButtonType {
+	BUTTON_WALK,
+	BUTTON_TALK,
+	BUTTON_LOOK,
+	BUTTON_USE,
+	BUTTON_PICKUP,
+	BUTTON_SCROLL_LEFT,
+	BUTTON_SCROLL_RIGHT,
+	BUTTON_SETTINGS
+};
+
+enum {
+	INVENTORY_START_X = 88,
+	INVENTORY_START_Y = 149,
+	INVENTORY_ITEM_WIDTH = 34,
+	INVENTORY_ITEM_HEIGHT = 33,
+	INVENTORY_ITEMS_PER_LINE = 8,
+	INVENTORY_ITEMS_LINES = 5
+};
+
+static Common::Rect ButtonRects[] = {
+	Common::Rect(0, 148, 67, 158), // Walk
+	Common::Rect(0, 158, 67, 168), // Talk
+	Common::Rect(0, 168, 67, 178), // Look
+	Common::Rect(0, 178, 67, 188), // Use
+	Common::Rect(0, 188, 67, 198), // PickUp
+	Common::Rect(67, 149, 88, 174), // ScrollLeft
+	Common::Rect(67, 174, 88, 199), // ScrollRight
+	Common::Rect(301, 148, 320, 200) // Settings
+};
+
+Gui::Gui(Game &game, Graphics::Screen *screen)
+	: _game(game),
+	_screen(screen),
+	_inventoryDirty(false) {
+}
+
+bool Gui::init() {
+	const bool result1 = loadInventoryList();
+	const bool result2 = loadInventoryGfx();
+
+	_game.getGameData().getInventory().setObserver(this);
+
+	return result1 && result2;
+}
+
+void Gui::markInventoryDirty() {
+	_inventoryDirty = true;
+}
+
+void Gui::update() {
+	if (_inventoryDirty) {
+		drawInventory();
+		_inventoryDirty = false;
+	}
+}
+
+class InventoryAnimationDecoderCallback : public AnimationDecoderCallback {
+public:
+	InventoryAnimationDecoderCallback(Gui &gui) : _gui(gui) {}
+	virtual void onFrame(int frameNo, Graphics::Surface &surface) override;
+	virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
+private:
+	Gui &_gui;
+};
+
+void InventoryAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE]) {
+	_gui._screen->setPalette(palette + 0xC0 * 3, 0xC0, 0x20); // Load only 0x20 colors.
+}
+
+void InventoryAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
+	if (frameNo < 3) {
+		Graphics::Surface outSurface;
+		outSurface.copyFrom(surface);
+		_gui._inventorySurfaces.push_back(outSurface);
+	}
+}
+
+bool Gui::loadInventoryGfx() {
+	AnimationDecoder decoder("icons.dat");
+	InventoryAnimationDecoderCallback callback(*this);
+	return decoder.decode(&callback);
+}
+
+bool Gui::loadInventoryList() {
+	EncryptedFile file;
+	const char *fileName = "fixitems.dat";
+	file.open(fileName);
+	if (!file.isOpen()) {
+		reportFileMissingError(fileName);
+		return false;
+	}
+
+	int itemIndex = 0;
+	while (!file.eos()) {
+		Common::String line = file.readLine();
+		if (line.empty() || line.hasPrefix("#")) {
+			continue;
+		}
+		const char *firstSpace = strchr(line.c_str(), ' ');
+		if (!firstSpace) {
+			continue;
+		}
+		const int len = firstSpace - line.c_str();
+		if (!len) {
+			continue;
+		}
+		Common::String item(line.c_str(), len);
+		_inventoryItems[item] = itemIndex;
+		itemIndex++;
+	}
+
+	return true;
+}
+
+void Gui::drawInventoryItem(const Common::String &item, int pos) {
+	InventoryMap::iterator it = _inventoryItems.find(item);
+	if (it == _inventoryItems.end()) {
+		return;
+	}
+
+	const int index = it->_value;
+	const int surfaceNo = index / (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
+	const int indexInSurface = index % (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
+	const int itemX = indexInSurface % INVENTORY_ITEMS_PER_LINE;
+	const int itemY = indexInSurface / INVENTORY_ITEMS_PER_LINE;
+
+	Common::Point destStartPos(INVENTORY_START_X + pos * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y);
+	Common::Rect sourceRect(itemX * INVENTORY_ITEM_WIDTH, itemY * INVENTORY_ITEM_HEIGHT, (itemX + 1) * INVENTORY_ITEM_WIDTH, (itemY + 1) * INVENTORY_ITEM_HEIGHT);
+	_screen->blitFrom(_inventorySurfaces[surfaceNo], sourceRect, destStartPos);
+}
+
+void Gui::drawInventory() {
+	Inventory &inventory = _game.getGameData().getInventory();
+	const Inventory::Items &items = inventory.getItems();
+	Common::Rect fullRect(INVENTORY_START_X, INVENTORY_START_Y, INVENTORY_START_X + Inventory::VISIBLE_ITEMS * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y + INVENTORY_ITEM_HEIGHT);
+	_screen->fillRect(fullRect, 0x00);
+	for (int i = 0; i < MIN((int) items.size(), (int) Inventory::VISIBLE_ITEMS); ++i) {
+		drawInventoryItem(items[i], i);
+	}
+}
+
+void Gui::onInventoryChanged() {
+	markInventoryDirty();
+}
+
+}
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
new file mode 100644
index 0000000..27e0b0c
--- /dev/null
+++ b/engines/mutationofjb/gui.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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_GUI_H
+#define MUTATIONOFJB_GUI_H
+
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "graphics/surface.h"
+#include "mutationofjb/inventory.h"
+
+namespace Graphics {
+class Screen;
+}
+
+namespace MutationOfJB {
+
+class Game;
+
+class Gui : public InventoryObserver {
+public:
+	friend class InventoryAnimationDecoderCallback;
+	Gui(Game &game, Graphics::Screen *screen);
+	bool init();
+	void update();
+
+	void markInventoryDirty();
+
+	virtual void onInventoryChanged() override;
+
+private:
+	bool loadInventoryGfx();
+	bool loadInventoryList();
+	void drawInventoryItem(const Common::String &item, int pos);
+	void drawInventory();
+
+	typedef Common::HashMap<Common::String, int> InventoryMap;
+
+	Game &_game;
+	Graphics::Screen *_screen;
+	InventoryMap _inventoryItems;
+	Common::Array<Graphics::Surface> _inventorySurfaces;
+	bool _inventoryDirty;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
index 4a46548..b6561b2 100644
--- a/engines/mutationofjb/inventory.cpp
+++ b/engines/mutationofjb/inventory.cpp
@@ -21,13 +21,13 @@
  */
 
 #include "mutationofjb/inventory.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gui.h"
 #include "common/algorithm.h"
 #include "common/debug.h"
 
 namespace MutationOfJB {
 
-static const uint VISIBLE_ITEMS = 6;
-
 const Inventory::Items &Inventory::getItems() const {
 	return _items;
 }
@@ -43,6 +43,9 @@ void Inventory::addItem(const Common::String &item) {
 	if (_items.size() > VISIBLE_ITEMS) {
 		rotateItemsRight(VISIBLE_ITEMS);
 	}
+	if (_observer) {
+		_observer->onInventoryChanged();
+	}
 }
 
 void Inventory::removeItem(const Common::String &item) {
@@ -53,10 +56,16 @@ void Inventory::removeItem(const Common::String &item) {
 	}
 
 	_items.remove_at(it - _items.begin());
+	if (_observer) {
+		_observer->onInventoryChanged();
+	}
 }
 
 void Inventory::removeAllItems() {
 	_items.clear();
+	if (_observer) {
+		_observer->onInventoryChanged();
+	}
 }
 
 void Inventory::rotateItemsRight(uint n) {
@@ -68,6 +77,9 @@ void Inventory::rotateItemsRight(uint n) {
 	reverseItems(0, _items.size() - 1);
 	reverseItems(0, n - 1);
 	reverseItems(n, _items.size() - 1);
+	if (_observer) {
+		_observer->onInventoryChanged();
+	}
 }
 
 void Inventory::rotateItemsLeft(uint n) {
@@ -79,6 +91,13 @@ void Inventory::rotateItemsLeft(uint n) {
 	reverseItems(0, _items.size() - 1);
 	reverseItems(_items.size() - n, _items.size() - 1);
 	reverseItems(0, _items.size() - n - 1);
+	if (_observer) {
+		_observer->onInventoryChanged();
+	}
+}
+
+void Inventory::setObserver(InventoryObserver *observer) {
+	_observer = observer;
 }
 
 void Inventory::reverseItems(uint from, uint to) {
diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h
index fe7ca67..91c2932 100644
--- a/engines/mutationofjb/inventory.h
+++ b/engines/mutationofjb/inventory.h
@@ -29,8 +29,20 @@
 
 namespace MutationOfJB {
 
+class Game;
+
+class InventoryObserver {
+public:
+	virtual void onInventoryChanged() = 0;
+	virtual ~InventoryObserver() {}
+};
+
 class Inventory {
 public:
+	enum {
+		VISIBLE_ITEMS = 6
+	};
+
 	typedef Common::Array<Common::String> Items;
 
 	const Items &getItems() const;
@@ -42,10 +54,13 @@ public:
 	void rotateItemsRight(uint n);
 	void rotateItemsLeft(uint n);
 
+	void setObserver(InventoryObserver *observer);
+
 private:
 	void reverseItems(uint from, uint to);
 
 	Items _items;
+	InventoryObserver *_observer;
 };
 
 }
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index b796698..729ea44 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS := \
 	encryptedfile.o \
 	game.o \
 	gamedata.o \
+	gui.o \
 	inventory.o \
 	mutationofjb.o \
 	room.o \
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 5ed6ca3..62af983 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -47,7 +47,11 @@ void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE])
 
 void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
 	if (frameNo == 0) {
-		_room._screen->blitFrom(surface);
+		Common::Rect rect(0, 0, 320, 139);
+		if (_room._game->isCurrentSceneMap()) {
+			rect = Common::Rect(0, 0, 320, 200);
+		}
+		_room._screen->blitFrom(surface, rect, Common::Point(0, 0));
 	}
 
 	const int frameNo1 = frameNo + 1;


Commit: c25ed8957228cfd580216383c3391ccc7e512bb5
    https://github.com/scummvm/scummvm/commit/c25ed8957228cfd580216383c3391ccc7e512bb5
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Refactor inventory UI into separate widget, add button widgets.

Changed paths:
  A engines/mutationofjb/widgets/buttonwidget.cpp
  A engines/mutationofjb/widgets/buttonwidget.h
  A engines/mutationofjb/widgets/inventorywidget.cpp
  A engines/mutationofjb/widgets/inventorywidget.h
  A engines/mutationofjb/widgets/widget.cpp
  A engines/mutationofjb/widgets/widget.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/gui.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/room.h


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 492a424..cc6a455 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -31,6 +31,7 @@
 	("#L " | "-L ") <object>
 	("#W " | "-W ") <object>
 	("#T " | "-T ") <object>
+	("#P " | "-P ") <object1>
 	("#U " | "-U ") <object1> [<object2>]
 	("#ELSE" | "-ELSE") [<tag>]
 	"#MACRO " <name>
@@ -73,6 +74,10 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 		ActionInfo ai = {ActionInfo::Talk, line.c_str() + 3, "", firstChar == '#', nullptr};
 		parseCtx._actionInfos.push_back(ai);
 		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
+	} else if (line.size() >= 4 && (line.hasPrefix("#P ") || line.hasPrefix("-P "))) {
+		ActionInfo ai = {ActionInfo::PickUp, line.c_str() + 3, "", firstChar == '#', nullptr};
+		parseCtx._actionInfos.push_back(ai);
+		_pendingActionInfos.push_back(parseCtx._actionInfos.size() - 1);
 	} else if (line.size() >= 4 && (line.hasPrefix("#U ") || line.hasPrefix("-U "))) {
 		int secondObjPos = -1;
 		for (uint i = 3; i < line.size(); ++i) {
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index b7f1893..af0b76b 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -35,7 +35,12 @@
 namespace MutationOfJB {
 
 Game::Game(MutationOfJBEngine *vm)
-: _vm(vm), _delayedLocalScript(nullptr), _gui(*this, _vm->getScreen()), _scriptExecCtx(*this) {
+	: _vm(vm),
+	_delayedLocalScript(nullptr),
+	_gui(*this, _vm->getScreen()),
+	_scriptExecCtx(*this),
+	_currentAction(ActionInfo::Walk) {
+
 	_gameData = new GameData;
 	loadGameData(false);
 
@@ -87,7 +92,7 @@ bool Game::loadGameData(bool partB) {
 
 Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
 	if (isCurrentSceneMap()) {
-		_gui.markInventoryDirty();
+		_gui.markDirty();
 	}
 
 	_gameData->_lastScene = _gameData->_currentScene;
@@ -179,4 +184,12 @@ Gui &Game::getGui() {
 	return _gui;
 }
 
+ActionInfo::Action Game::getCurrentAction() const {
+	return _currentAction;
+}
+
+void Game::setCurrentAction(ActionInfo::Action action) {
+	_currentAction = action;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 71dadf2..19cf406 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -61,6 +61,9 @@ public:
 
 	Gui &getGui();
 
+	ActionInfo::Action getCurrentAction() const;
+	void setCurrentAction(ActionInfo::Action);
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
@@ -75,6 +78,7 @@ private:
 	Script *_delayedLocalScript;
 	Room *_room;
 	Gui _gui;
+	ActionInfo::Action _currentAction;
 
 	ScriptExecutionContext _scriptExecCtx;
 };
diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index 5ceed67..76e9305 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -27,20 +27,23 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/inventory.h"
 #include "mutationofjb/util.h"
+#include "mutationofjb/widgets/widget.h"
+#include "mutationofjb/widgets/inventorywidget.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
 
 namespace MutationOfJB {
 
 enum ButtonType {
-	BUTTON_WALK,
+	BUTTON_WALK = 0,
 	BUTTON_TALK,
 	BUTTON_LOOK,
 	BUTTON_USE,
 	BUTTON_PICKUP,
 	BUTTON_SCROLL_LEFT,
 	BUTTON_SCROLL_RIGHT,
-	BUTTON_SETTINGS
+	BUTTON_SETTINGS,
+	NUM_BUTTONS
 };
 
 enum {
@@ -52,40 +55,78 @@ enum {
 	INVENTORY_ITEMS_LINES = 5
 };
 
-static Common::Rect ButtonRects[] = {
-	Common::Rect(0, 148, 67, 158), // Walk
-	Common::Rect(0, 158, 67, 168), // Talk
-	Common::Rect(0, 168, 67, 178), // Look
-	Common::Rect(0, 178, 67, 188), // Use
-	Common::Rect(0, 188, 67, 198), // PickUp
-	Common::Rect(67, 149, 88, 174), // ScrollLeft
-	Common::Rect(67, 174, 88, 199), // ScrollRight
-	Common::Rect(301, 148, 320, 200) // Settings
-};
 
 Gui::Gui(Game &game, Graphics::Screen *screen)
 	: _game(game),
-	_screen(screen),
-	_inventoryDirty(false) {
+	_screen(screen) {}
+
+Gui::~Gui() {
+	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
+		delete *it;
+	}
+}
+
+Game &Gui::getGame() {
+	return _game;
 }
 
 bool Gui::init() {
-	const bool result1 = loadInventoryList();
-	const bool result2 = loadInventoryGfx();
+	if (!loadInventoryList()) {
+		return false;
+	}
+
+	if (!loadInventoryGfx()) {
+		return false;
+	}
+
+	if (!loadHudGfx()) {
+		return false;
+	}
 
 	_game.getGameData().getInventory().setObserver(this);
 
-	return result1 && result2;
+	// Init widgets.
+	_inventoryWidget = new InventoryWidget(*this, _inventoryItems, _inventorySurfaces);
+	_widgets.push_back(_inventoryWidget);
+
+	const Common::Rect ButtonRects[] = {
+		Common::Rect(0, 148, 67, 158), // Walk
+		Common::Rect(0, 158, 67, 168), // Talk
+		Common::Rect(0, 168, 67, 178), // Look
+		Common::Rect(0, 178, 67, 188), // Use
+		Common::Rect(0, 188, 67, 198), // PickUp
+		Common::Rect(67, 149, 88, 174), // ScrollLeft
+		Common::Rect(67, 174, 88, 199), // ScrollRight
+		Common::Rect(301, 148, 320, 200) // Settings
+	};
+
+	for (int i = 0; i < NUM_BUTTONS; ++i) {
+		const Graphics::Surface normalSurface = _hudSurfaces[0].getSubArea(ButtonRects[i]);
+		const Graphics::Surface pressedSurface = _hudSurfaces[1].getSubArea(ButtonRects[i]);
+		ButtonWidget *button = new ButtonWidget(*this, ButtonRects[i], normalSurface, pressedSurface);
+		button->setId(i);
+		button->setCallback(this);
+		_widgets.push_back(button);
+	}
+
+	return true;
 }
 
-void Gui::markInventoryDirty() {
-	_inventoryDirty = true;
+void Gui::markDirty() {
+	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
+		(*it)->markDirty();
+	}
+}
+
+void Gui::handleEvent(const Common::Event &event) {
+	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
+		(*it)->handleEvent(event);
+	}
 }
 
 void Gui::update() {
-	if (_inventoryDirty) {
-		drawInventory();
-		_inventoryDirty = false;
+	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
+		(*it)->update(*_screen);
 	}
 }
 
@@ -116,6 +157,32 @@ bool Gui::loadInventoryGfx() {
 	return decoder.decode(&callback);
 }
 
+class HudAnimationDecoderCallback : public AnimationDecoderCallback {
+public:
+	HudAnimationDecoderCallback(Gui &gui) : _gui(gui) {}
+	virtual void onFrame(int frameNo, Graphics::Surface &surface) override;
+	virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) override;
+private:
+	Gui &_gui;
+};
+
+void HudAnimationDecoderCallback::onPaletteUpdated(byte [PALETTE_SIZE]) {
+}
+
+void HudAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
+	if (frameNo == 0 || frameNo == 1 || frameNo == 3) {
+		Graphics::Surface outSurface;
+		outSurface.copyFrom(surface);
+		_gui._hudSurfaces.push_back(outSurface);
+	}
+}
+
+bool Gui::loadHudGfx() {
+	AnimationDecoder decoder("room0.dat");
+	HudAnimationDecoderCallback callback(*this);
+	return decoder.decode(&callback);
+}
+
 bool Gui::loadInventoryList() {
 	EncryptedFile file;
 	const char *fileName = "fixitems.dat";
@@ -147,35 +214,16 @@ bool Gui::loadInventoryList() {
 	return true;
 }
 
-void Gui::drawInventoryItem(const Common::String &item, int pos) {
-	InventoryMap::iterator it = _inventoryItems.find(item);
-	if (it == _inventoryItems.end()) {
-		return;
-	}
-
-	const int index = it->_value;
-	const int surfaceNo = index / (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
-	const int indexInSurface = index % (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
-	const int itemX = indexInSurface % INVENTORY_ITEMS_PER_LINE;
-	const int itemY = indexInSurface / INVENTORY_ITEMS_PER_LINE;
-
-	Common::Point destStartPos(INVENTORY_START_X + pos * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y);
-	Common::Rect sourceRect(itemX * INVENTORY_ITEM_WIDTH, itemY * INVENTORY_ITEM_HEIGHT, (itemX + 1) * INVENTORY_ITEM_WIDTH, (itemY + 1) * INVENTORY_ITEM_HEIGHT);
-	_screen->blitFrom(_inventorySurfaces[surfaceNo], sourceRect, destStartPos);
+void Gui::onInventoryChanged() {
+	_inventoryWidget->markDirty();
 }
 
-void Gui::drawInventory() {
-	Inventory &inventory = _game.getGameData().getInventory();
-	const Inventory::Items &items = inventory.getItems();
-	Common::Rect fullRect(INVENTORY_START_X, INVENTORY_START_Y, INVENTORY_START_X + Inventory::VISIBLE_ITEMS * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y + INVENTORY_ITEM_HEIGHT);
-	_screen->fillRect(fullRect, 0x00);
-	for (int i = 0; i < MIN((int) items.size(), (int) Inventory::VISIBLE_ITEMS); ++i) {
-		drawInventoryItem(items[i], i);
+void Gui::onButtonClicked(ButtonWidget *button) {
+	const int buttonId = button->getId();
+	if (buttonId <= BUTTON_PICKUP) {
+		const ActionInfo::Action actions[] = {ActionInfo::Walk, ActionInfo::Talk, ActionInfo::Look, ActionInfo::Use, ActionInfo::PickUp};
+		_game.setCurrentAction(actions[buttonId]);
 	}
 }
 
-void Gui::onInventoryChanged() {
-	markInventoryDirty();
-}
-
 }
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
index 27e0b0c..5d8358b 100644
--- a/engines/mutationofjb/gui.h
+++ b/engines/mutationofjb/gui.h
@@ -23,10 +23,16 @@
 #ifndef MUTATIONOFJB_GUI_H
 #define MUTATIONOFJB_GUI_H
 
+#include "common/array.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 #include "graphics/surface.h"
 #include "mutationofjb/inventory.h"
+#include "mutationofjb/widgets/buttonwidget.h"
+
+namespace Common {
+class Event;
+}
 
 namespace Graphics {
 class Screen;
@@ -35,31 +41,44 @@ class Screen;
 namespace MutationOfJB {
 
 class Game;
+class Widget;
+class InventoryWidget;
 
-class Gui : public InventoryObserver {
+class Gui : public InventoryObserver, public ButtonWidgetCallback {
 public:
+	typedef Common::HashMap<Common::String, int> InventoryMap;
+
 	friend class InventoryAnimationDecoderCallback;
+	friend class HudAnimationDecoderCallback;
+
 	Gui(Game &game, Graphics::Screen *screen);
+	~Gui();
+	Game &getGame();
+
 	bool init();
 	void update();
 
-	void markInventoryDirty();
+	void markDirty();
+	void handleEvent(const Common::Event &event);
 
 	virtual void onInventoryChanged() override;
+	virtual void onButtonClicked(ButtonWidget *) override;
 
 private:
 	bool loadInventoryGfx();
+	bool loadHudGfx();
 	bool loadInventoryList();
 	void drawInventoryItem(const Common::String &item, int pos);
 	void drawInventory();
 
-	typedef Common::HashMap<Common::String, int> InventoryMap;
-
 	Game &_game;
 	Graphics::Screen *_screen;
 	InventoryMap _inventoryItems;
 	Common::Array<Graphics::Surface> _inventorySurfaces;
-	bool _inventoryDirty;
+	Common::Array<Graphics::Surface> _hudSurfaces;
+
+	InventoryWidget *_inventoryWidget;
+	Common::Array<Widget *> _widgets;
 };
 
 }
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 729ea44..2352ae9 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -18,6 +18,9 @@ MODULE_OBJS := \
 	commands/removeitemcommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
+	widgets/buttonwidget.o \
+	widgets/inventorywidget.o \
+	widgets/widget.o \
 	animationdecoder.o \
 	debug.o \
 	detection.o \
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index df8ad11..e7f534e 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -43,7 +43,6 @@ MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 : Engine(syst),
  _console(nullptr),
  _screen(nullptr),
- _currentAction(ActionInfo::Walk),
  _mapObjectId(0) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
@@ -82,12 +81,12 @@ void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
 		const int16 y = event.mouse.y;
 
 		if (Door *const door = scene->findDoor(x, y)) {
-			if (!_game->startActionSection(_currentAction, door->_name) && _currentAction == ActionInfo::Walk && door->_destSceneId != 0) {
+			if (!_game->startActionSection(_game->getCurrentAction(), door->_name) && _game->getCurrentAction() == ActionInfo::Walk && door->_destSceneId != 0) {
 				_game->changeScene(door->_destSceneId, _game->getGameData()._partB);
 			}
 		} else if (Static *const stat = scene->findStatic(x, y)) {
 			if (stat->_active == 1) {
-				_game->startActionSection(_currentAction, stat->_name);
+				_game->startActionSection(_game->getCurrentAction(), stat->_name);
 			}
 		}
 		break;
@@ -95,6 +94,7 @@ void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
 	default:
 		break;
 	}
+	_game->getGui().handleEvent(event);
 }
 
 /*
@@ -187,19 +187,19 @@ Common::Error MutationOfJBEngine::run() {
 			{
 				switch (event.kbd.ascii) {
 				case 'g':
-					_currentAction = ActionInfo::Walk;
+					_game->setCurrentAction(ActionInfo::Walk);
 					break;
 				case 'r':
-					_currentAction = ActionInfo::Talk;
+					_game->setCurrentAction(ActionInfo::Talk);
 					break;
 				case 's':
-					_currentAction = ActionInfo::Look;
+					_game->setCurrentAction(ActionInfo::Look);
 					break;
 				case 'b':
-					_currentAction = ActionInfo::Use;
+					_game->setCurrentAction(ActionInfo::Use);
 					break;
 				case 'n':
-					_currentAction = ActionInfo::PickUp;
+					_game->setCurrentAction(ActionInfo::PickUp);
 					break;
 				}
 			}
diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h
index a2be2fc..e57d2eb 100644
--- a/engines/mutationofjb/room.h
+++ b/engines/mutationofjb/room.h
@@ -39,6 +39,7 @@ class Game;
 class Room {
 public:
 	friend class RoomAnimationDecoderCallback;
+	friend class GuiAnimationDecoderCallback;
 
 	Room(Game *game, Graphics::Screen *screen);
 	bool load(uint8 roomNumber, bool roomB);
diff --git a/engines/mutationofjb/widgets/buttonwidget.cpp b/engines/mutationofjb/widgets/buttonwidget.cpp
new file mode 100644
index 0000000..9f9a401
--- /dev/null
+++ b/engines/mutationofjb/widgets/buttonwidget.cpp
@@ -0,0 +1,70 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/widgets/buttonwidget.h"
+#include "common/events.h"
+#include "graphics/managed_surface.h"
+
+namespace MutationOfJB {
+
+ButtonWidget::ButtonWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &normalSurface, const Graphics::Surface &pressedSurface) :
+	Widget(gui, area),
+	_normalSurface(normalSurface),
+	_pressedSurface(pressedSurface),
+	_callback(nullptr),
+	_pressed(false) {}
+
+void ButtonWidget::setCallback(ButtonWidgetCallback *callback) {
+	_callback = callback;
+}
+
+void ButtonWidget::handleEvent(const Common::Event &event) {
+	switch(event.type) {
+	case Common::EVENT_LBUTTONDOWN:
+	{
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+		if (_area.contains(x, y)) {
+			_pressed = true;
+			markDirty();
+		}
+		break;
+	}
+	case Common::EVENT_LBUTTONUP:
+	{
+		if (_pressed) {
+			_pressed = false;
+			markDirty();
+			if (_callback) {
+				_callback->onButtonClicked(this);
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+void ButtonWidget::_draw(Graphics::ManagedSurface &surface) {
+	surface.blitFrom(_pressed ? _pressedSurface : _normalSurface, Common::Point(_area.left, _area.top));
+}
+}
diff --git a/engines/mutationofjb/widgets/buttonwidget.h b/engines/mutationofjb/widgets/buttonwidget.h
new file mode 100644
index 0000000..de8cb63
--- /dev/null
+++ b/engines/mutationofjb/widgets/buttonwidget.h
@@ -0,0 +1,58 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_BUTTONWIDGET_H
+#define MUTATIONOFJB_BUTTONWIDGET_H
+
+#include "mutationofjb/widgets/widget.h"
+#include "graphics/surface.h"
+
+namespace MutationOfJB {
+
+class ButtonWidget;
+
+class ButtonWidgetCallback {
+public:
+	virtual ~ButtonWidgetCallback() {}
+	virtual void onButtonClicked(ButtonWidget *) = 0;
+};
+
+class ButtonWidget : public Widget {
+public:
+	ButtonWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &normalSurface, const Graphics::Surface &pressedSurface);
+	void setCallback(ButtonWidgetCallback *callback);
+
+	virtual void handleEvent(const Common::Event &event) override;
+
+protected:
+	virtual void _draw(Graphics::ManagedSurface &) override;
+
+private:
+	Graphics::Surface _normalSurface;
+	Graphics::Surface _pressedSurface;
+	ButtonWidgetCallback *_callback;
+	bool _pressed;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/widgets/inventorywidget.cpp b/engines/mutationofjb/widgets/inventorywidget.cpp
new file mode 100644
index 0000000..d78ef10
--- /dev/null
+++ b/engines/mutationofjb/widgets/inventorywidget.cpp
@@ -0,0 +1,75 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/widgets/inventorywidget.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/gui.h"
+#include "mutationofjb/inventory.h"
+#include "common/str.h"
+#include "common/rect.h"
+#include "common/util.h"
+#include "graphics/managed_surface.h"
+
+namespace MutationOfJB {
+
+enum {
+	INVENTORY_START_X = 88,
+	INVENTORY_START_Y = 149,
+	INVENTORY_ITEM_WIDTH = 34,
+	INVENTORY_ITEM_HEIGHT = 33,
+	INVENTORY_ITEMS_PER_LINE = 8,
+	INVENTORY_ITEMS_LINES = 5
+};
+
+InventoryWidget::InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface>& inventorySurfaces) :
+	Widget(gui, Common::Rect(INVENTORY_START_X, INVENTORY_START_Y, INVENTORY_START_X + Inventory::VISIBLE_ITEMS * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y + INVENTORY_ITEM_HEIGHT)),
+	_inventoryMap(inventoryMap),
+	_surfaces(inventorySurfaces) {}
+
+void InventoryWidget::drawInventoryItem(Graphics::ManagedSurface &surface, const Common::String &item, int pos) {
+	Gui::InventoryMap::iterator it = _inventoryMap.find(item);
+	if (it == _inventoryMap.end()) {
+		return;
+	}
+
+	const int index = it->_value;
+	const int surfaceNo = index / (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
+	const int indexInSurface = index % (INVENTORY_ITEMS_LINES * INVENTORY_ITEMS_PER_LINE);
+	const int itemX = indexInSurface % INVENTORY_ITEMS_PER_LINE;
+	const int itemY = indexInSurface / INVENTORY_ITEMS_PER_LINE;
+
+	Common::Point destStartPos(INVENTORY_START_X + pos * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y);
+	Common::Rect sourceRect(itemX * INVENTORY_ITEM_WIDTH, itemY * INVENTORY_ITEM_HEIGHT, (itemX + 1) * INVENTORY_ITEM_WIDTH, (itemY + 1) * INVENTORY_ITEM_HEIGHT);
+	surface.blitFrom(_surfaces[surfaceNo], sourceRect, destStartPos);
+}
+
+void InventoryWidget::_draw(Graphics::ManagedSurface &surface) {
+	Inventory &inventory = _gui.getGame().getGameData().getInventory();
+	const Inventory::Items &items = inventory.getItems();
+	surface.fillRect(_area, 0x00);
+	for (int i = 0; i < MIN((int) items.size(), (int) Inventory::VISIBLE_ITEMS); ++i) {
+		drawInventoryItem(surface, items[i], i);
+	}
+}
+
+}
diff --git a/engines/mutationofjb/widgets/inventorywidget.h b/engines/mutationofjb/widgets/inventorywidget.h
new file mode 100644
index 0000000..f0bc4ba
--- /dev/null
+++ b/engines/mutationofjb/widgets/inventorywidget.h
@@ -0,0 +1,48 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_INVENTORYWIDGET_H
+#define MUTATIONOFJB_INVENTORYWIDGET_H
+
+#include "mutationofjb/widgets/widget.h"
+#include "mutationofjb/gui.h"
+
+namespace Common {
+class String;
+}
+
+namespace MutationOfJB {
+
+class InventoryWidget : public Widget {
+public:
+	InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface>& inventorySurfaces);
+	virtual void _draw(Graphics::ManagedSurface &) override;
+
+private:
+	void drawInventoryItem(Graphics::ManagedSurface &surface, const Common::String &item, int pos);
+	Gui::InventoryMap &_inventoryMap;
+	const Common::Array<Graphics::Surface>& _surfaces;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/widgets/widget.cpp b/engines/mutationofjb/widgets/widget.cpp
new file mode 100644
index 0000000..fea7f6f
--- /dev/null
+++ b/engines/mutationofjb/widgets/widget.cpp
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/widgets/widget.h"
+
+namespace MutationOfJB {
+
+int Widget::getId() const {
+	return _id;
+}
+
+void Widget::setId(int id) {
+	_id = id;
+}
+
+void Widget::markDirty() {
+	_dirty = true;
+}
+
+bool Widget::isDirty() const {
+	return _dirty;
+}
+
+void Widget::update(Graphics::ManagedSurface &surface) {
+	if (_dirty) {
+		_draw(surface);
+		_dirty = false;
+	}
+}
+
+}
diff --git a/engines/mutationofjb/widgets/widget.h b/engines/mutationofjb/widgets/widget.h
new file mode 100644
index 0000000..f81d466
--- /dev/null
+++ b/engines/mutationofjb/widgets/widget.h
@@ -0,0 +1,66 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_WIDGET_H
+#define MUTATIONOFJB_WIDGET_H
+
+#include <common/scummsys.h>
+#include <common/rect.h>
+
+namespace Common {
+class Event;
+}
+
+namespace Graphics {
+class ManagedSurface;
+}
+
+namespace MutationOfJB {
+
+class Gui;
+
+class Widget {
+public:
+	Widget(Gui &gui, const Common::Rect &area) : _gui(gui), _area(area), _id(0), _dirty(true) {}
+	virtual ~Widget() {}
+
+	int getId() const;
+	void setId(int id);
+
+	bool isDirty() const;
+	void markDirty();
+	void update(Graphics::ManagedSurface &);
+
+	virtual void handleEvent(const Common::Event &) {}
+protected:
+	virtual void _draw(Graphics::ManagedSurface &) = 0;
+
+	Gui &_gui;
+	Common::Rect _area;
+	int _id;
+	bool _dirty;
+};
+
+}
+
+#endif
+


Commit: 29a809d691c77b549bea615421d9e840f3773142
    https://github.com/scummvm/scummvm/commit/29a809d691c77b549bea615421d9e840f3773142
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add rename command.

Changed paths:
  A engines/mutationofjb/commands/renamecommand.cpp
  A engines/mutationofjb/commands/renamecommand.h
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/gotocommand.h
    engines/mutationofjb/commands/saycommand.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/inventory.cpp
    engines/mutationofjb/inventory.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index f2639b6..a2d4526 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -66,7 +66,7 @@ bool ChangeCommandParser::parseValueString(const Common::String &valueString, bo
 	if (valueString.hasPrefix("NM")) {
 		reg = ChangeCommand::NM;
 		op = ChangeCommand::SetValue;
-		strncpy(ccv._strVal, val, MAX_STR_LENGTH);
+		strncpy(ccv._strVal, val, MAX_ENTITY_NAME_LENGTH);
 	} else if (valueString.hasPrefix("LT")) {
 		reg = ChangeCommand::LT;
 		ccv._byteVal = parseInteger(val, op);
@@ -324,7 +324,7 @@ Command::ExecuteResult ChangeDoorCommand::execute(ScriptExecutionContext &script
 
 	switch (_register) {
 	case NM:
-		strncpy(door->_name, _value._strVal, MAX_STR_LENGTH);
+		strncpy(door->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH);
 		break;
 	case LT:
 		door->_destSceneId = _value._byteVal;
@@ -447,7 +447,7 @@ Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scri
 		stat->_active = _value._byteVal;
 		break;
 	case NM:
-		strncpy(stat->_name, _value._strVal, MAX_STR_LENGTH);
+		strncpy(stat->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH);
 		break;
 	case XX:
 		stat->_x = _value._wordVal;
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index f5d7cf5..6fa090e 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -28,7 +28,7 @@ namespace MutationOfJB {
 union ChangeCommandValue {
 	uint8 _byteVal;
 	uint16 _wordVal;
-	char _strVal[MAX_STR_LENGTH + 1];
+	char _strVal[MAX_ENTITY_NAME_LENGTH + 1];
 };
 
 class ChangeCommand : public SeqCommand {
diff --git a/engines/mutationofjb/commands/gotocommand.h b/engines/mutationofjb/commands/gotocommand.h
index 09d426f..b56e642 100644
--- a/engines/mutationofjb/commands/gotocommand.h
+++ b/engines/mutationofjb/commands/gotocommand.h
@@ -34,7 +34,7 @@ class GotoCommandParser : public CommandParser {
 public:
 	GotoCommandParser() {}
 
-	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 };
 
 class GotoCommand : public Command {
diff --git a/engines/mutationofjb/commands/renamecommand.cpp b/engines/mutationofjb/commands/renamecommand.cpp
new file mode 100644
index 0000000..03a8832
--- /dev/null
+++ b/engines/mutationofjb/commands/renamecommand.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/renamecommand.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "common/algorithm.h"
+
+/*
+	"REN " <oldName> " " <newName>
+	Renames every door, static (in the current scene) and inventory item
+	with the name oldName to newName.
+*/
+
+namespace MutationOfJB {
+
+bool RenameCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 7 || !line.hasPrefix("REN")) {
+		return false;
+	}
+
+	Common::String::const_iterator sep = Common::find(line.begin() + 4, line.end(), ' ');
+	if (sep == line.end() || sep + 1 == line.end()) {
+		return false;
+	}
+
+	const Common::String oldName(line.begin() + 4, sep);
+	const Common::String newName(sep + 1, line.end());
+	command = new RenameCommand(oldName, newName);
+
+	return true;
+}
+
+
+Command::ExecuteResult RenameCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Scene *const scene = scriptExecCtx.getGameData().getCurrentScene();
+
+	for (int i = 1; i <= scene->getNoDoors(); ++i) {
+		Door *const door = scene->getDoor(i);
+		if (strcmp(door->_name, _oldName.c_str()) == 0) {
+			strncpy(door->_name, _newName.c_str(), MAX_ENTITY_NAME_LENGTH);
+		}
+	}
+	for (int i = 1; i <= scene->getNoStatics(); ++i) {
+		Static *const stat = scene->getStatic(i);
+		if (strcmp(stat->_name, _oldName.c_str()) == 0) {
+			strncpy(stat->_name, _newName.c_str(), MAX_ENTITY_NAME_LENGTH);
+		}
+	}
+
+	scriptExecCtx.getGameData().getInventory().renameItem(_oldName, _newName);
+	return Finished;
+}
+
+Common::String RenameCommand::debugString() const {
+	return Common::String::format("RENAME '%s' '%s'", _oldName.c_str(), _newName.c_str());
+}
+
+}
diff --git a/engines/mutationofjb/commands/renamecommand.h b/engines/mutationofjb/commands/renamecommand.h
new file mode 100644
index 0000000..566d46e
--- /dev/null
+++ b/engines/mutationofjb/commands/renamecommand.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_RENAMECOMMAND_H
+#define MUTATIONOFJB_RENAMECOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class RenameCommandParser : public SeqCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class RenameCommand : public SeqCommand {
+public:
+	RenameCommand(const Common::String &oldName, const Common::String &newName) : _oldName(oldName), _newName(newName) {}
+
+	virtual Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Common::String debugString() const override;
+
+private:
+	Common::String _oldName;
+	Common::String _newName;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/saycommand.h b/engines/mutationofjb/commands/saycommand.h
index e41d10f..60c7cc8 100644
--- a/engines/mutationofjb/commands/saycommand.h
+++ b/engines/mutationofjb/commands/saycommand.h
@@ -32,7 +32,7 @@ class SayCommandParser : public SeqCommandParser {
 public:
 	SayCommandParser() {}
 
-	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 };
 
 class SayCommand : public SeqCommand {
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index 099cea7..1ece44c 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -28,13 +28,13 @@
 namespace MutationOfJB {
 
 static bool readString(Common::ReadStream &stream, char *str) {
-	char buf[MAX_STR_LENGTH];
-	memset(str, 0, MAX_STR_LENGTH + 1);
+	char buf[MAX_ENTITY_NAME_LENGTH];
+	memset(str, 0, MAX_ENTITY_NAME_LENGTH + 1);
 
 	uint8 len = stream.readByte();
-	stream.read(buf, MAX_STR_LENGTH);
+	stream.read(buf, MAX_ENTITY_NAME_LENGTH);
 
-	len = MIN(len, MAX_STR_LENGTH);
+	len = MIN(len, (uint8) MAX_ENTITY_NAME_LENGTH);
 	memcpy(str, buf, len);
 
 	return true;
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 64de01e..321a688 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -32,7 +32,9 @@ class ReadStream;
 
 namespace MutationOfJB {
 
-static const uint8 MAX_STR_LENGTH = 0x14;
+enum {
+	MAX_ENTITY_NAME_LENGTH = 0x14
+};
 
 /*
 	There are 4 types of entities present in the game data:
@@ -47,7 +49,7 @@ struct Door {
 		Door name.
 		Can be empty - deactivates door completely.
 	*/
-	char _name[MAX_STR_LENGTH + 1];
+	char _name[MAX_ENTITY_NAME_LENGTH + 1];
 	/*
 		Scene ID where the door leads.
 		Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
@@ -96,7 +98,7 @@ struct Object {
 
 struct Static {
 	uint8  _active;
-	char _name[MAX_STR_LENGTH + 1];
+	char _name[MAX_ENTITY_NAME_LENGTH + 1];
 	uint16 _x;
 	uint8  _y;
 	uint16 _width;
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
index b6561b2..2fa7f2c 100644
--- a/engines/mutationofjb/inventory.cpp
+++ b/engines/mutationofjb/inventory.cpp
@@ -33,7 +33,7 @@ const Inventory::Items &Inventory::getItems() const {
 }
 
 bool Inventory::hasItem(const Common::String &item) const {
-	Items::const_iterator it = find(_items.begin(), _items.end(), item);
+	Items::const_iterator it = Common::find(_items.begin(), _items.end(), item);
 	return (it != _items.end());
 }
 
@@ -49,7 +49,7 @@ void Inventory::addItem(const Common::String &item) {
 }
 
 void Inventory::removeItem(const Common::String &item) {
-	Items::iterator it = find(_items.begin(), _items.end(), item);
+	Items::iterator it = Common::find(_items.begin(), _items.end(), item);
 	if (it == _items.end()) {
 		debug("Item '%s' not in inventory.", item.c_str());
 		return;
@@ -68,6 +68,19 @@ void Inventory::removeAllItems() {
 	}
 }
 
+void Inventory::renameItem(const Common::String &oldName, const Common::String &newName) {
+	bool renamed = false;
+	for (Items::iterator it = _items.begin(); it != _items.end(); ++it) {
+		if (*it == oldName) {
+			*it = newName;
+			renamed = true;
+		}
+	}
+	if (renamed && _observer) {
+		_observer->onInventoryChanged();
+	}
+}
+
 void Inventory::rotateItemsRight(uint n) {
 	if (_items.size() < 2) {
 		return;
diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h
index 91c2932..c22422d 100644
--- a/engines/mutationofjb/inventory.h
+++ b/engines/mutationofjb/inventory.h
@@ -50,6 +50,7 @@ public:
 	void addItem(const Common::String &item);
 	void removeItem(const Common::String &item);
 	void removeAllItems();
+	void renameItem(const Common::String &oldName, const Common::String &newName);
 
 	void rotateItemsRight(uint n);
 	void rotateItemsLeft(uint n);
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 2352ae9..f66b671 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS := \
 	commands/newroomcommand.o \
 	commands/removeallitemscommand.o \
 	commands/removeitemcommand.o \
+	commands/renamecommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
 	widgets/buttonwidget.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index f213de9..1f5571f 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -42,6 +42,7 @@
 #include "mutationofjb/commands/camefromcommand.h"
 #include "mutationofjb/commands/callmacrocommand.h"
 #include "mutationofjb/commands/newroomcommand.h"
+#include "mutationofjb/commands/renamecommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -62,6 +63,7 @@ static CommandParser **getParsers() {
 		new AddItemCommandParser,
 		new RemoveItemCommandParser,
 		new RemoveAllItemsCommandParser,
+		new RenameCommandParser,
 		new NewRoomCommandParser,
 		new GotoCommandParser,
 		new LabelCommandParser,


Commit: 5290d9a74b2f704675cf5ae69bcf892b5afd274f
    https://github.com/scummvm/scummvm/commit/5290d9a74b2f704675cf5ae69bcf892b5afd274f
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Draw HUD background.

Changed paths:
  A engines/mutationofjb/widgets/imagewidget.cpp
  A engines/mutationofjb/widgets/imagewidget.h
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/module.mk
    engines/mutationofjb/mutationofjb.h


diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index 76e9305..c7be355 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -29,6 +29,7 @@
 #include "mutationofjb/util.h"
 #include "mutationofjb/widgets/widget.h"
 #include "mutationofjb/widgets/inventorywidget.h"
+#include "mutationofjb/widgets/imagewidget.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
 
@@ -52,7 +53,11 @@ enum {
 	INVENTORY_ITEM_WIDTH = 34,
 	INVENTORY_ITEM_HEIGHT = 33,
 	INVENTORY_ITEMS_PER_LINE = 8,
-	INVENTORY_ITEMS_LINES = 5
+	INVENTORY_ITEMS_LINES = 5,
+	CONVERSATION_X = 0,
+	CONVERSATION_Y = 139,
+	CONVERSATION_WIDTH = 320,
+	CONVERSATION_HEIGHT = 61
 };
 
 
@@ -86,6 +91,12 @@ bool Gui::init() {
 	_game.getGameData().getInventory().setObserver(this);
 
 	// Init widgets.
+
+	const Common::Rect backgroundRect(CONVERSATION_X, CONVERSATION_Y, CONVERSATION_X + CONVERSATION_WIDTH, CONVERSATION_Y + CONVERSATION_HEIGHT);
+	const Graphics::Surface backgroundSurface = _hudSurfaces[0].getSubArea(backgroundRect);
+	ImageWidget *image = new ImageWidget(*this, backgroundRect, backgroundSurface);
+	_widgets.push_back(image);
+
 	_inventoryWidget = new InventoryWidget(*this, _inventoryItems, _inventorySurfaces);
 	_widgets.push_back(_inventoryWidget);
 
@@ -170,7 +181,7 @@ void HudAnimationDecoderCallback::onPaletteUpdated(byte [PALETTE_SIZE]) {
 }
 
 void HudAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
-	if (frameNo == 0 || frameNo == 1 || frameNo == 3) {
+	if (frameNo == 0 || frameNo == 1 || frameNo == 4) {
 		Graphics::Surface outSurface;
 		outSurface.copyFrom(surface);
 		_gui._hudSurfaces.push_back(outSurface);
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index f66b671..8604d40 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS := \
 	commands/saycommand.o \
 	commands/seqcommand.o \
 	widgets/buttonwidget.o \
+	widgets/imagewidget.o \
 	widgets/inventorywidget.o \
 	widgets/widget.o \
 	animationdecoder.o \
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index ef0c973..348165f 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -57,7 +57,6 @@ private:
 	Console *_console;
 	Graphics::Screen *_screen;
 	Game *_game;
-	ActionInfo::Action _currentAction;
 	uint8 _mapObjectId;
 };
 
diff --git a/engines/mutationofjb/widgets/imagewidget.cpp b/engines/mutationofjb/widgets/imagewidget.cpp
new file mode 100644
index 0000000..3395da8
--- /dev/null
+++ b/engines/mutationofjb/widgets/imagewidget.cpp
@@ -0,0 +1,37 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/widgets/imagewidget.h"
+#include "graphics/managed_surface.h"
+
+namespace MutationOfJB {
+
+ImageWidget::ImageWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &image) :
+	Widget(gui, area),
+	_image(image) {}
+
+
+void ImageWidget::_draw(Graphics::ManagedSurface &surface) {
+	surface.blitFrom(_image, Common::Point(_area.left, _area.top));
+}
+
+}
diff --git a/engines/mutationofjb/widgets/imagewidget.h b/engines/mutationofjb/widgets/imagewidget.h
new file mode 100644
index 0000000..4585a72
--- /dev/null
+++ b/engines/mutationofjb/widgets/imagewidget.h
@@ -0,0 +1,44 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_IMAGEWIDGET_H
+#define MUTATIONOFJB_IMAGEWIDGET_H
+
+#include "mutationofjb/widgets/widget.h"
+#include "graphics/surface.h"
+
+namespace MutationOfJB {
+
+class ImageWidget : public Widget {
+public:
+	ImageWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &image);
+
+protected:
+	virtual void _draw(Graphics::ManagedSurface &surface) override;
+
+private:
+	Graphics::Surface _image;
+};
+
+}
+
+#endif


Commit: 61c106b3307ee2f8aaa579dbc5d7c8f8e62ae41a
    https://github.com/scummvm/scummvm/commit/61c106b3307ee2f8aaa579dbc5d7c8f8e62ae41a
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add font support and conversation widget.

Changed paths:
  A engines/mutationofjb/font.cpp
  A engines/mutationofjb/font.h
  A engines/mutationofjb/widgets/conversationwidget.cpp
  A engines/mutationofjb/widgets/conversationwidget.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/gui.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/widgets/widget.cpp
    engines/mutationofjb/widgets/widget.h


diff --git a/engines/mutationofjb/font.cpp b/engines/mutationofjb/font.cpp
new file mode 100644
index 0000000..235c60a
--- /dev/null
+++ b/engines/mutationofjb/font.cpp
@@ -0,0 +1,125 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/font.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/util.h"
+#include "common/debug.h"
+
+namespace MutationOfJB {
+
+Font::Font(const Common::String &fileName, int horizSpacing, int vertSpacing) :
+	_horizSpacing(horizSpacing),
+	_vertSpacing(vertSpacing) {
+
+	load(fileName);
+}
+
+bool Font::load(const Common::String &fileName) {
+	EncryptedFile file;
+	file.open(fileName);
+	if (!file.isOpen()) {
+		reportFileMissingError(fileName.c_str());
+		return false;
+	}
+
+	file.seek(0x02D6, SEEK_SET); // Skip header + unknown data (unused palette?).
+
+	uint16 noGlyphs = 0;
+	noGlyphs = file.readUint16LE();
+
+	file.seek(7, SEEK_CUR); // Skip unknown data (0s).
+
+	uint8 maxHeight = 0;
+
+	while (noGlyphs--) {
+		const uint8 character = file.readByte();
+		const uint8 width = file.readByte();
+		const uint8 height = file.readByte();
+
+		Graphics::ManagedSurface &surf = _glyphs[character];
+		surf.create(width, height);
+		for (int h = 0; h < height; ++h) {
+			file.read(surf.getBasePtr(0, h), width);
+		}
+
+		if (height > maxHeight) {
+			maxHeight = height;
+		}
+	}
+
+	if (_vertSpacing == -1) {
+		_vertSpacing = maxHeight;
+	}
+
+	return true;
+}
+
+
+void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) {
+	GlyphMap::iterator it = _glyphs.find(glyph);
+	if (it == _glyphs.end()) {
+		warning("Glyph %d not found", glyph);
+		return;
+	}
+
+	Graphics::ManagedSurface &glyphSurface = it->_value;
+
+	Graphics::ManagedSurface tmp(glyphSurface);
+	for (int h = 0; h < tmp.h; ++h) {
+		uint8 *ptr = reinterpret_cast<uint8 *>(tmp.getBasePtr(0, h));
+		for (int w = 0; w < tmp.w; ++w) {
+			if (*ptr != 0) {
+				*ptr = transformColor(baseColor, *ptr);
+			}
+			ptr++;
+		}
+	}
+	surf.transBlitFrom(tmp.rawSurface(), Common::Point(x, y));
+
+	x += glyphSurface.w + _horizSpacing;
+}
+
+void Font::drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) {
+	for (uint i = 0; i < str.size(); ++i) {
+		drawGlyph(str[i], baseColor, x, y, surf); // "x" is updated.
+	}
+}
+
+uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) {
+	return baseColor + glyphColor - 0x10;
+}
+
+SystemFont::SystemFont() : Font("sysfnt.aft", 1, 7) {}
+
+SpeechFont::SpeechFont() : Font("font1.aft", -1, -1) {}
+
+uint8 SpeechFont::transformColor(uint8 baseColor, uint8 glyphColor) {
+	// Hack in original game.
+	if (glyphColor == 0x11) {
+		return 0xC0;
+	}
+
+	return Font::transformColor(baseColor, glyphColor);
+}
+
+}
diff --git a/engines/mutationofjb/font.h b/engines/mutationofjb/font.h
new file mode 100644
index 0000000..5aa6a4f
--- /dev/null
+++ b/engines/mutationofjb/font.h
@@ -0,0 +1,70 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_FONT_H
+#define MUTATIONOFJB_FONT_H
+
+#include "common/scummsys.h"
+#include "common/hashmap.h"
+#include "graphics/managed_surface.h"
+
+namespace Common {
+class String;
+}
+
+namespace MutationOfJB {
+
+class Font {
+public:
+	Font(const Common::String &fileName, int horizSpacing, int vertSpacing);
+	virtual ~Font() {}
+	void drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf);
+
+protected:
+	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor);
+
+private:
+	void drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf);
+	bool load(const Common::String &fileName);
+
+	int _horizSpacing;
+	int _vertSpacing;
+	typedef Common::HashMap<uint8, Graphics::ManagedSurface> GlyphMap;
+	GlyphMap _glyphs;
+};
+
+class SystemFont : public Font {
+public:
+	SystemFont();
+};
+
+class SpeechFont : public Font {
+public:
+	SpeechFont();
+
+protected:
+	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) override;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index af0b76b..d6ab622 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -192,4 +192,12 @@ void Game::setCurrentAction(ActionInfo::Action action) {
 	_currentAction = action;
 }
 
+Font& Game::getSystemFont() {
+	return _systemFont;
+}
+
+Font& Game::getSpeechFont() {
+	return _speechFont;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 19cf406..122c2a1 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "mutationofjb/script.h"
+#include "mutationofjb/font.h"
 #include "mutationofjb/gui.h"
 
 namespace Common {
@@ -64,6 +65,9 @@ public:
 	ActionInfo::Action getCurrentAction() const;
 	void setCurrentAction(ActionInfo::Action);
 
+	Font& getSystemFont();
+	Font& getSpeechFont();
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
@@ -81,6 +85,9 @@ private:
 	ActionInfo::Action _currentAction;
 
 	ScriptExecutionContext _scriptExecCtx;
+
+	SystemFont _systemFont;
+	SpeechFont _speechFont;
 };
 
 }
diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index c7be355..7102407 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -30,6 +30,7 @@
 #include "mutationofjb/widgets/widget.h"
 #include "mutationofjb/widgets/inventorywidget.h"
 #include "mutationofjb/widgets/imagewidget.h"
+#include "mutationofjb/widgets/conversationwidget.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
 
@@ -120,6 +121,12 @@ bool Gui::init() {
 		_widgets.push_back(button);
 	}
 
+	const Common::Rect conversationRect(CONVERSATION_X, CONVERSATION_Y, CONVERSATION_X + CONVERSATION_WIDTH, CONVERSATION_Y + CONVERSATION_HEIGHT);
+	const Graphics::Surface conversationSurface =_hudSurfaces[2].getSubArea(conversationRect);
+	_conversationWidget = new ConversationWidget(*this, conversationRect, conversationSurface);
+	_conversationWidget->setVisible(false);
+	_widgets.push_back(_conversationWidget);
+
 	return true;
 }
 
@@ -141,6 +148,10 @@ void Gui::update() {
 	}
 }
 
+ConversationWidget& Gui::getConversationWidget() {
+	return *_conversationWidget;
+}
+
 class InventoryAnimationDecoderCallback : public AnimationDecoderCallback {
 public:
 	InventoryAnimationDecoderCallback(Gui &gui) : _gui(gui) {}
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
index 5d8358b..d8e1286 100644
--- a/engines/mutationofjb/gui.h
+++ b/engines/mutationofjb/gui.h
@@ -43,9 +43,21 @@ namespace MutationOfJB {
 class Game;
 class Widget;
 class InventoryWidget;
+class ConversationWidget;
 
 class Gui : public InventoryObserver, public ButtonWidgetCallback {
 public:
+	enum Colors {
+		WHITE = 0xC6,
+		DARKGRAY = 0xC2,
+		LIGHTGRAY = 0xC4,
+		GREEN = 0xC8,
+		ORANGE = 0xCA,
+		DARKBLUE = 0xD6,
+		LIGHTBLUE = 0xDA,
+		BROWN = 0xDC
+	};
+
 	typedef Common::HashMap<Common::String, int> InventoryMap;
 
 	friend class InventoryAnimationDecoderCallback;
@@ -64,6 +76,8 @@ public:
 	virtual void onInventoryChanged() override;
 	virtual void onButtonClicked(ButtonWidget *) override;
 
+	ConversationWidget& getConversationWidget();
+
 private:
 	bool loadInventoryGfx();
 	bool loadHudGfx();
@@ -78,6 +92,7 @@ private:
 	Common::Array<Graphics::Surface> _hudSurfaces;
 
 	InventoryWidget *_inventoryWidget;
+	ConversationWidget *_conversationWidget;
 	Common::Array<Widget *> _widgets;
 };
 
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 8604d40..2fd4123 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS := \
 	commands/saycommand.o \
 	commands/seqcommand.o \
 	widgets/buttonwidget.o \
+	widgets/conversationwidget.o \
 	widgets/imagewidget.o \
 	widgets/inventorywidget.o \
 	widgets/widget.o \
@@ -27,6 +28,7 @@ MODULE_OBJS := \
 	debug.o \
 	detection.o \
 	encryptedfile.o \
+	font.o \
 	game.o \
 	gamedata.o \
 	gui.o \
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
new file mode 100644
index 0000000..6196bc6
--- /dev/null
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -0,0 +1,65 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/widgets/conversationwidget.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gui.h"
+#include "mutationofjb/font.h"
+
+namespace MutationOfJB {
+
+enum {
+	CONVERSATION_LINES_X = 5,
+	CONVERSATION_LINES_Y = 151,
+	CONVERSATION_LINE_HEIGHT = 12
+};
+
+ConversationWidget::ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface) :
+	Widget(gui, area),
+	_surface(surface) {}
+
+
+void ConversationWidget::setLine(int lineNo, const Common::String &str) {
+	if (lineNo >= CONVERSATION_LINES) {
+		return;
+	}
+
+	_lines[lineNo] = str;
+	markDirty();
+}
+
+void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
+	surface.blitFrom(_surface, Common::Point(_area.left, _area.top));
+
+	for (int i = 0; i < CONVERSATION_LINES; ++i) {
+		Common::String &line = _lines[i];
+		if (line.empty()) {
+			continue;
+		}
+
+		// TODO: Active line should be Gui::WHITE.
+		_gui.getGame().getSystemFont().drawString(line, Gui::LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+	}
+}
+
+}
+
diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h
new file mode 100644
index 0000000..0f26a99
--- /dev/null
+++ b/engines/mutationofjb/widgets/conversationwidget.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_CONVERSATIONWIDGET_H
+#define MUTATIONOFJB_CONVERSATIONWIDGET_H
+
+#include "mutationofjb/widgets/widget.h"
+#include "graphics/surface.h"
+
+namespace MutationOfJB {
+
+class ConversationWidget : public Widget {
+public:
+	enum { CONVERSATION_LINES = 4 };
+
+	ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface);
+
+	void setLine(int lineNo, const Common::String &str);
+
+protected:
+	void _draw(Graphics::ManagedSurface &surface);
+
+private:
+
+	Graphics::Surface _surface;
+	Common::String _lines[CONVERSATION_LINES];
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/widgets/widget.cpp b/engines/mutationofjb/widgets/widget.cpp
index fea7f6f..5503f62 100644
--- a/engines/mutationofjb/widgets/widget.cpp
+++ b/engines/mutationofjb/widgets/widget.cpp
@@ -32,6 +32,17 @@ void Widget::setId(int id) {
 	_id = id;
 }
 
+bool Widget::isVisible() const {
+	return _visible;
+}
+
+void Widget::setVisible(bool visible) {
+	if (!_visible && visible) {
+		markDirty();
+	}
+	_visible = visible;
+}
+
 void Widget::markDirty() {
 	_dirty = true;
 }
@@ -42,7 +53,9 @@ bool Widget::isDirty() const {
 
 void Widget::update(Graphics::ManagedSurface &surface) {
 	if (_dirty) {
-		_draw(surface);
+		if (_visible) {
+			_draw(surface);
+		}
 		_dirty = false;
 	}
 }
diff --git a/engines/mutationofjb/widgets/widget.h b/engines/mutationofjb/widgets/widget.h
index f81d466..ed3a3ac 100644
--- a/engines/mutationofjb/widgets/widget.h
+++ b/engines/mutationofjb/widgets/widget.h
@@ -40,12 +40,15 @@ class Gui;
 
 class Widget {
 public:
-	Widget(Gui &gui, const Common::Rect &area) : _gui(gui), _area(area), _id(0), _dirty(true) {}
+	Widget(Gui &gui, const Common::Rect &area) : _gui(gui), _area(area), _id(0), _visible(true), _dirty(true) {}
 	virtual ~Widget() {}
 
 	int getId() const;
 	void setId(int id);
 
+	bool isVisible() const;
+	void setVisible(bool visible);
+
 	bool isDirty() const;
 	void markDirty();
 	void update(Graphics::ManagedSurface &);
@@ -57,6 +60,7 @@ protected:
 	Gui &_gui;
 	Common::Rect _area;
 	int _id;
+	bool _visible;
 	bool _dirty;
 };
 


Commit: 2fb867b2f56b5c337fe3c547a5f53a627e52b74e
    https://github.com/scummvm/scummvm/commit/2fb867b2f56b5c337fe3c547a5f53a627e52b74e
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix issue with parsing #MACRO and #STARTUP right after end block command.

Changed paths:
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index cc6a455..3b4c25b 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -61,6 +61,7 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 	}
 
 	// This is the start or end of section/block.
+	command = new EndBlockCommand();
 
 	if (line.size() >= 4 && (line.hasPrefix("#L ") || line.hasPrefix("-L "))) {
 		ActionInfo ai = {ActionInfo::Look, line.c_str() + 3, "", firstChar == '#', nullptr};
@@ -112,21 +113,23 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 			_ifTag = line[5];
 		}
 	} else if (line.size() >= 8 && line.hasPrefix("#MACRO")) {
-		_foundMacro = line.c_str() + 7;
+		NameAndCommand nc = {line.c_str() + 7, command};
+		_foundMacros.push_back(nc);
 	} else if (line.size() >= 10 && line.hasPrefix("#STARTUP")) {
-		_foundStartup = line.c_str() + 9;
+		const uint8 startupId = atoi(line.c_str() + 9);
+		IdAndCommand ic = {startupId, command};
+		_foundStartups.push_back(ic);
 	}
 
 	if (firstChar == '#') {
 		_hashFound = true;
 	}
 
-	command = new EndBlockCommand();
 
 	return true;
 }
 
-void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *newCommandParser) {
+void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) {
 	if (_elseFound || _hashFound) {
 		if (newCommand) {
 			ScriptParseContext::ConditionalCommandInfos::iterator it = parseCtx._pendingCondCommands.begin();
@@ -146,26 +149,39 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *,
 		_ifTag = 0;
 	}
 
-	if (!_foundMacro.empty()) {
+	if (!_foundMacros.empty()) {
 		if (newCommand) {
-			if (!parseCtx._macros.contains(_foundMacro)) {
-				parseCtx._macros[_foundMacro] = newCommand;
-			} else {
-				warning(_("Macro '%s' already exists."), _foundMacro.c_str());
+			for (NameAndCommandArray::iterator it = _foundMacros.begin(); it != _foundMacros.end();) {
+				if (it->_command != oldCommand) {
+					it++;
+					continue;
+				}
+
+				if (!parseCtx._macros.contains(it->_name)) {
+					parseCtx._macros[it->_name] = newCommand;
+				} else {
+					warning(_("Macro '%s' already exists"), it->_name.c_str());
+				}
+				it = _foundMacros.erase(it);
 			}
 		}
-		_foundMacro.clear();
 	}
-	if (!_foundStartup.empty()) {
+	if (!_foundStartups.empty()) {
 		if (newCommand) {
-			const uint8 startupId = atoi(_foundStartup.c_str());
-			if (!parseCtx._startups.contains(startupId)) {
-				parseCtx._startups[startupId] = newCommand;
-			} else {
-				warning(_("Startup %u already exists."), (unsigned int) startupId);
+			for (IdAndCommandArray::iterator it = _foundStartups.begin(); it != _foundStartups.end();) {
+				if (it->_command != oldCommand) {
+					it++;
+					continue;
+				}
+
+				if (!parseCtx._startups.contains(it->_id)) {
+					parseCtx._startups[it->_id] = newCommand;
+				} else {
+					warning(_("Startup %u already exists"), (unsigned int) it->_id);
+				}
+				it = _foundStartups.erase(it);
 			}
 		}
-		_foundStartup.clear();
 	}
 
 	if (newCommandParser != this) {
@@ -186,8 +202,15 @@ void EndBlockCommandParser::finish(ScriptParseContext &) {
 	if (!_pendingActionInfos.empty()) {
 		debug("Problem: Pending action infos from end block parser is not empty!");
 	}
+	if (!_foundMacros.empty()) {
+		debug("Problem: Found macros from end block parser is not empty!");
+	}
+	if (!_foundStartups.empty()) {
+		debug("Problem: Found startups from end block parser is not empty!");
+	}
 	_pendingActionInfos.clear();
-	_foundMacro = "";
+	_foundMacros.clear();
+	_foundStartups.clear();
 }
 
 Command::ExecuteResult EndBlockCommand::execute(ScriptExecutionContext &scriptExecCtx) {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index eb77f43..4ca46dd 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -44,8 +44,18 @@ private:
 	char _ifTag;
 
 	Common::Array<uint> _pendingActionInfos;
-	Common::String _foundMacro;
-	Common::String _foundStartup;
+	struct NameAndCommand {
+		Common::String _name;
+		Command *_command;
+	};
+	struct IdAndCommand {
+		uint8 _id;
+		Command *_command;
+	};
+	typedef Common::Array<NameAndCommand> NameAndCommandArray;
+	typedef Common::Array<IdAndCommand> IdAndCommandArray;
+	NameAndCommandArray _foundMacros;
+	IdAndCommandArray _foundStartups;
 };
 
 class EndBlockCommand : public Command {


Commit: f102667fc20d91149b685aac1bb5b05cabbc6e2b
    https://github.com/scummvm/scummvm/commit/f102667fc20d91149b685aac1bb5b05cabbc6e2b
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for DEFINE_STRUCT script command.

Changed paths:
  A engines/mutationofjb/commands/definestructcommand.cpp
  A engines/mutationofjb/commands/definestructcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/gui.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/widgets/conversationwidget.cpp


diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp
new file mode 100644
index 0000000..ee15274
--- /dev/null
+++ b/engines/mutationofjb/commands/definestructcommand.cpp
@@ -0,0 +1,83 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/definestructcommand.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/game.h"
+#include "common/debug.h"
+
+namespace MutationOfJB {
+
+bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
+	if (line.size() < 24 || !line.hasPrefix("DEFINE_STRUCT")) {
+		return false;
+	}
+
+	ConversationInfo convInfo;
+
+	const int numLines = atoi(line.c_str() + 14);
+	convInfo._context = atoi(line.c_str() + 18);
+	convInfo._objectId = atoi(line.c_str() + 20);
+	convInfo._color = Game::colorFromString(line.c_str() + 23);
+
+	for (int i = 0; i < numLines; ++i) {
+		Common::String convLineStr;
+		if (!parseCtx.readLine(convLineStr)) {
+			break;
+		}
+
+		if (convLineStr.size() != 74) {
+			debug("Conversation line in DEFINE_STRUCT with wrong length");
+			continue;
+		}
+
+		const char* linePtr = convLineStr.c_str();
+
+		ConversationInfo::Line convLine;
+
+		for (int j = 0; j < 5; ++j) {
+			ConversationInfo::Item convItem;
+			convItem._question = atoi(linePtr);
+			linePtr += 6;
+			convItem._response = atoi(linePtr);
+			linePtr += 6;
+			convItem._nextLineIndex = atoi(linePtr);
+			linePtr += 3;
+			convLine._items.push_back(convItem);
+		}
+		convInfo._lines.push_back(convLine);
+	}
+
+	command = new DefineStructCommand(convInfo);
+
+	return true;
+}
+
+Command::ExecuteResult DefineStructCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	scriptExecCtx.getGameData()._conversationInfo = _conversationInfo;
+	return Command::Finished;
+}
+
+Common::String DefineStructCommand::debugString() const {
+	return "DEFINE_STRUCT <data omitted>";
+}
+}
diff --git a/engines/mutationofjb/commands/definestructcommand.h b/engines/mutationofjb/commands/definestructcommand.h
new file mode 100644
index 0000000..13fc910
--- /dev/null
+++ b/engines/mutationofjb/commands/definestructcommand.h
@@ -0,0 +1,41 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "mutationofjb/gamedata.h"
+
+namespace MutationOfJB {
+
+class DefineStructCommandParser : public SeqCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class DefineStructCommand : public SeqCommand {
+public:
+	DefineStructCommand(const ConversationInfo& convInfo) : _conversationInfo(convInfo) {}
+	virtual Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Common::String debugString() const override;
+private:
+	ConversationInfo _conversationInfo;
+};
+}
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index d6ab622..a0f85b4 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -200,4 +200,29 @@ Font& Game::getSpeechFont() {
 	return _speechFont;
 }
 
+uint8 Game::colorFromString(const char *colorStr) {
+	struct {
+		const char *str;
+		uint8 color;
+	} colors[] = {
+		{"white", WHITE},
+		{"dakrgray", DARKGRAY},
+		{"lightgray", LIGHTGRAY},
+		{"green", GREEN},
+		{"orange", ORANGE},
+		{"darkblue", DARKBLUE},
+		{"lightblue", LIGHTBLUE},
+		{"brown", BROWN}
+	};
+
+	for (int i = 0; i < ARRAYSIZE(colors); ++i) {
+		if (strcmp(colors[i].str, colorStr) == 0) {
+			return colors[i].color;
+		}
+	}
+
+	warning(_("Color not found"));
+	return 0x00;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 122c2a1..4985a88 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -68,6 +68,8 @@ public:
 	Font& getSystemFont();
 	Font& getSpeechFont();
 
+	static uint8 colorFromString(const char *colorStr);
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 321a688..22be9dd 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -161,6 +161,23 @@ struct Scene {
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+struct ConversationInfo {
+	struct Item {
+		uint8 _question;
+		uint8 _response;
+		uint8 _nextLineIndex;
+	};
+
+	struct Line {
+		Common::Array<Item> _items;
+	};
+
+	Common::Array<Line> _lines;
+	uint8 _context;
+	uint8 _objectId;
+	uint8 _color;
+};
+
 struct GameData {
 public:
 	GameData();
@@ -175,10 +192,22 @@ public:
 	bool _partB;
 	Inventory _inventory;
 	Common::String _currentAPK;
+	ConversationInfo _conversationInfo;
 private:
 	Scene _scenes[45];
 };
 
+enum Colors {
+	WHITE = 0xC6,
+	DARKGRAY = 0xC2,
+	LIGHTGRAY = 0xC4,
+	GREEN = 0xC8,
+	ORANGE = 0xCA,
+	DARKBLUE = 0xD6,
+	LIGHTBLUE = 0xDA,
+	BROWN = 0xDC
+};
+
 }
 
 #endif
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
index d8e1286..8919a9d 100644
--- a/engines/mutationofjb/gui.h
+++ b/engines/mutationofjb/gui.h
@@ -47,17 +47,6 @@ class ConversationWidget;
 
 class Gui : public InventoryObserver, public ButtonWidgetCallback {
 public:
-	enum Colors {
-		WHITE = 0xC6,
-		DARKGRAY = 0xC2,
-		LIGHTGRAY = 0xC4,
-		GREEN = 0xC8,
-		ORANGE = 0xCA,
-		DARKBLUE = 0xD6,
-		LIGHTBLUE = 0xDA,
-		BROWN = 0xDC
-	};
-
 	typedef Common::HashMap<Common::String, int> InventoryMap;
 
 	friend class InventoryAnimationDecoderCallback;
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 2fd4123..507ae39 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	commands/changecommand.o \
 	commands/command.o \
 	commands/conditionalcommand.o \
+	commands/definestructcommand.o \
 	commands/endblockcommand.o \
 	commands/gotocommand.o \
 	commands/ifcommand.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 1f5571f..321a5ba 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -43,6 +43,7 @@
 #include "mutationofjb/commands/callmacrocommand.h"
 #include "mutationofjb/commands/newroomcommand.h"
 #include "mutationofjb/commands/renamecommand.h"
+#include "mutationofjb/commands/definestructcommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -59,6 +60,7 @@ static CommandParser **getParsers() {
 		new ChangeObjectCommandParser,
 		new ChangeStaticCommandParser,
 		new ChangeSceneCommandParser,
+		new DefineStructCommandParser,
 		new SayCommandParser,
 		new AddItemCommandParser,
 		new RemoveItemCommandParser,
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index 6196bc6..c609fb4 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -22,6 +22,7 @@
 
 #include "mutationofjb/widgets/conversationwidget.h"
 #include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/gui.h"
 #include "mutationofjb/font.h"
 
@@ -56,8 +57,8 @@ void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
 			continue;
 		}
 
-		// TODO: Active line should be Gui::WHITE.
-		_gui.getGame().getSystemFont().drawString(line, Gui::LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+		// TODO: Active line should be WHITE.
+		_gui.getGame().getSystemFont().drawString(line, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
 	}
 }
 


Commit: 20d6d71ec97c1f7bc4b95ed6c98375b47dff6646
    https://github.com/scummvm/scummvm/commit/20d6d71ec97c1f7bc4b95ed6c98375b47dff6646
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Basic conversation support.

Changed paths:
  A engines/mutationofjb/assets.cpp
  A engines/mutationofjb/assets.h
  A engines/mutationofjb/commands/talkcommand.cpp
  A engines/mutationofjb/commands/talkcommand.h
  A engines/mutationofjb/conversationlinelist.cpp
  A engines/mutationofjb/conversationlinelist.h
  A engines/mutationofjb/tasks/conversationtask.cpp
  A engines/mutationofjb/tasks/conversationtask.h
  A engines/mutationofjb/tasks/task.h
  A engines/mutationofjb/tasks/taskmanager.cpp
  A engines/mutationofjb/tasks/taskmanager.h
    engines/mutationofjb/commands/ifitemcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/util.cpp
    engines/mutationofjb/util.h
    engines/mutationofjb/widgets/conversationwidget.cpp
    engines/mutationofjb/widgets/conversationwidget.h


diff --git a/engines/mutationofjb/assets.cpp b/engines/mutationofjb/assets.cpp
new file mode 100644
index 0000000..d532469
--- /dev/null
+++ b/engines/mutationofjb/assets.cpp
@@ -0,0 +1,45 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/assets.h"
+
+namespace MutationOfJB {
+
+Assets::Assets(Game &game) : _game(game), _toSayList("tosay.ger"), _responseList("response.ger") {}
+
+Font& Assets::getSystemFont() {
+	return _systemFont;
+}
+
+Font& Assets::getSpeechFont() {
+	return _speechFont;
+}
+
+ConversationLineList &Assets::getToSayList() {
+	return _toSayList;
+}
+
+ConversationLineList &Assets::getResponseList() {
+	return _responseList;
+}
+
+}
diff --git a/engines/mutationofjb/assets.h b/engines/mutationofjb/assets.h
new file mode 100644
index 0000000..1d47641
--- /dev/null
+++ b/engines/mutationofjb/assets.h
@@ -0,0 +1,53 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_ASSETS_H
+#define MUTATIONOFJB_ASSETS_H
+
+#include "mutationofjb/font.h"
+#include "mutationofjb/conversationlinelist.h"
+
+namespace MutationOfJB {
+
+class Game;
+
+class Assets {
+public:
+	Assets(Game &game);
+
+	Font& getSystemFont();
+	Font& getSpeechFont();
+
+	ConversationLineList& getToSayList();
+	ConversationLineList& getResponseList();
+
+private:
+	Game &_game;
+	SystemFont _systemFont;
+	SpeechFont _speechFont;
+	ConversationLineList _toSayList;
+	ConversationLineList _responseList;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/commands/ifitemcommand.h b/engines/mutationofjb/commands/ifitemcommand.h
index f11ba7c..df073b9 100644
--- a/engines/mutationofjb/commands/ifitemcommand.h
+++ b/engines/mutationofjb/commands/ifitemcommand.h
@@ -33,7 +33,7 @@ class ScriptParseContext;
 
 class IfItemCommandParser : public ConditionalCommandParser {
 public:
-	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command);
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
 };
 
 class IfItemCommand : public ConditionalCommand {
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
new file mode 100644
index 0000000..d0775e4
--- /dev/null
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/talkcommand.h"
+#include "mutationofjb/tasks/conversationtask.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/game.h"
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 11 || !line.hasPrefix("TALK TO HIM")) {
+		return false;
+	}
+
+	int modeInt = 0;
+
+	if (line.size() >= 13) {
+		modeInt = atoi(line.c_str() + 12);
+	}
+
+	TalkCommand::Mode mode = TalkCommand::NORMAL_MODE;
+
+	if (modeInt == 1) {
+		mode = TalkCommand::RAY_AND_BUTTLEG_MODE;
+	} else if (modeInt == 3) {
+		mode = TalkCommand::CARNIVAL_TICKET_SELLER_MODE;
+	}
+
+	command = new TalkCommand(mode);
+	return true;
+}
+
+Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) {
+	if (!_task) {
+		_task = new ConversationTask(scriptExeCtx.getGame().getGameData()._conversationInfo);
+		scriptExeCtx.getGame().getTaskManager().addTask(_task);
+	}
+
+	if (_task->getState() == Task::FINISHED) {
+		scriptExeCtx.getGame().getTaskManager().removeTask(_task);
+		delete _task;
+		_task = nullptr;
+
+		return Command::Finished;
+	}
+
+	return Command::InProgress;
+}
+
+Common::String TalkCommand::debugString() const {
+	const char * modes[] = {"NORMAL", "RAY_AND_BUTTLEG", "CARNIVAL_TICKET_SELLER"};
+	return Common::String::format("TALK %s", modes[(int) _mode]);
+}
+
+}
diff --git a/engines/mutationofjb/commands/talkcommand.h b/engines/mutationofjb/commands/talkcommand.h
new file mode 100644
index 0000000..09424b2
--- /dev/null
+++ b/engines/mutationofjb/commands/talkcommand.h
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_TALKCOMMAND_H
+#define MUTATIONOFJB_TALKCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class ConversationTask;
+
+class TalkCommandParser : public SeqCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class TalkCommand : public SeqCommand {
+public:
+	enum Mode {
+		NORMAL_MODE,
+		RAY_AND_BUTTLEG_MODE,
+		CARNIVAL_TICKET_SELLER_MODE
+	};
+
+	TalkCommand(Mode mode) : _mode(mode), _task(nullptr) {}
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Common::String debugString() const;
+
+private:
+	Mode _mode;
+	ConversationTask *_task;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/conversationlinelist.cpp b/engines/mutationofjb/conversationlinelist.cpp
new file mode 100644
index 0000000..562c2d0
--- /dev/null
+++ b/engines/mutationofjb/conversationlinelist.cpp
@@ -0,0 +1,91 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/conversationlinelist.h"
+#include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/util.h"
+
+namespace MutationOfJB {
+
+ConversationLineList::ConversationLineList(const Common::String &fileName) {
+	parseFile(fileName);
+}
+
+const ConversationLineList::Line *ConversationLineList::getLine(uint index) const {
+	if (index > _lines.size()) {
+		return nullptr;
+	}
+
+	return &_lines[index - 1];
+}
+
+bool ConversationLineList::parseFile(const Common::String &fileName) {
+	EncryptedFile file;
+	file.open(fileName);
+	if (!file.isOpen()) {
+		reportFileMissingError(fileName.c_str());
+		return false;
+	}
+
+	while (!file.eos()) {
+		Common::String lineStr = file.readLine();
+		if (lineStr.empty()) {
+			continue;
+		}
+
+		Line line;
+
+		Common::String::iterator endIt = Common::find(lineStr.begin(), lineStr.end(), '|');
+		if (endIt != lineStr.end()) {
+			Common::String extra = lineStr + endIt;
+			if (*endIt == 'X') {
+				line._extra = Common::String(endIt + 1, lineStr.end()); // Skip 'X' char.
+			}
+		}
+
+		Common::String::iterator startSpeechIt = lineStr.begin();
+		Common::String::iterator endSpeechIt = startSpeechIt;
+
+		while (startSpeechIt < endIt) {
+			endSpeechIt = Common::find(startSpeechIt, endIt, '\\');
+			Common::String::iterator voiceFileIt = Common::find(startSpeechIt, endSpeechIt, '<');
+			Speech speech;
+
+			if (voiceFileIt != endSpeechIt) {
+				if (*voiceFileIt == 'S') {
+					speech._voiceFile = Common::String(voiceFileIt + 1, endSpeechIt);
+				}
+			}
+
+			speech._text = Common::String(startSpeechIt, voiceFileIt);
+			line._speeches.push_back(speech);
+
+			startSpeechIt = endSpeechIt + 1;
+		}
+
+		_lines.push_back(line);
+	}
+
+	return true;
+}
+
+}
diff --git a/engines/mutationofjb/conversationlinelist.h b/engines/mutationofjb/conversationlinelist.h
new file mode 100644
index 0000000..9ec446e
--- /dev/null
+++ b/engines/mutationofjb/conversationlinelist.h
@@ -0,0 +1,58 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_CONVERSATIONLINELIST_H
+#define MUTATIONOFJB_CONVERSATIONLINELIST_H
+
+#include "common/str.h"
+#include "common/array.h"
+
+namespace MutationOfJB {
+
+class ConversationLineList {
+public:
+	struct Speech {
+		Common::String _text;
+		Common::String _voiceFile;
+
+		bool isRepeating() const { return _text.firstChar() == '*'; }
+		bool isFirstSpeaker() const { return _text.firstChar() == '~'; }
+		bool isSecondSpeaker() const { return _text.firstChar() == '`'; }
+	};
+
+	struct Line {
+		Common::Array<Speech> _speeches;
+		Common::String _extra;
+	};
+
+	ConversationLineList(const Common::String &fileName);
+	const Line *getLine(uint index) const;
+
+private:
+	bool parseFile(const Common::String &fileName);
+
+	Common::Array<Line> _lines;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index a0f85b4..2415925 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -39,7 +39,9 @@ Game::Game(MutationOfJBEngine *vm)
 	_delayedLocalScript(nullptr),
 	_gui(*this, _vm->getScreen()),
 	_scriptExecCtx(*this),
-	_currentAction(ActionInfo::Walk) {
+	_currentAction(ActionInfo::Walk),
+	_taskManager(*this),
+	_assets(*this) {
 
 	_gameData = new GameData;
 	loadGameData(false);
@@ -192,14 +194,6 @@ void Game::setCurrentAction(ActionInfo::Action action) {
 	_currentAction = action;
 }
 
-Font& Game::getSystemFont() {
-	return _systemFont;
-}
-
-Font& Game::getSpeechFont() {
-	return _speechFont;
-}
-
 uint8 Game::colorFromString(const char *colorStr) {
 	struct {
 		const char *str;
@@ -225,4 +219,12 @@ uint8 Game::colorFromString(const char *colorStr) {
 	return 0x00;
 }
 
+TaskManager& Game::getTaskManager() {
+	return _taskManager;
+}
+
+Assets& Game::getAssets() {
+	return _assets;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 4985a88..33527ae 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -24,9 +24,10 @@
 #define MUTATIONOFJB_GAME_H
 
 #include "common/scummsys.h"
-#include "mutationofjb/script.h"
-#include "mutationofjb/font.h"
+#include "mutationofjb/assets.h"
 #include "mutationofjb/gui.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/tasks/taskmanager.h"
 
 namespace Common {
 class String;
@@ -65,11 +66,11 @@ public:
 	ActionInfo::Action getCurrentAction() const;
 	void setCurrentAction(ActionInfo::Action);
 
-	Font& getSystemFont();
-	Font& getSpeechFont();
-
 	static uint8 colorFromString(const char *colorStr);
 
+	TaskManager& getTaskManager();
+	Assets &getAssets();
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
@@ -88,8 +89,8 @@ private:
 
 	ScriptExecutionContext _scriptExecCtx;
 
-	SystemFont _systemFont;
-	SpeechFont _speechFont;
+	TaskManager _taskManager;
+	Assets _assets;
 };
 
 }
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 22be9dd..d1bbc54 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -168,8 +168,9 @@ struct ConversationInfo {
 		uint8 _nextLineIndex;
 	};
 
+	typedef Common::Array<Item> Items;
 	struct Line {
-		Common::Array<Item> _items;
+		Items _items;
 	};
 
 	Common::Array<Line> _lines;
diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index 7102407..3c8cc66 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -132,19 +132,25 @@ bool Gui::init() {
 
 void Gui::markDirty() {
 	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
-		(*it)->markDirty();
+		if ((*it)->isVisible()) {
+			(*it)->markDirty();
+		}
 	}
 }
 
 void Gui::handleEvent(const Common::Event &event) {
 	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
-		(*it)->handleEvent(event);
+		if ((*it)->isVisible()) {
+			(*it)->handleEvent(event);
+		}
 	}
 }
 
 void Gui::update() {
 	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
-		(*it)->update(*_screen);
+		if ((*it)->isVisible()) {
+			(*it)->update(*_screen);
+		}
 	}
 }
 
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 507ae39..89da245 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -20,12 +20,17 @@ MODULE_OBJS := \
 	commands/renamecommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
+	commands/talkcommand.o \
+	tasks/conversationtask.o \
+	tasks/taskmanager.o \
 	widgets/buttonwidget.o \
 	widgets/conversationwidget.o \
 	widgets/imagewidget.o \
 	widgets/inventorywidget.o \
 	widgets/widget.o \
 	animationdecoder.o \
+	assets.o \
+	conversationlinelist.o \
 	debug.o \
 	detection.o \
 	encryptedfile.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 321a5ba..90146f6 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -44,6 +44,7 @@
 #include "mutationofjb/commands/newroomcommand.h"
 #include "mutationofjb/commands/renamecommand.h"
 #include "mutationofjb/commands/definestructcommand.h"
+#include "mutationofjb/commands/talkcommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -62,6 +63,7 @@ static CommandParser **getParsers() {
 		new ChangeSceneCommandParser,
 		new DefineStructCommandParser,
 		new SayCommandParser,
+		new TalkCommandParser,
 		new AddItemCommandParser,
 		new RemoveItemCommandParser,
 		new RemoveAllItemsCommandParser,
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
new file mode 100644
index 0000000..75d8504
--- /dev/null
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -0,0 +1,83 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/conversationtask.h"
+#include "mutationofjb/tasks/taskmanager.h"
+#include "mutationofjb/assets.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/gui.h"
+#include "mutationofjb/util.h"
+#include "mutationofjb/widgets/conversationwidget.h"
+
+namespace MutationOfJB {
+
+void ConversationTask::start() {
+	Game &game = getTaskManager()->getGame();
+	ConversationWidget &widget = game.getGui().getConversationWidget();
+
+	widget.setCallback(this);
+	widget.setVisible(true);
+
+	updateWidget();
+}
+
+void ConversationTask::update() {
+}
+
+void ConversationTask::onResponseClicked(ConversationWidget *, int response) {
+
+	uint8 nextLineIndex = _convInfo._lines[_currentLine]._items[response]._nextLineIndex;
+	if (nextLineIndex == 0) {
+		setState(FINISHED);
+		Game &game = getTaskManager()->getGame();
+		ConversationWidget &widget = game.getGui().getConversationWidget();
+		widget.setVisible(false);
+		game.getGui().markDirty(); // TODO: Handle automatically when changing visibility.
+		return;
+	}
+
+	_currentLine = nextLineIndex - 1;
+	updateWidget();
+}
+
+void ConversationTask::updateWidget() {
+	Game &game = getTaskManager()->getGame();
+	ConversationWidget &widget = game.getGui().getConversationWidget();
+
+	const ConversationLineList& toSayList = game.getAssets().getToSayList();
+
+	const ConversationInfo::Line &convLine = _convInfo._lines[_currentLine];
+
+	for (ConversationInfo::Items::size_type i = 0; i < convLine._items.size(); ++i) {
+		Common::String widgetText;
+		const uint8 question = convLine._items[i]._question;
+		if (question != 0) {
+			const ConversationLineList::Line *line = toSayList.getLine(convLine._items[i]._question);
+			widgetText = toUpperCP895(line->_speeches[0]._text);
+		}
+
+		widget.setLine(i, widgetText);
+	}
+}
+
+}
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
new file mode 100644
index 0000000..5cc0e5f
--- /dev/null
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -0,0 +1,45 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/task.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/widgets/conversationwidget.h"
+
+namespace MutationOfJB {
+
+class ConversationTask : public Task, public ConversationWidgetCallback {
+public:
+	ConversationTask(const ConversationInfo& convInfo) : _convInfo(convInfo), _currentLine(0) {}
+	virtual ~ConversationTask() {}
+
+	virtual void start() override;
+	virtual void update() override;
+
+	virtual void onResponseClicked(ConversationWidget *, int response) override;
+private:
+	void updateWidget();
+
+	const ConversationInfo &_convInfo;
+	uint _currentLine;
+};
+
+}
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
new file mode 100644
index 0000000..7b43868
--- /dev/null
+++ b/engines/mutationofjb/tasks/task.h
@@ -0,0 +1,56 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class TaskManager;
+
+class Task {
+public:
+	enum State {
+		IDLE,
+		RUNNING,
+		FINISHED
+	};
+
+	Task() : _taskManager(nullptr) {}
+	virtual ~Task() {}
+
+	virtual void start() = 0;
+	virtual void update() = 0;
+
+	void setTaskManager(TaskManager *taskMan) { _taskManager = taskMan; }
+	TaskManager *getTaskManager() { return _taskManager; }
+
+	State getState() const { return _state; }
+
+protected:
+	void setState(State state) { _state = state; }
+
+private:
+	TaskManager *_taskManager;
+	State _state;
+};
+
+}
diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp
new file mode 100644
index 0000000..9816549
--- /dev/null
+++ b/engines/mutationofjb/tasks/taskmanager.cpp
@@ -0,0 +1,47 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/taskmanager.h"
+#include "mutationofjb/tasks/task.h"
+
+namespace MutationOfJB {
+
+void TaskManager::addTask(Task *task) {
+	_tasks.push_back(task);
+	task->setTaskManager(this);
+	task->start();
+}
+
+void TaskManager::removeTask(Task *task) {
+	Tasks::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
+	if (it != _tasks.end()) {
+		_tasks.erase(it);
+	}
+}
+
+void TaskManager::update() {
+	for (Tasks::const_iterator it = _tasks.begin(); it != _tasks.end(); ++it) {
+		(*it)->update();
+	}
+}
+
+}
diff --git a/engines/mutationofjb/tasks/taskmanager.h b/engines/mutationofjb/tasks/taskmanager.h
new file mode 100644
index 0000000..1f6be36
--- /dev/null
+++ b/engines/mutationofjb/tasks/taskmanager.h
@@ -0,0 +1,51 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_TASKMANAGER_H
+#define MUTATIONOFJB_TASKMANAGER_H
+
+#include "common/array.h"
+
+namespace MutationOfJB {
+
+class Game;
+class Task;
+
+class TaskManager {
+public:
+	TaskManager(Game &game) : _game(game) {}
+
+	void addTask(Task *task);
+	void removeTask(Task *task);
+	void update();
+
+	Game &getGame() { return _game; }
+
+private:
+	typedef Common::Array<Task *> Tasks;
+	Tasks _tasks;
+	Game &_game;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 1a56a62..6d7c02a 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -33,5 +33,27 @@ void reportFileMissingError(const char *fileName) {
 	warning("%s", errorMessage.c_str());
 }
 
+Common::String toUpperCP895(const Common::String &str) {
+	static const byte conversionTable[] = {
+		0x00, 0x9A, 0x90, 0x85, 0x8E, 0x00, 0x00, 0x80, 0x89, 0x00, 0x00, 0x00, 0x9C, 0x8A, 0x00, 0x00,	/* 0x80-0x8F */
+		0x00, 0x92, 0x00, 0xA7, 0x99, 0x00, 0xA6, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,	/* 0x90-0x9F */
+		0x8F, 0x8B, 0x95, 0x97, 0xA5, 0x00, 0x00, 0x00, 0x9B, 0x9E, 0xAB, 0x00							/* 0xA0-0xAB */
+	};
+
+	Common::String ret = str;
+	for (Common::String::iterator it = ret.begin(); it != ret.end(); ++it) {
+		const byte cp895Byte = reinterpret_cast<const byte &>(*it);
+		if (cp895Byte < 0x80) {
+			*it = static_cast<char>(toupper(*it));
+		} else if (cp895Byte <= 0xAB) {
+			byte newChar = conversionTable[cp895Byte - 0x80];
+			if (newChar != 0) {
+				reinterpret_cast<byte &>(*it) = newChar;
+			}
+		}
+	}
+	return ret;
+}
+
 }
 
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 95a896d..86ee10f 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -23,9 +23,14 @@
 #ifndef MUTATIONOFJB_UTIL_H
 #define MUTATIONOFJB_UTIL_H
 
+namespace Common {
+class String;
+}
+
 namespace MutationOfJB {
 
 void reportFileMissingError(const char *fileName);
+Common::String toUpperCP895(const Common::String &str);
 
 }
 
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index c609fb4..71a3483 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -25,6 +25,7 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/gui.h"
 #include "mutationofjb/font.h"
+#include "common/events.h"
 
 namespace MutationOfJB {
 
@@ -36,7 +37,8 @@ enum {
 
 ConversationWidget::ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface) :
 	Widget(gui, area),
-	_surface(surface) {}
+	_surface(surface),
+	_callback(nullptr) {}
 
 
 void ConversationWidget::setLine(int lineNo, const Common::String &str) {
@@ -58,7 +60,28 @@ void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
 		}
 
 		// TODO: Active line should be WHITE.
-		_gui.getGame().getSystemFont().drawString(line, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+		_gui.getGame().getAssets().getSystemFont().drawString(line, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+	}
+}
+
+void ConversationWidget::handleEvent(const Common::Event &event) {
+	switch(event.type) {
+	case Common::EVENT_LBUTTONDOWN:
+	{
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+		if (_area.contains(x, y)) {
+			if (_callback) {
+				int lineNum = (y - CONVERSATION_LINES_Y) / CONVERSATION_LINE_HEIGHT;
+				if (!_lines[lineNum].empty()) {
+					_callback->onResponseClicked(this, lineNum);
+				}
+			}
+		}
+		break;
+	}
+	default:
+		break;
 	}
 }
 
diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h
index 0f26a99..b404abc 100644
--- a/engines/mutationofjb/widgets/conversationwidget.h
+++ b/engines/mutationofjb/widgets/conversationwidget.h
@@ -28,21 +28,32 @@
 
 namespace MutationOfJB {
 
+class ConversationWidget;
+
+class ConversationWidgetCallback {
+public:
+	virtual ~ConversationWidgetCallback() {}
+	virtual void onResponseClicked(ConversationWidget *, int response) = 0;
+};
+
 class ConversationWidget : public Widget {
 public:
 	enum { CONVERSATION_LINES = 4 };
 
 	ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface);
+	void setCallback(ConversationWidgetCallback *callback) { _callback = callback; }
 
 	void setLine(int lineNo, const Common::String &str);
 
+	virtual void handleEvent(const Common::Event &event) override;
+
 protected:
-	void _draw(Graphics::ManagedSurface &surface);
+	virtual void _draw(Graphics::ManagedSurface &surface) override;
 
 private:
-
 	Graphics::Surface _surface;
 	Common::String _lines[CONVERSATION_LINES];
+	ConversationWidgetCallback *_callback;
 };
 
 }


Commit: d2e354b51f637dc2e9b251256b5a017cb41cfe59
    https://github.com/scummvm/scummvm/commit/d2e354b51f637dc2e9b251256b5a017cb41cfe59
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement scroll buttons.

Changed paths:
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/inventory.cpp
    engines/mutationofjb/inventory.h


diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index 3c8cc66..41ef94a 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -251,6 +251,10 @@ void Gui::onButtonClicked(ButtonWidget *button) {
 	if (buttonId <= BUTTON_PICKUP) {
 		const ActionInfo::Action actions[] = {ActionInfo::Walk, ActionInfo::Talk, ActionInfo::Look, ActionInfo::Use, ActionInfo::PickUp};
 		_game.setCurrentAction(actions[buttonId]);
+	} else if (buttonId == BUTTON_SCROLL_LEFT) {
+		_game.getGameData().getInventory().scrollLeft();
+	} else if (buttonId == BUTTON_SCROLL_RIGHT) {
+		_game.getGameData().getInventory().scrollRight();
 	}
 }
 
diff --git a/engines/mutationofjb/inventory.cpp b/engines/mutationofjb/inventory.cpp
index 2fa7f2c..2d7ba28 100644
--- a/engines/mutationofjb/inventory.cpp
+++ b/engines/mutationofjb/inventory.cpp
@@ -81,6 +81,18 @@ void Inventory::renameItem(const Common::String &oldName, const Common::String &
 	}
 }
 
+void Inventory::scrollLeft() {
+	if (_items.size() > VISIBLE_ITEMS) {
+		rotateItemsRight(1);
+	}
+}
+
+void Inventory::scrollRight() {
+	if (_items.size() > VISIBLE_ITEMS) {
+		rotateItemsLeft(1);
+	}
+}
+
 void Inventory::rotateItemsRight(uint n) {
 	if (_items.size() < 2) {
 		return;
@@ -121,7 +133,7 @@ void Inventory::reverseItems(uint from, uint to) {
 
 	const uint size = to - from + 1;
 	for (uint i = 0; i < size / 2; ++i) {
-		SWAP(_items[i], _items[size - i - 1]);
+		SWAP(_items[from + i], _items[to - i]);
 	}
 }
 
diff --git a/engines/mutationofjb/inventory.h b/engines/mutationofjb/inventory.h
index c22422d..79a0d75 100644
--- a/engines/mutationofjb/inventory.h
+++ b/engines/mutationofjb/inventory.h
@@ -52,12 +52,14 @@ public:
 	void removeAllItems();
 	void renameItem(const Common::String &oldName, const Common::String &newName);
 
-	void rotateItemsRight(uint n);
-	void rotateItemsLeft(uint n);
+	void scrollLeft();
+	void scrollRight();
 
 	void setObserver(InventoryObserver *observer);
 
 private:
+	void rotateItemsRight(uint n);
+	void rotateItemsLeft(uint n);
 	void reverseItems(uint from, uint to);
 
 	Items _items;


Commit: febff83a4edc89e1dbc6f4c56f5531f0eb8f3287
    https://github.com/scummvm/scummvm/commit/febff83a4edc89e1dbc6f4c56f5531f0eb8f3287
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Draw objects (first frame only) and improve conversation support.

Changed paths:
  A engines/mutationofjb/tasks/saytask.cpp
  A engines/mutationofjb/tasks/saytask.h
  A engines/mutationofjb/timer.cpp
  A engines/mutationofjb/timer.h
    engines/mutationofjb/commands/definestructcommand.cpp
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/room.cpp
    engines/mutationofjb/room.h
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/conversationtask.h
    engines/mutationofjb/tasks/task.h
    engines/mutationofjb/tasks/taskmanager.cpp
    engines/mutationofjb/widgets/conversationwidget.cpp
    engines/mutationofjb/widgets/conversationwidget.h


diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp
index ee15274..93dbfc8 100644
--- a/engines/mutationofjb/commands/definestructcommand.cpp
+++ b/engines/mutationofjb/commands/definestructcommand.cpp
@@ -56,7 +56,7 @@ bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseCon
 
 		for (int j = 0; j < 5; ++j) {
 			ConversationInfo::Item convItem;
-			convItem._question = atoi(linePtr);
+			convItem._choice = atoi(linePtr);
 			linePtr += 6;
 			convItem._response = atoi(linePtr);
 			linePtr += 6;
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index d0775e4..18ce956 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -21,9 +21,12 @@
  */
 
 #include "mutationofjb/commands/talkcommand.h"
-#include "mutationofjb/tasks/conversationtask.h"
-#include "mutationofjb/script.h"
+
 #include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+#include "mutationofjb/tasks/conversationtask.h"
+#include "mutationofjb/tasks/taskmanager.h"
+
 #include "common/str.h"
 
 namespace MutationOfJB {
@@ -53,7 +56,7 @@ bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &,
 
 Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) {
 	if (!_task) {
-		_task = new ConversationTask(scriptExeCtx.getGame().getGameData()._conversationInfo);
+		_task = new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo);
 		scriptExeCtx.getGame().getTaskManager().addTask(_task);
 	}
 
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 2415925..69647f2 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -100,7 +100,9 @@ Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
 	_gameData->_lastScene = _gameData->_currentScene;
 	_gameData->_currentScene = sceneId;
 	_gameData->_partB = partB;
+
 	_room->load(_gameData->_currentScene, partB);
+	_room->redraw();
 
 	EncryptedFile scriptFile;
 	Common::String fileName = Common::String::format("scrn%d%s.atn", sceneId, partB ? "b" : "");
@@ -180,6 +182,7 @@ void Game::update() {
 	}
 
 	_gui.update();
+	_taskManager.update();
 }
 
 Gui &Game::getGui() {
@@ -227,4 +230,9 @@ Assets& Game::getAssets() {
 	return _assets;
 }
 
+Graphics::Screen &Game::getScreen()
+{
+	return *_vm->getScreen();
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 33527ae..056cffb 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -71,6 +71,8 @@ public:
 	TaskManager& getTaskManager();
 	Assets &getAssets();
 
+	Graphics::Screen &getScreen();
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index 1ece44c..6a9c166 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -136,7 +136,11 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	_palRotStart = stream.readByte();
 	_palRotEnd = stream.readByte();
 	_palRotPeriod = stream.readByte();
-	stream.read(_unknown38A, 80);
+	_exhaustedChoiceNext = stream.readByte();
+
+	for (i = 0; i < 79; ++i) {
+		_exhaustedChoices[i]._encodedData = stream.readByte();
+	}
 
 	return true;
 }
@@ -222,6 +226,22 @@ Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) {
 	return nullptr;
 }
 
+void Scene::addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) {
+	_exhaustedChoices[_exhaustedChoiceNext - 1] = ExhaustedChoice(context, choiceIndex, choiceIndexList);
+	_exhaustedChoiceNext++;
+}
+
+bool Scene::isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) const {
+	for (uint i = 0; i < _exhaustedChoiceNext - 1; ++i) {
+		const ExhaustedChoice &choice = _exhaustedChoices[i];
+		if (choice.getContext() == context && choice.getChoiceIndex() == choiceIndex && choice.getChoiceListIndex() == choiceListIndex) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 
 GameData::GameData()
 	: _currentScene(0),
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index d1bbc54..62c66a1 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -121,6 +121,22 @@ struct Bitmap {
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+struct ExhaustedChoice {
+	/*
+		1 bit - context
+		3 bits - choice index
+		4 bits - choice list index
+	*/
+	uint8 _encodedData;
+
+	uint8 getContext() const { return (_encodedData >> 7) & 0x1; }
+	uint8 getChoiceIndex() const { return (_encodedData >> 4) & 0x7; }
+	uint8 getChoiceListIndex() const { return _encodedData & 0xF; }
+
+	ExhaustedChoice() : _encodedData(0) {}
+	ExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) :
+		_encodedData(((context & 0x1) << 7) | ((choiceIndex & 0x7) << 4) | (choiceListIndex & 0xF)) {}
+};
 
 struct Scene {
 	Door *getDoor(uint8 objectId);
@@ -135,6 +151,9 @@ struct Scene {
 	Static *findStatic(int16 x, int16 y, int *index = nullptr);
 	Bitmap *findBitmap(int16 x, int16 y, int *index = nullptr);
 
+	void addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList);
+	bool isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) const;
+
 	uint8 _startup;
 	uint8 _unknown001;
 	uint8 _unknown002;
@@ -156,14 +175,17 @@ struct Scene {
 	uint8 _palRotStart;
 	uint8 _palRotEnd;
 	uint8 _palRotPeriod;
-	uint8 _unknown38A[80];
+
+	/* Points to the first free item in exhausted choices list. */
+	uint8 _exhaustedChoiceNext;
+	ExhaustedChoice _exhaustedChoices[79];
 
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
 struct ConversationInfo {
 	struct Item {
-		uint8 _question;
+		uint8 _choice;
 		uint8 _response;
 		uint8 _nextLineIndex;
 	};
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 89da245..f3a1d78 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS := \
 	commands/seqcommand.o \
 	commands/talkcommand.o \
 	tasks/conversationtask.o \
+	tasks/saytask.o \
 	tasks/taskmanager.o \
 	widgets/buttonwidget.o \
 	widgets/conversationwidget.o \
@@ -42,6 +43,7 @@ MODULE_OBJS := \
 	mutationofjb.o \
 	room.o \
 	script.o \
+	timer.o \
 	util.o
 
 # This module can be built as a plugin
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 62af983..5577077 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -21,17 +21,25 @@
  */
 
 #include "mutationofjb/room.h"
+
 #include "mutationofjb/animationdecoder.h"
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/util.h"
+
 #include "common/str.h"
 #include "common/translation.h"
+
 #include "graphics/screen.h"
 
 namespace MutationOfJB {
 
+enum {
+	GAME_AREA_WIDTH = 320,
+	GAME_AREA_HEIGHT = 139
+};
+
 class RoomAnimationDecoderCallback : public AnimationDecoderCallback {
 public:
 	RoomAnimationDecoderCallback(Room &room) : _room(room) {}
@@ -47,9 +55,11 @@ void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE])
 
 void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) {
 	if (frameNo == 0) {
-		Common::Rect rect(0, 0, 320, 139);
+		Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT);
 		if (_room._game->isCurrentSceneMap()) {
 			rect = Common::Rect(0, 0, 320, 200);
+		} else {
+			_room._background.blitFrom(surface, rect, Common::Point(0, 0));
 		}
 		_room._screen->blitFrom(surface, rect, Common::Point(0, 0));
 	}
@@ -78,10 +88,11 @@ void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surfa
 	}
 }
 
-Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen) {}
+Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen), _background(GAME_AREA_WIDTH, GAME_AREA_HEIGHT) {}
 
 bool Room::load(uint8 roomNumber, bool roomB) {
 	_objectsStart.clear();
+	_surfaces.clear(); // TODO: Fix memory leak.
 
 	Scene *const scene = _game->getGameData().getCurrentScene();
 	if (scene) {
@@ -118,7 +129,23 @@ void Room::drawObjectAnimation(uint8 objectId, int animOffset) {
 
 	const int startFrame = _objectsStart[objectId - 1];
 	const int animFrame = startFrame + animOffset;
+	// TODO: Threshold.
 	_screen->blitFrom(_surfaces[animFrame], Common::Point(object->_x, object->_y));
 }
 
+void Room::redraw() {
+	if (!_game->isCurrentSceneMap()) {
+		Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT);
+		_screen->blitFrom(_background.rawSurface(), rect, Common::Point(0, 0));
+
+		Scene *const currentScene = _game->getGameData().getCurrentScene();
+		for (int i = 0; i < currentScene->getNoObjects(); ++i) {
+			Object *const obj = currentScene->getObject(i + 1);
+			if (obj->_AC) {
+				drawObjectAnimation(i + 1, 0);
+			}
+		}
+	}
+}
+
 }
diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h
index e57d2eb..083fba6 100644
--- a/engines/mutationofjb/room.h
+++ b/engines/mutationofjb/room.h
@@ -26,6 +26,7 @@
 #include "common/scummsys.h"
 #include "common/array.h"
 #include "graphics/surface.h"
+#include "graphics/managed_surface.h"
 
 namespace Graphics {
 class Screen;
@@ -44,9 +45,11 @@ public:
 	Room(Game *game, Graphics::Screen *screen);
 	bool load(uint8 roomNumber, bool roomB);
 	void drawObjectAnimation(uint8 objectId, int animOffset);
+	void redraw();
 private:
 	Game *_game;
 	Graphics::Screen *_screen;
+	Graphics::ManagedSurface _background;
 	Common::Array<Graphics::Surface> _surfaces;
 	Common::Array<int> _objectsStart;
 };
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 75d8504..512b38b 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -21,63 +21,186 @@
  */
 
 #include "mutationofjb/tasks/conversationtask.h"
-#include "mutationofjb/tasks/taskmanager.h"
+
 #include "mutationofjb/assets.h"
+#include "mutationofjb/conversationlinelist.h"
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/gui.h"
+#include "mutationofjb/tasks/saytask.h"
+#include "mutationofjb/tasks/taskmanager.h"
 #include "mutationofjb/util.h"
 #include "mutationofjb/widgets/conversationwidget.h"
 
 namespace MutationOfJB {
 
 void ConversationTask::start() {
+	setState(RUNNING);
+
 	Game &game = getTaskManager()->getGame();
 	ConversationWidget &widget = game.getGui().getConversationWidget();
 
 	widget.setCallback(this);
 	widget.setVisible(true);
 
-	updateWidget();
+	_currentLineIndex = 0;
+
+	showChoicesOrPick();
 }
 
 void ConversationTask::update() {
-}
+	if (_sayTask) {
+		if (_sayTask->getState() == Task::FINISHED) {
+			getTaskManager()->removeTask(_sayTask);
+			delete _sayTask;
+			_sayTask = nullptr;
 
-void ConversationTask::onResponseClicked(ConversationWidget *, int response) {
+			switch (_substate) {
+			case SAYING_NO_CHOICES:
+				finish();
+				break;
+			case SAYING_CHOICE: {
+				const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList();
+				const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
 
-	uint8 nextLineIndex = _convInfo._lines[_currentLine]._items[response]._nextLineIndex;
-	if (nextLineIndex == 0) {
-		setState(FINISHED);
-		Game &game = getTaskManager()->getGame();
-		ConversationWidget &widget = game.getGui().getConversationWidget();
-		widget.setVisible(false);
-		game.getGui().markDirty(); // TODO: Handle automatically when changing visibility.
-		return;
+				_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
+				getTaskManager()->addTask(_sayTask);
+				_substate = SAYING_RESPONSE;
+				break;
+			}
+			case SAYING_RESPONSE: {
+				if (_currentItem->_nextLineIndex == 0) {
+					finish();
+				} else {
+					_currentLineIndex = _currentItem->_nextLineIndex - 1;
+					showChoicesOrPick();
+				}
+				break;
+			}
+			default:
+				break;
+			}
+		}
 	}
+}
 
-	_currentLine = nextLineIndex - 1;
-	updateWidget();
+void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) {
+	const ConversationInfo::Item &item = getCurrentLine()->_items[data];
+	convWidget->clearChoices();
+
+	const ConversationLineList& toSayList = getTaskManager()->getGame().getAssets().getToSayList();
+	_sayTask = new SayTask(toSayList.getLine(item._choice)->_speeches[0]._text, _convInfo._color);
+	getTaskManager()->addTask(_sayTask);
+	_substate = SAYING_CHOICE;
+	_currentItem = &item;
+	getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1);
 }
 
-void ConversationTask::updateWidget() {
+void ConversationTask::showChoicesOrPick() {
 	Game &game = getTaskManager()->getGame();
-	ConversationWidget &widget = game.getGui().getConversationWidget();
+	GameData &gameData = game.getGameData();
+	Scene *const scene = gameData.getScene(_sceneId);
+
+	Common::Array<uint32> itemsWithValidChoices;
+	Common::Array<uint32> itemsWithValidResponses;
+	Common::Array<uint32> itemsWithValidNext;
+
+	/*
+		Collect valid "to say" choices (not exhausted and not empty).
+		Collect valid responses (not exhausted and not empty).
+		If there are at least two visible choices, we show them.
+		If there is just one visible choice, pick it automatically.
+		If there are no visible choices, automatically pick first valid response.
+	*/
+
+	const ConversationInfo::Line *const currentLine = getCurrentLine();
+	for (ConversationInfo::Items::size_type i = 0; i < currentLine->_items.size(); ++i) {
+		const ConversationInfo::Item &item = currentLine->_items[i];
+
+		if (scene->isChoiceExhausted(_convInfo._context, (uint8) i + 1, (uint8) _currentLineIndex + 1)) {
+			continue;
+		}
+		const uint8 choice = item._choice;
+		const uint8 response = item._response;
+		const uint8 next = item._nextLineIndex;
+
+		if (choice != 0) {
+			itemsWithValidChoices.push_back(i);
+		}
 
-	const ConversationLineList& toSayList = game.getAssets().getToSayList();
+		if (response != 0) {
+			itemsWithValidResponses.push_back(i);
+		}
 
-	const ConversationInfo::Line &convLine = _convInfo._lines[_currentLine];
+		if (next != 0) {
+			itemsWithValidNext.push_back(i);
+		}
+	}
+
+	if (itemsWithValidChoices.size() > 1) {
+		ConversationWidget &widget = game.getGui().getConversationWidget();
+		const ConversationLineList& toSayList = game.getAssets().getToSayList();
 
-	for (ConversationInfo::Items::size_type i = 0; i < convLine._items.size(); ++i) {
-		Common::String widgetText;
-		const uint8 question = convLine._items[i]._question;
-		if (question != 0) {
-			const ConversationLineList::Line *line = toSayList.getLine(convLine._items[i]._question);
-			widgetText = toUpperCP895(line->_speeches[0]._text);
+		for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidChoices.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
+			const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices[i]];
+			const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
+			const Common::String widgetText = toUpperCP895(line->_speeches[0]._text);
+			widget.setChoice((int) i, widgetText, itemsWithValidChoices[i]);
 		}
+		_substate = IDLE;
+		_currentItem = nullptr;
+
+		_haveChoices = true;
+	} else if (itemsWithValidChoices.size() == 1) {
+		const ConversationLineList& toSayList = game.getAssets().getToSayList();
+		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()];
+		const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
+
+		_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
+		getTaskManager()->addTask(_sayTask);
+		_substate = SAYING_CHOICE;
+		_currentItem = &item;
+
+		game.getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, itemsWithValidChoices.front() + 1, _currentLineIndex + 1);
+
+		_haveChoices = true;
+	} else if (!itemsWithValidResponses.empty()) {
+		const ConversationLineList& responseList = game.getAssets().getResponseList();
+		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()];
+		const ConversationLineList::Line *const line = responseList.getLine(item._response);
 
-		widget.setLine(i, widgetText);
+		_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
+		getTaskManager()->addTask(_sayTask);
+		_substate = SAYING_RESPONSE;
+		_currentItem = &item;
+
+		_haveChoices = true;
+	} else if (!itemsWithValidNext.empty()) {
+		_currentLineIndex = currentLine->_items[itemsWithValidNext.front()]._nextLineIndex - 1;
+		showChoicesOrPick();
+	} else {
+		if (_haveChoices) {
+			finish();
+		} else {
+			_sayTask = new SayTask("Nothing to talk about.", _convInfo._color); // TODO: This is hardcoded in executable. Load it.
+			getTaskManager()->addTask(_sayTask);
+			_substate = SAYING_NO_CHOICES;
+			_currentItem = nullptr;
+		}
 	}
 }
 
+const ConversationInfo::Line *ConversationTask::getCurrentLine() const {
+	return &_convInfo._lines[_currentLineIndex];
+}
+
+void ConversationTask::finish() {
+	setState(FINISHED);
+
+	Game &game = getTaskManager()->getGame();
+	ConversationWidget &widget = game.getGui().getConversationWidget();
+	widget.setVisible(false);
+	game.getGui().markDirty(); // TODO: Handle automatically when changing visibility.
+}
+
 }
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
index 5cc0e5f..3bcbb6f 100644
--- a/engines/mutationofjb/tasks/conversationtask.h
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -26,20 +26,37 @@
 
 namespace MutationOfJB {
 
+class SayTask;
+
 class ConversationTask : public Task, public ConversationWidgetCallback {
 public:
-	ConversationTask(const ConversationInfo& convInfo) : _convInfo(convInfo), _currentLine(0) {}
+	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo) : _sceneId(sceneId), _convInfo(convInfo), _currentLineIndex(0), _currentItem(nullptr), _sayTask(nullptr), _substate(IDLE), _haveChoices(false) {}
 	virtual ~ConversationTask() {}
 
 	virtual void start() override;
 	virtual void update() override;
 
-	virtual void onResponseClicked(ConversationWidget *, int response) override;
+	virtual void onChoiceClicked(ConversationWidget *, int response, uint32 data) override;
 private:
-	void updateWidget();
+	void showChoicesOrPick();
+	const ConversationInfo::Line *getCurrentLine() const;
+	void finish();
 
+	uint8 _sceneId;
 	const ConversationInfo &_convInfo;
-	uint _currentLine;
+	uint _currentLineIndex;
+	const ConversationInfo::Item *_currentItem;
+	SayTask* _sayTask;
+
+	enum Substate {
+		IDLE,
+		SAYING_CHOICE,
+		SAYING_RESPONSE,
+		SAYING_NO_CHOICES
+	};
+
+	Substate _substate;
+	bool _haveChoices;
 };
 
 }
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
new file mode 100644
index 0000000..2ef4cdf
--- /dev/null
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -0,0 +1,55 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/saytask.h"
+
+#include "mutationofjb/tasks/taskmanager.h"
+#include "mutationofjb/assets.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/room.h"
+
+#include "graphics/managed_surface.h"
+#include "graphics/screen.h"
+
+namespace MutationOfJB {
+
+SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(1000) {}
+
+void SayTask::start() {
+
+	getTaskManager()->getGame().getAssets().getSpeechFont().drawString(_toSay, _color, 0, 0, getTaskManager()->getGame().getScreen());
+	_timer.start();
+	setState(RUNNING);
+}
+
+void SayTask::update() {
+	_timer.update();
+
+	if (_timer.isFnished()) {
+		getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
+		setState(FINISHED);
+		return;
+	}
+}
+
+}
diff --git a/engines/mutationofjb/tasks/saytask.h b/engines/mutationofjb/tasks/saytask.h
new file mode 100644
index 0000000..a7ac96d
--- /dev/null
+++ b/engines/mutationofjb/tasks/saytask.h
@@ -0,0 +1,44 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/task.h"
+
+#include "mutationofjb/timer.h"
+
+#include "common/str.h"
+
+namespace MutationOfJB {
+
+class SayTask : public Task {
+public:
+	SayTask(const Common::String &toSay, uint8 color);
+
+	virtual void start() override;
+	virtual void update() override;
+
+private:
+	Common::String _toSay;
+	uint8 _color;
+	Timer _timer;
+};
+
+}
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
index 7b43868..1b98097 100644
--- a/engines/mutationofjb/tasks/task.h
+++ b/engines/mutationofjb/tasks/task.h
@@ -20,6 +20,9 @@
  *
  */
 
+#ifndef MUTATIONOFJB_TASK_H
+#define MUTATIONOFJB_TASK_H
+
 #include "common/scummsys.h"
 
 namespace MutationOfJB {
@@ -54,3 +57,5 @@ private:
 };
 
 }
+
+#endif
diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp
index 9816549..7fbf64d 100644
--- a/engines/mutationofjb/tasks/taskmanager.cpp
+++ b/engines/mutationofjb/tasks/taskmanager.cpp
@@ -40,7 +40,9 @@ void TaskManager::removeTask(Task *task) {
 
 void TaskManager::update() {
 	for (Tasks::const_iterator it = _tasks.begin(); it != _tasks.end(); ++it) {
-		(*it)->update();
+		if ((*it)->getState() == Task::RUNNING) {
+			(*it)->update();
+		}
 	}
 }
 
diff --git a/engines/mutationofjb/timer.cpp b/engines/mutationofjb/timer.cpp
new file mode 100644
index 0000000..8375445
--- /dev/null
+++ b/engines/mutationofjb/timer.cpp
@@ -0,0 +1,56 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/timer.h"
+
+#include "common/system.h"
+
+namespace MutationOfJB {
+
+Timer::Timer(uint32 millis) : _millis(millis), _startTime(0), _state(IDLE) {
+}
+
+void Timer::start() {
+	_startTime = g_system->getMillis();
+	_state = RUNNING;
+}
+
+bool Timer::isFnished() const {
+	return _state == FINISHED;
+}
+
+bool Timer::isRunning() const {
+	return _state == RUNNING;
+}
+
+void Timer::update() {
+	if (_state != RUNNING) {
+		return;
+	}
+
+	uint32 currentTime = g_system->getMillis();
+	if (currentTime - _startTime >= _millis) {
+		_state = FINISHED;
+	}
+}
+
+}
diff --git a/engines/mutationofjb/timer.h b/engines/mutationofjb/timer.h
new file mode 100644
index 0000000..b12fe22
--- /dev/null
+++ b/engines/mutationofjb/timer.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class Timer {
+public:
+	Timer(uint32 millis);
+
+	void start();
+
+	bool isFnished() const;
+	bool isRunning() const;
+
+	void update();
+
+private:
+	enum State {
+		IDLE,
+		RUNNING,
+		FINISHED
+	};
+
+	uint32 _millis;
+	uint32 _startTime;
+	State _state;
+};
+
+}
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index 71a3483..a46b9c5 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -41,26 +41,35 @@ ConversationWidget::ConversationWidget(Gui &gui, const Common::Rect &area, const
 	_callback(nullptr) {}
 
 
-void ConversationWidget::setLine(int lineNo, const Common::String &str) {
-	if (lineNo >= CONVERSATION_LINES) {
+void ConversationWidget::setChoice(int choiceNo, const Common::String &str, uint32 data) {
+	if (choiceNo >= CONVERSATION_MAX_CHOICES) {
 		return;
 	}
 
-	_lines[lineNo] = str;
+	_choices[choiceNo]._str = str;
+	_choices[choiceNo]._data = data;
+	markDirty();
+}
+
+void ConversationWidget::clearChoices() {
+	for (int i = 0; i < CONVERSATION_MAX_CHOICES; ++i) {
+		_choices[i]._str.clear();
+		_choices[i]._data = 0;
+	}
 	markDirty();
 }
 
 void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
 	surface.blitFrom(_surface, Common::Point(_area.left, _area.top));
 
-	for (int i = 0; i < CONVERSATION_LINES; ++i) {
-		Common::String &line = _lines[i];
-		if (line.empty()) {
+	for (int i = 0; i < CONVERSATION_MAX_CHOICES; ++i) {
+		Common::String &str = _choices[i]._str;
+		if (str.empty()) {
 			continue;
 		}
 
 		// TODO: Active line should be WHITE.
-		_gui.getGame().getAssets().getSystemFont().drawString(line, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+		_gui.getGame().getAssets().getSystemFont().drawString(str, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
 	}
 }
 
@@ -72,9 +81,9 @@ void ConversationWidget::handleEvent(const Common::Event &event) {
 		const int16 y = event.mouse.y;
 		if (_area.contains(x, y)) {
 			if (_callback) {
-				int lineNum = (y - CONVERSATION_LINES_Y) / CONVERSATION_LINE_HEIGHT;
-				if (!_lines[lineNum].empty()) {
-					_callback->onResponseClicked(this, lineNum);
+				int choiceNo = (y - CONVERSATION_LINES_Y) / CONVERSATION_LINE_HEIGHT;
+				if (!_choices[choiceNo]._str.empty()) {
+					_callback->onChoiceClicked(this, choiceNo, _choices[choiceNo]._data);
 				}
 			}
 		}
diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h
index b404abc..51ea86e 100644
--- a/engines/mutationofjb/widgets/conversationwidget.h
+++ b/engines/mutationofjb/widgets/conversationwidget.h
@@ -33,17 +33,18 @@ class ConversationWidget;
 class ConversationWidgetCallback {
 public:
 	virtual ~ConversationWidgetCallback() {}
-	virtual void onResponseClicked(ConversationWidget *, int response) = 0;
+	virtual void onChoiceClicked(ConversationWidget *, int choiceNo, uint32 data) = 0;
 };
 
 class ConversationWidget : public Widget {
 public:
-	enum { CONVERSATION_LINES = 4 };
+	enum { CONVERSATION_MAX_CHOICES = 4 };
 
 	ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface);
 	void setCallback(ConversationWidgetCallback *callback) { _callback = callback; }
 
-	void setLine(int lineNo, const Common::String &str);
+	void setChoice(int choiceNo, const Common::String &str, uint32 data = 0);
+	void clearChoices();
 
 	virtual void handleEvent(const Common::Event &event) override;
 
@@ -52,7 +53,10 @@ protected:
 
 private:
 	Graphics::Surface _surface;
-	Common::String _lines[CONVERSATION_LINES];
+	struct ChoiceInfo {
+		Common::String _str;
+		uint32 _data;
+	} _choices[CONVERSATION_MAX_CHOICES];
 	ConversationWidgetCallback *_callback;
 };
 


Commit: 3b614f08327441d5252add7c16f4955652e32d0a
    https://github.com/scummvm/scummvm/commit/3b614f08327441d5252add7c16f4955652e32d0a
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement RANDOM command.

Changed paths:
  A engines/mutationofjb/commands/randomcommand.cpp
  A engines/mutationofjb/commands/randomcommand.h
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 3b4c25b..5fcccd3 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -56,7 +56,7 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 	}
 
 	const char firstChar = line.firstChar();
-	if (firstChar != '#' && firstChar != '=' && firstChar != '-') {
+	if (firstChar != '#' && firstChar != '=' && firstChar != '-' && firstChar != '\\') {
 		return false;
 	}
 
diff --git a/engines/mutationofjb/commands/randomcommand.cpp b/engines/mutationofjb/commands/randomcommand.cpp
new file mode 100644
index 0000000..ff03e96
--- /dev/null
+++ b/engines/mutationofjb/commands/randomcommand.cpp
@@ -0,0 +1,110 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/randomcommand.h"
+
+#include "mutationofjb/game.h"
+#include "mutationofjb/script.h"
+#include "common/debug.h"
+#include "common/random.h"
+#include "common/translation.h"
+
+/*
+	"RANDOM " <numChoices>
+
+	RANDOM command randomly picks one of the command blocks that
+	follow it and jumps to its start.
+
+	These blocks start with "/" and end with "\". The end of a random
+	block also ends the current section. The number of blocks must
+	match numChoices.
+*/
+
+namespace MutationOfJB {
+
+bool RandomCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
+	if (line.size() < 8 || !line.hasPrefix("RANDOM")) {
+		return false;
+	}
+
+	int numChoices = atoi(line.c_str() + 7);
+	if (parseCtx._pendingRandomCommand) {
+		// Nested RANDOM commands are unused and not properly supported by the original game.
+		warning(_("Ignoring nested RANDOM command."));
+	} else if (numChoices >= 1) {
+		RandomCommand *randomCommand = new RandomCommand(static_cast<uint>(numChoices));
+		parseCtx._pendingRandomCommand = randomCommand;
+		command = randomCommand;
+	} else {
+		warning(_("Ignoring malformed RANDOM command with %d choices."), numChoices);
+	}
+
+	return true;
+}
+
+bool RandomBlockStartParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&) {
+	if (line != "/") {
+		return false;
+	}
+
+	if (!parseCtx._pendingRandomCommand) {
+		warning(_("Unexpected start of RANDOM block"));
+	}
+
+	return true;
+}
+
+void RandomBlockStartParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *) {
+	if (newCommand && parseCtx._pendingRandomCommand) {
+		parseCtx._pendingRandomCommand->_choices.push_back(newCommand);
+	}
+}
+
+RandomCommand::RandomCommand(uint numChoices)
+	: _numChoices(numChoices),
+	  _chosenNext(nullptr)
+{
+	_choices.reserve(numChoices);
+}
+
+Command::ExecuteResult RandomCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	assert(!_choices.empty());
+
+	Common::RandomSource &rng = scriptExecCtx.getGame().getRandomSource();
+	uint choice = rng.getRandomNumber(_choices.size() - 1);
+	_chosenNext = _choices[choice];
+	return Finished;
+}
+
+Command *RandomCommand::next() const {
+	return _chosenNext;
+}
+
+Common::String RandomCommand::debugString() const {
+	return Common::String::format("RANDOM %u", _numChoices);
+}
+
+const RandomCommand::Choices &RandomCommand::getChoices() const {
+	return _choices;
+}
+
+}
diff --git a/engines/mutationofjb/commands/randomcommand.h b/engines/mutationofjb/commands/randomcommand.h
new file mode 100644
index 0000000..ffe9fc2
--- /dev/null
+++ b/engines/mutationofjb/commands/randomcommand.h
@@ -0,0 +1,70 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_RANDOMCOMMAND_H
+#define MUTATIONOFJB_RANDOMCOMMAND_H
+
+#include "mutationofjb/commands/command.h"
+#include "common/array.h"
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class RandomCommandParser : public CommandParser {
+public:
+	RandomCommandParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+class RandomBlockStartParser : public CommandParser {
+public:
+	RandomBlockStartParser() {}
+
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
+};
+
+class RandomCommand : public Command {
+	friend class RandomBlockStartParser;
+
+public:
+	typedef Common::Array<Command *> Choices;
+
+	RandomCommand(uint numChoices);
+
+	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Command *next() const override;
+
+	virtual Common::String debugString() const override;
+
+	const Choices &getChoices() const;
+
+private:
+	uint _numChoices;
+	Choices _choices;
+	Command *_chosenNext;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 171eca5..b1c96ef 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -30,6 +30,7 @@
 #include "mutationofjb/commands/seqcommand.h"
 #include "mutationofjb/commands/conditionalcommand.h"
 #include "mutationofjb/commands/callmacrocommand.h"
+#include "mutationofjb/commands/randomcommand.h"
 #include "common/debug-channels.h"
 #include "common/translation.h"
 #include "common/scummsys.h"
@@ -152,6 +153,14 @@ void Console::showCommands(Command *command, int indentLevel) {
 			command = nullptr;
 		} else if (CallMacroCommand* const callMacroCmd = dynamic_cast<CallMacroCommand *>(command)) {
 			command = callMacroCmd->getReturnCommand();
+		} else if (RandomCommand* const randomCmd = dynamic_cast<RandomCommand *>(command)) {
+			const RandomCommand::Choices &choices = randomCmd->getChoices();
+			for (RandomCommand::Choices::size_type i = 0; i < choices.size(); ++i) {
+				showIndent(indentLevel + 1);
+				debugPrintf("CASE %u\n", i);
+				showCommands(choices[i], indentLevel + 2);
+			}
+			command = nullptr;
 		} else {
 			command = nullptr;
 		}
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 69647f2..190b62e 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -36,6 +36,7 @@ namespace MutationOfJB {
 
 Game::Game(MutationOfJBEngine *vm)
 	: _vm(vm),
+	_randomSource("mutationofjb"),
 	_delayedLocalScript(nullptr),
 	_gui(*this, _vm->getScreen()),
 	_scriptExecCtx(*this),
@@ -60,6 +61,10 @@ Game::Game(MutationOfJBEngine *vm)
 	changeScene(13, false); // Initial scene.
 }
 
+Common::RandomSource &Game::getRandomSource() {
+	return _randomSource;
+}
+
 GameData &Game::getGameData() {
 	return *_gameData;
 }
@@ -222,11 +227,11 @@ uint8 Game::colorFromString(const char *colorStr) {
 	return 0x00;
 }
 
-TaskManager& Game::getTaskManager() {
+TaskManager &Game::getTaskManager() {
 	return _taskManager;
 }
 
-Assets& Game::getAssets() {
+Assets &Game::getAssets() {
 	return _assets;
 }
 
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 056cffb..d70bd09 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -23,6 +23,7 @@
 #ifndef MUTATIONOFJB_GAME_H
 #define MUTATIONOFJB_GAME_H
 
+#include "common/random.h"
 #include "common/scummsys.h"
 #include "mutationofjb/assets.h"
 #include "mutationofjb/gui.h"
@@ -47,6 +48,7 @@ class Bitmap;
 class Game {
 public:
 	Game(MutationOfJBEngine *vm);
+	Common::RandomSource &getRandomSource();
 	GameData &getGameData();
 	Room &getRoom();
 	Script *getGlobalScript() const;
@@ -68,7 +70,7 @@ public:
 
 	static uint8 colorFromString(const char *colorStr);
 
-	TaskManager& getTaskManager();
+	TaskManager &getTaskManager();
 	Assets &getAssets();
 
 	Graphics::Screen &getScreen();
@@ -80,6 +82,7 @@ private:
 	Script *changeSceneLoadScript(uint8 sceneId, bool partB);
 
 	MutationOfJBEngine *_vm;
+	Common::RandomSource _randomSource;
 
 	GameData *_gameData;
 	Script *_globalScript;
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index f3a1d78..2ac4fab 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS := \
 	commands/saycommand.o \
 	commands/seqcommand.o \
 	commands/talkcommand.o \
+	commands/randomcommand.o \
 	tasks/conversationtask.o \
 	tasks/saytask.o \
 	tasks/taskmanager.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 90146f6..d90f37e 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -45,6 +45,7 @@
 #include "mutationofjb/commands/renamecommand.h"
 #include "mutationofjb/commands/definestructcommand.h"
 #include "mutationofjb/commands/talkcommand.h"
+#include "mutationofjb/commands/randomcommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -71,6 +72,8 @@ static CommandParser **getParsers() {
 		new NewRoomCommandParser,
 		new GotoCommandParser,
 		new LabelCommandParser,
+		new RandomCommandParser,
+		new RandomBlockStartParser,
 		nullptr
 	};
 
@@ -81,7 +84,8 @@ static CommandParser **getParsers() {
 ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) :
 	_stream(stream),
 	_currentCommand(nullptr),
-	_lastCommand(nullptr)
+	_lastCommand(nullptr),
+	_pendingRandomCommand(nullptr)
 {}
 
 bool ScriptParseContext::readLine(Common::String &line) {
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 8a15a48..3ef25f4 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -43,6 +43,7 @@ class GameData;
 class GotoCommand;
 class ConditionalCommand;
 class Script;
+class RandomCommand;
 typedef Common::Array<Command *> Commands;
 
 
@@ -92,6 +93,8 @@ public:
 	typedef Common::HashMap<Common::String, GotoCommands> PendingGotoMap;
 	PendingGotoMap _pendingGotos;
 
+	RandomCommand *_pendingRandomCommand;
+
 	ActionInfos _actionInfos;
 	Macros _macros;
 	Startups _startups;


Commit: 296835f84ae2b0c54882b8de19506989a3cf1fc4
    https://github.com/scummvm/scummvm/commit/296835f84ae2b0c54882b8de19506989a3cf1fc4
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Draw object animations on map scene.

Changed paths:
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 5577077..009c4be 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -137,13 +137,13 @@ void Room::redraw() {
 	if (!_game->isCurrentSceneMap()) {
 		Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT);
 		_screen->blitFrom(_background.rawSurface(), rect, Common::Point(0, 0));
+	}
 
-		Scene *const currentScene = _game->getGameData().getCurrentScene();
-		for (int i = 0; i < currentScene->getNoObjects(); ++i) {
-			Object *const obj = currentScene->getObject(i + 1);
-			if (obj->_AC) {
-				drawObjectAnimation(i + 1, 0);
-			}
+	Scene *const currentScene = _game->getGameData().getCurrentScene();
+	for (int i = 0; i < currentScene->getNoObjects(); ++i) {
+		Object *const obj = currentScene->getObject(i + 1);
+		if (obj->_AC) {
+			drawObjectAnimation(i + 1, 0);
 		}
 	}
 }


Commit: d22da95282fea56caef5066331dc7d921b079811
    https://github.com/scummvm/scummvm/commit/d22da95282fea56caef5066331dc7d921b079811
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix multiple RANDOM commands in one script.

Changed paths:
    engines/mutationofjb/commands/randomcommand.cpp


diff --git a/engines/mutationofjb/commands/randomcommand.cpp b/engines/mutationofjb/commands/randomcommand.cpp
index ff03e96..b9cc303 100644
--- a/engines/mutationofjb/commands/randomcommand.cpp
+++ b/engines/mutationofjb/commands/randomcommand.cpp
@@ -74,8 +74,13 @@ bool RandomBlockStartParser::parse(const Common::String &line, ScriptParseContex
 }
 
 void RandomBlockStartParser::transition(ScriptParseContext &parseCtx, Command *, Command *newCommand, CommandParser *) {
-	if (newCommand && parseCtx._pendingRandomCommand) {
-		parseCtx._pendingRandomCommand->_choices.push_back(newCommand);
+	RandomCommand *randomCommand = parseCtx._pendingRandomCommand;
+	if (newCommand && randomCommand) {
+		randomCommand->_choices.push_back(newCommand);
+
+		if (randomCommand->_choices.size() == randomCommand->_numChoices) {
+			parseCtx._pendingRandomCommand = nullptr;
+		}
 	}
 }
 


Commit: d358a65bbc57ab9099620bf2309893f99dbf164c
    https://github.com/scummvm/scummvm/commit/d358a65bbc57ab9099620bf2309893f99dbf164c
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Run extra sections from conversation.

Changed paths:
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/conversationlinelist.cpp
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/conversationtask.h


diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 5fcccd3..b883bee 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -35,6 +35,7 @@
 	("#U " | "-U ") <object1> [<object2>]
 	("#ELSE" | "-ELSE") [<tag>]
 	"#MACRO " <name>
+	"#EXTRA" <name>
 
 	If a line starts with '#', '=', '-', it is treated as the end of a section.
 	However, at the same time it can also start a new section depending on what follows.
@@ -46,6 +47,8 @@
 	#ELSE is used by conditional commands (see comments for IfCommand and others).
 
 	#MACRO starts a new macro. Global script can call macros from local script and vice versa.
+
+	#EXTRA defines an "extra" section. This is called from dialog responses ("TALK TO HIM" command).
 */
 
 namespace MutationOfJB {
@@ -119,6 +122,9 @@ bool EndBlockCommandParser::parse(const Common::String &line, ScriptParseContext
 		const uint8 startupId = atoi(line.c_str() + 9);
 		IdAndCommand ic = {startupId, command};
 		_foundStartups.push_back(ic);
+	} else if (line.size() >= 7 && line.hasPrefix("#EXTRA")) {
+		NameAndCommand nc = {line.c_str() + 6, command};
+		_foundExtras.push_back(nc);
 	}
 
 	if (firstChar == '#') {
@@ -183,6 +189,23 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *ol
 			}
 		}
 	}
+	if (!_foundExtras.empty()) {
+		if (newCommand) {
+			for (NameAndCommandArray::iterator it = _foundExtras.begin(); it != _foundExtras.end();) {
+				if (it->_command != oldCommand) {
+					it++;
+					continue;
+				}
+
+				if (!parseCtx._extras.contains(it->_name)) {
+					parseCtx._extras[it->_name] = newCommand;
+				} else {
+					warning(_("Extra '%s' already exists"), it->_name.c_str());
+				}
+				it = _foundExtras.erase(it);
+			}
+		}
+	}
 
 	if (newCommandParser != this) {
 		if (!_pendingActionInfos.empty()) {
@@ -208,9 +231,13 @@ void EndBlockCommandParser::finish(ScriptParseContext &) {
 	if (!_foundStartups.empty()) {
 		debug("Problem: Found startups from end block parser is not empty!");
 	}
+	if (!_foundExtras.empty()) {
+		debug("Problem: Found extras from end block parser is not empty!");
+	}
 	_pendingActionInfos.clear();
 	_foundMacros.clear();
 	_foundStartups.clear();
+	_foundExtras.clear();
 }
 
 Command::ExecuteResult EndBlockCommand::execute(ScriptExecutionContext &scriptExecCtx) {
diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 4ca46dd..55656aa 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -56,6 +56,7 @@ private:
 	typedef Common::Array<IdAndCommand> IdAndCommandArray;
 	NameAndCommandArray _foundMacros;
 	IdAndCommandArray _foundStartups;
+	NameAndCommandArray _foundExtras;
 };
 
 class EndBlockCommand : public Command {
diff --git a/engines/mutationofjb/conversationlinelist.cpp b/engines/mutationofjb/conversationlinelist.cpp
index 562c2d0..3164ff5 100644
--- a/engines/mutationofjb/conversationlinelist.cpp
+++ b/engines/mutationofjb/conversationlinelist.cpp
@@ -56,8 +56,8 @@ bool ConversationLineList::parseFile(const Common::String &fileName) {
 
 		Common::String::iterator endIt = Common::find(lineStr.begin(), lineStr.end(), '|');
 		if (endIt != lineStr.end()) {
-			Common::String extra = lineStr + endIt;
-			if (*endIt == 'X') {
+			endIt++;
+			if (endIt != lineStr.end() && *endIt == 'X') {
 				line._extra = Common::String(endIt + 1, lineStr.end()); // Skip 'X' char.
 			}
 		}
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index e7f534e..986b00d 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -202,6 +202,7 @@ Common::Error MutationOfJBEngine::run() {
 					_game->setCurrentAction(ActionInfo::PickUp);
 					break;
 				}
+				break;
 			}
 			default:
 				break;
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index d90f37e..39c4859 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -189,6 +189,23 @@ Command *ScriptExecutionContext::getMacro(const Common::String &name) const {
 	return cmd;
 }
 
+Command *ScriptExecutionContext::getExtra(const Common::String &name) const {
+	Command *cmd = nullptr;
+
+	Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
+	Script *const globalScript = _game.getGlobalScript();
+
+	if (localScript) {
+		cmd = localScript->getExtra(name);
+	}
+
+	if (!cmd && globalScript) {
+		cmd = globalScript->getExtra(name);
+	}
+
+	return cmd;
+}
+
 bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 	destroy();
 
@@ -236,6 +253,7 @@ bool Script::loadFromStream(Common::SeekableReadStream &stream) {
 
 	_macros = parseCtx._macros;
 	_startups = parseCtx._startups;
+	_extras = parseCtx._extras;
 
 	return true;
 }
@@ -285,4 +303,13 @@ Command *Script::getStartup(uint8 startupId) const {
 	return it->_value;
 }
 
+Command *Script::getExtra(const Common::String &name) const {
+	Extras::const_iterator it = _extras.find(name);
+	if (it == _extras.end()) {
+		return nullptr;
+	}
+
+	return it->_value;
+}
+
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 3ef25f4..28e0e98 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -67,6 +67,7 @@ typedef Common::Array<ActionInfo> ActionInfos;
 typedef Common::Array<GotoCommand *> GotoCommands;
 typedef Common::HashMap<Common::String, Command *> Macros;
 typedef Common::HashMap<uint8, Command *> Startups;
+typedef Common::HashMap<Common::String, Command *> Extras;
 
 class ScriptParseContext {
 public:
@@ -98,6 +99,7 @@ public:
 	ActionInfos _actionInfos;
 	Macros _macros;
 	Startups _startups;
+	Extras _extras;
 
 private:
 };
@@ -116,6 +118,7 @@ public:
 	Game &getGame();
 	GameData &getGameData();
 	Command *getMacro(const Common::String &name) const;
+	Command *getExtra(const Common::String &name) const;
 
 private:
 	Game &_game;
@@ -135,6 +138,7 @@ public:
 	const Startups &getStartups() const;
 	Command *getMacro(const Common::String &name) const;
 	Command *getStartup(uint8 startupId) const;
+	Command *getExtra(const Common::String &name) const;
 
 private:
 	void destroy();
@@ -142,6 +146,7 @@ private:
 	ActionInfos _actionInfos[5];
 	Macros _macros;
 	Startups _startups;
+	Extras _extras;
 };
 
 }
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 512b38b..47f27df 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -27,11 +27,14 @@
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/gui.h"
+#include "mutationofjb/script.h"
 #include "mutationofjb/tasks/saytask.h"
 #include "mutationofjb/tasks/taskmanager.h"
 #include "mutationofjb/util.h"
 #include "mutationofjb/widgets/conversationwidget.h"
 
+#include "common/translation.h"
+
 namespace MutationOfJB {
 
 void ConversationTask::start() {
@@ -69,11 +72,11 @@ void ConversationTask::update() {
 				break;
 			}
 			case SAYING_RESPONSE: {
-				if (_currentItem->_nextLineIndex == 0) {
-					finish();
-				} else {
-					_currentLineIndex = _currentItem->_nextLineIndex - 1;
-					showChoicesOrPick();
+				startExtra();
+
+				if (_substate != RUNNING_EXTRA)
+				{
+					gotoNextLine();
 				}
 				break;
 			}
@@ -82,6 +85,16 @@ void ConversationTask::update() {
 			}
 		}
 	}
+
+	if (_innerExecCtx) {
+		Command::ExecuteResult res = _innerExecCtx->runActiveCommand();
+		if (res == Command::Finished) {
+			delete _innerExecCtx;
+			_innerExecCtx = nullptr;
+
+			gotoNextLine();
+		}
+	}
 }
 
 void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) {
@@ -203,4 +216,35 @@ void ConversationTask::finish() {
 	game.getGui().markDirty(); // TODO: Handle automatically when changing visibility.
 }
 
+void ConversationTask::startExtra() {
+	const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList();
+	const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
+	if (!line->_extra.empty()) {
+		_innerExecCtx = new ScriptExecutionContext(getTaskManager()->getGame());
+		Command *const extraCmd = _innerExecCtx->getExtra(line->_extra);
+		if (extraCmd) {
+			Command::ExecuteResult res = _innerExecCtx->startCommand(extraCmd);
+			if (res == Command::InProgress) {
+				_substate = RUNNING_EXTRA;
+			} else {
+				delete _innerExecCtx;
+				_innerExecCtx = nullptr;
+			}
+		} else {
+			warning(_("Extra '%s' not found"), line->_extra.c_str());
+			delete _innerExecCtx;
+			_innerExecCtx = nullptr;
+		}
+	}
+}
+
+void ConversationTask::gotoNextLine() {
+	if (_currentItem->_nextLineIndex == 0) {
+		finish();
+	} else {
+		_currentLineIndex = _currentItem->_nextLineIndex - 1;
+		showChoicesOrPick();
+	}
+}
+
 }
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
index 3bcbb6f..41c8284 100644
--- a/engines/mutationofjb/tasks/conversationtask.h
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -27,10 +27,11 @@
 namespace MutationOfJB {
 
 class SayTask;
+class ScriptExecutionContext;
 
 class ConversationTask : public Task, public ConversationWidgetCallback {
 public:
-	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo) : _sceneId(sceneId), _convInfo(convInfo), _currentLineIndex(0), _currentItem(nullptr), _sayTask(nullptr), _substate(IDLE), _haveChoices(false) {}
+	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo) : _sceneId(sceneId), _convInfo(convInfo), _currentLineIndex(0), _currentItem(nullptr), _sayTask(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
 	virtual ~ConversationTask() {}
 
 	virtual void start() override;
@@ -41,6 +42,8 @@ private:
 	void showChoicesOrPick();
 	const ConversationInfo::Line *getCurrentLine() const;
 	void finish();
+	void startExtra();
+	void gotoNextLine();
 
 	uint8 _sceneId;
 	const ConversationInfo &_convInfo;
@@ -52,11 +55,13 @@ private:
 		IDLE,
 		SAYING_CHOICE,
 		SAYING_RESPONSE,
-		SAYING_NO_CHOICES
+		SAYING_NO_CHOICES,
+		RUNNING_EXTRA
 	};
 
 	Substate _substate;
 	bool _haveChoices;
+	ScriptExecutionContext *_innerExecCtx;
 };
 
 }


Commit: eaba12cecdb8c062093559af9d3caee6535059d2
    https://github.com/scummvm/scummvm/commit/eaba12cecdb8c062093559af9d3caee6535059d2
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Use the vanilla cursor.

Changed paths:
    engines/mutationofjb/mutationofjb.cpp


diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 986b00d..b535e19 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -54,12 +54,29 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 
 void MutationOfJBEngine::setupCursor() {
 	const uint8 white[] = {0xFF, 0xFF, 0xFF};
-	const uint8 cursor[] = {0xFF};
+
+	const uint8 cursor[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	};
 
 	_screen->setPalette(white, 0xFF, 1);
 
 	CursorMan.disableCursorPalette(true);
-	CursorMan.pushCursor(cursor, 1, 1, 0, 0, 0);
+	CursorMan.pushCursor(cursor, 15, 15, 7, 7, 0);
 	CursorMan.showMouse(true);
 }
 


Commit: 2cd1728f42664643b1bb20f76b5350ef40401786
    https://github.com/scummvm/scummvm/commit/2cd1728f42664643b1bb20f76b5350ef40401786
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add support for repeating choices.

Changed paths:
    engines/mutationofjb/tasks/conversationtask.cpp


diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 47f27df..d675b2e 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -102,11 +102,16 @@ void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint
 	convWidget->clearChoices();
 
 	const ConversationLineList& toSayList = getTaskManager()->getGame().getAssets().getToSayList();
-	_sayTask = new SayTask(toSayList.getLine(item._choice)->_speeches[0]._text, _convInfo._color);
+	const ConversationLineList::Speech &speech = toSayList.getLine(item._choice)->_speeches[0];
+
+	_sayTask = new SayTask(speech._text, _convInfo._color);
 	getTaskManager()->addTask(_sayTask);
 	_substate = SAYING_CHOICE;
 	_currentItem = &item;
-	getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1);
+
+	if (!speech.isRepeating()) {
+		getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1);
+	}
 }
 
 void ConversationTask::showChoicesOrPick() {
@@ -122,8 +127,10 @@ void ConversationTask::showChoicesOrPick() {
 		Collect valid "to say" choices (not exhausted and not empty).
 		Collect valid responses (not exhausted and not empty).
 		If there are at least two visible choices, we show them.
-		If there is just one visible choice, pick it automatically.
+		If there is just one visible choice, pick it automatically ONLY if this is not the first choice in this conversation.
+		Otherwise we don't start the conversation.
 		If there are no visible choices, automatically pick first valid response.
+		If nothing above applies, don't start the conversation.
 	*/
 
 	const ConversationInfo::Line *const currentLine = getCurrentLine();
@@ -164,7 +171,7 @@ void ConversationTask::showChoicesOrPick() {
 		_currentItem = nullptr;
 
 		_haveChoices = true;
-	} else if (itemsWithValidChoices.size() == 1) {
+	} else if (itemsWithValidChoices.size() == 1 && _haveChoices) {
 		const ConversationLineList& toSayList = game.getAssets().getToSayList();
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()];
 		const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
@@ -174,10 +181,12 @@ void ConversationTask::showChoicesOrPick() {
 		_substate = SAYING_CHOICE;
 		_currentItem = &item;
 
-		game.getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, itemsWithValidChoices.front() + 1, _currentLineIndex + 1);
+		if (!line->_speeches[0].isRepeating()) {
+			game.getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, itemsWithValidChoices.front() + 1, _currentLineIndex + 1);
+		}
 
 		_haveChoices = true;
-	} else if (!itemsWithValidResponses.empty()) {
+	} else if (!itemsWithValidResponses.empty() && _haveChoices) {
 		const ConversationLineList& responseList = game.getAssets().getResponseList();
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()];
 		const ConversationLineList::Line *const line = responseList.getLine(item._response);
@@ -188,7 +197,7 @@ void ConversationTask::showChoicesOrPick() {
 		_currentItem = &item;
 
 		_haveChoices = true;
-	} else if (!itemsWithValidNext.empty()) {
+	} else if (!itemsWithValidNext.empty() && _haveChoices) {
 		_currentLineIndex = currentLine->_items[itemsWithValidNext.front()]._nextLineIndex - 1;
 		showChoicesOrPick();
 	} else {


Commit: 2ee0a900598caa2e91b7261eb886793b74f9e25e
    https://github.com/scummvm/scummvm/commit/2ee0a900598caa2e91b7261eb886793b74f9e25e
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Change cursor color when it's under entity.

Changed paths:
    engines/mutationofjb/game.cpp
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h


diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 190b62e..0cb6f53 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -57,8 +57,6 @@ Game::Game(MutationOfJBEngine *vm)
 	_room = new Room(this, _vm->getScreen());
 
 	_gui.init();
-
-	changeScene(13, false); // Initial scene.
 }
 
 Common::RandomSource &Game::getRandomSource() {
@@ -126,6 +124,8 @@ Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) {
 	localScript->loadFromStream(scriptFile);
 	scriptFile.close();
 
+	_vm->updateCursor();
+
 	return localScript;
 }
 
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index b535e19..cdaa037 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -43,7 +43,8 @@ MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 : Engine(syst),
  _console(nullptr),
  _screen(nullptr),
- _mapObjectId(0) {
+ _mapObjectId(0),
+ _cursorState(CURSOR_IDLE) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -53,8 +54,6 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 
 
 void MutationOfJBEngine::setupCursor() {
-	const uint8 white[] = {0xFF, 0xFF, 0xFF};
-
 	const uint8 cursor[] = {
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -73,13 +72,24 @@ void MutationOfJBEngine::setupCursor() {
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	};
 
-	_screen->setPalette(white, 0xFF, 1);
+	updateCursorPalette();
 
 	CursorMan.disableCursorPalette(true);
 	CursorMan.pushCursor(cursor, 15, 15, 7, 7, 0);
 	CursorMan.showMouse(true);
 }
 
+void MutationOfJBEngine::updateCursorPalette() {
+	if (_cursorState == CURSOR_OFF) {
+		return;
+	}
+
+	const uint8 white[] = {0xFF, 0xFF, 0xFF};
+	const uint8 blue[] = {0x00, 0xFF, 0xC3};
+
+	_screen->setPalette(_cursorState == CURSOR_ACTIVE ? blue : white, 0xFF, 1);
+}
+
 Graphics::Screen *MutationOfJBEngine::getScreen() const {
 	return _screen;
 }
@@ -88,12 +98,36 @@ Game &MutationOfJBEngine::getGame() {
 	return *_game;
 }
 
+void MutationOfJBEngine::setCursorState(CursorState cursorState) {
+	if (_cursorState == cursorState) {
+		return;
+	}
+
+	_cursorState = cursorState;
+	updateCursorPalette();
+}
+
+void MutationOfJBEngine::updateCursor() {
+	if (_cursorState == CURSOR_OFF) {
+		return;
+	}
+
+	if (_game->isCurrentSceneMap()) {
+		if (_cursorState != CURSOR_IDLE) {
+			_cursorState = CURSOR_IDLE;
+			updateCursorPalette();
+		}
+	} else {
+		const Common::Point point = _eventMan->getMousePos();
+		updateCursorHitTest(point.x, point.y);
+	}
+}
+
 void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
 	Scene *const scene = _game->getGameData().getCurrentScene();
 
 	switch (event.type) {
-	case Common::EVENT_LBUTTONDOWN:
-	{
+	case Common::EVENT_LBUTTONDOWN: {
 		const int16 x = event.mouse.x;
 		const int16 y = event.mouse.y;
 
@@ -108,6 +142,15 @@ void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
 		}
 		break;
 	}
+	case Common::EVENT_MOUSEMOVE: {
+		const int16 x = event.mouse.x;
+		const int16 y = event.mouse.y;
+
+		if (_cursorState != CURSOR_OFF) {
+			updateCursorHitTest(x, y);
+		}
+		break;
+	}
 	default:
 		break;
 	}
@@ -126,8 +169,7 @@ void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 	Scene *const scene = _game->getGameData().getCurrentScene();
 
 	switch (event.type) {
-	case Common::EVENT_LBUTTONDOWN:
-	{
+	case Common::EVENT_LBUTTONDOWN: {
 		const int16 x = event.mouse.x;
 		const int16 y = event.mouse.y;
 
@@ -140,8 +182,7 @@ void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 		}
 		break;
 	}
-	case Common::EVENT_MOUSEMOVE:
-	{
+	case Common::EVENT_MOUSEMOVE: {
 		const int16 x = event.mouse.x;
 		const int16 y = event.mouse.y;
 
@@ -177,6 +218,33 @@ void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 	}
 }
 
+void MutationOfJBEngine::updateCursorHitTest(int16 x, int16 y) {
+	Scene *const scene = _game->getGameData().getCurrentScene();
+	if (!scene) {
+		return;
+	}
+
+	bool entityHit = false;
+	if (Door *const door = scene->findDoor(x, y)) {
+		if (door->_destSceneId != 0) {
+			entityHit = true;
+		}
+	} else if (Static *const stat = scene->findStatic(x, y)) {
+		if (stat->_active == 1) {
+			entityHit = true;
+		}
+	}
+	bool cursorPaletteChange = false;
+	if ((_cursorState == CURSOR_ACTIVE && !entityHit) || (_cursorState == CURSOR_IDLE && entityHit)) {
+		cursorPaletteChange = true;
+	}
+	_cursorState = entityHit ? CURSOR_ACTIVE : CURSOR_IDLE;
+	if (cursorPaletteChange) {
+		updateCursorPalette();
+	}
+
+}
+
 Common::Error MutationOfJBEngine::run() {
 	debug("MutationOfJBEngine::run");
 
@@ -188,20 +256,20 @@ Common::Error MutationOfJBEngine::run() {
 
 	setupCursor();
 
+	_game->changeScene(13, false); // Initial scene.
+
 	while (!shouldQuit()) {
 		Common::Event event;
 		while (_eventMan->pollEvent(event)) {
 			switch (event.type) {
-			case Common::EVENT_KEYDOWN:
-			{
+			case Common::EVENT_KEYDOWN: {
 				if ((event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) ||
 						event.kbd.ascii == '~' || event.kbd.ascii == '#') {
 					_console->attach();
 				}
 				break;
 			}
-			case Common::EVENT_KEYUP:
-			{
+			case Common::EVENT_KEYUP: {
 				switch (event.kbd.ascii) {
 				case 'g':
 					_game->setCurrentAction(ActionInfo::Walk);
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 348165f..050800e 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -41,16 +41,26 @@ class Game;
 
 class MutationOfJBEngine : public Engine {
 public:
+	enum CursorState {
+		CURSOR_OFF,
+		CURSOR_IDLE,
+		CURSOR_ACTIVE
+	};
+
 	MutationOfJBEngine(OSystem *syst);
 	~MutationOfJBEngine();
 
 	virtual Common::Error run();
 	Graphics::Screen *getScreen() const;
 	Game &getGame();
+	void setCursorState(CursorState cursorState);
+	void updateCursor();
 
 private:
 	bool loadGameData(bool partB);
 	void setupCursor();
+	void updateCursorHitTest(int16 x, int16 y);
+	void updateCursorPalette();
 	void handleNormalScene(const Common::Event &event);
 	void handleMapScene(const Common::Event &event);
 
@@ -58,6 +68,8 @@ private:
 	Graphics::Screen *_screen;
 	Game *_game;
 	uint8 _mapObjectId;
+
+	CursorState _cursorState;
 };
 
 


Commit: 74ef0d9cfe2106cd0e4286ccd3e829c2edc00f98
    https://github.com/scummvm/scummvm/commit/74ef0d9cfe2106cd0e4286ccd3e829c2edc00f98
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Correctly handle empty animation frames.

Changed paths:
    engines/mutationofjb/animationdecoder.cpp


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
index 5b20153..c88a9e0 100644
--- a/engines/mutationofjb/animationdecoder.cpp
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -61,34 +61,38 @@ bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
 
 		// Subrecords.
 		if (recordId == 0xF1FA) {
-			for (int i = 0; i < subrecords; ++i) {
-				int32 filePos = file.pos();
-
-				const uint32 subLength = file.readUint32LE();
-				const uint16 type = file.readUint16LE();
-
-				if (type == 0x0B) {
-					loadPalette(file);
-					if (callback) {
-						callback->onPaletteUpdated(_palette);
-					}
-				} else if (type == 0x0F) {
-					loadFullFrame(file, subLength - 6);
-					if (callback) {
-						callback->onFrame(frameNo, _surface);
-					}
-				} else if (type == 0x0C) {
-					loadDiffFrame(file, subLength - 6);
-					if (callback) {
-						callback->onFrame(frameNo, _surface);
+			if (subrecords == 0) {
+				callback->onFrame(frameNo, _surface); // Empty record, frame identical to the previous one.
+			} else {
+				for (int i = 0; i < subrecords; ++i) {
+					int32 filePos = file.pos();
+
+					const uint32 subLength = file.readUint32LE();
+					const uint16 type = file.readUint16LE();
+
+					if (type == 0x0B) {
+						loadPalette(file);
+						if (callback) {
+							callback->onPaletteUpdated(_palette);
+						}
+					} else if (type == 0x0F) {
+						loadFullFrame(file, subLength - 6);
+						if (callback) {
+							callback->onFrame(frameNo, _surface);
+						}
+					} else if (type == 0x0C) {
+						loadDiffFrame(file, subLength - 6);
+						if (callback) {
+							callback->onFrame(frameNo, _surface);
+						}
+					} else {
+						debug(_("Unsupported record type %02X."), type);
+						file.seek(subLength - 6, SEEK_CUR);
 					}
-				} else {
-					debug(_("Unsupported record type %02X."), type);
-					file.seek(subLength - 6, SEEK_CUR);
-				}
 
-				// Makes decoding more robust, because for some reason records might have extra data at the end.
-				file.seek(filePos + subLength, SEEK_SET);
+					// Makes decoding more robust, because for some reason records might have extra data at the end.
+					file.seek(filePos + subLength, SEEK_SET);
+				}
 			}
 			frameNo++;
 		} else {


Commit: f70eb01061d3e5dd74ed8065d0435a4273d973dc
    https://github.com/scummvm/scummvm/commit/f70eb01061d3e5dd74ed8065d0435a4273d973dc
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Add null check.

Changed paths:
    engines/mutationofjb/animationdecoder.cpp


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
index c88a9e0..8132385 100644
--- a/engines/mutationofjb/animationdecoder.cpp
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -62,7 +62,9 @@ bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
 		// Subrecords.
 		if (recordId == 0xF1FA) {
 			if (subrecords == 0) {
-				callback->onFrame(frameNo, _surface); // Empty record, frame identical to the previous one.
+				if (callback) {
+					callback->onFrame(frameNo, _surface); // Empty record, frame identical to the previous one.
+				}
 			} else {
 				for (int i = 0; i < subrecords; ++i) {
 					int32 filePos = file.pos();


Commit: f94ff7aa8ec510de8dc353c9c2e079db5e05d5da
    https://github.com/scummvm/scummvm/commit/f94ff7aa8ec510de8dc353c9c2e079db5e05d5da
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement word wrapping for subtitles.

Changed paths:
    engines/mutationofjb/font.cpp
    engines/mutationofjb/font.h
    engines/mutationofjb/tasks/saytask.cpp
    engines/mutationofjb/tasks/saytask.h


diff --git a/engines/mutationofjb/font.cpp b/engines/mutationofjb/font.cpp
index 235c60a..4350b19 100644
--- a/engines/mutationofjb/font.cpp
+++ b/engines/mutationofjb/font.cpp
@@ -27,9 +27,9 @@
 
 namespace MutationOfJB {
 
-Font::Font(const Common::String &fileName, int horizSpacing, int vertSpacing) :
+Font::Font(const Common::String &fileName, int horizSpacing, int lineHeight) :
 	_horizSpacing(horizSpacing),
-	_vertSpacing(vertSpacing) {
+	_lineHeight(lineHeight) {
 
 	load(fileName);
 }
@@ -67,15 +67,14 @@ bool Font::load(const Common::String &fileName) {
 		}
 	}
 
-	if (_vertSpacing == -1) {
-		_vertSpacing = maxHeight;
+	if (_lineHeight == -1) {
+		_lineHeight = maxHeight;
 	}
 
 	return true;
 }
 
-
-void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) {
+void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) const {
 	GlyphMap::iterator it = _glyphs.find(glyph);
 	if (it == _glyphs.end()) {
 		warning("Glyph %d not found", glyph);
@@ -99,13 +98,54 @@ void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics:
 	x += glyphSurface.w + _horizSpacing;
 }
 
-void Font::drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) {
+int16 Font::getWidth(const Common::String &str) const {
+	int16 width = 0;
+	for (uint i = 0; i < str.size(); ++i) {
+		GlyphMap::iterator it = _glyphs.find(str[i]);
+		if (it == _glyphs.end()) {
+			continue;
+		}
+
+		width += it->_value.w + _horizSpacing;
+	}
+	return width;
+}
+
+int Font::getLineHeight() const {
+	return _lineHeight;
+}
+
+void Font::drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) const {
 	for (uint i = 0; i < str.size(); ++i) {
 		drawGlyph(str[i], baseColor, x, y, surf); // "x" is updated.
 	}
 }
 
-uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) {
+void Font::wordWrap(const Common::String &str, int16 maxLineWidth, Common::Array<Common::String> &lines) const {
+	lines.push_back("");
+
+	for (Common::String::const_iterator it = str.begin(); it != str.end();) {
+		Common::String::const_iterator partStart = it;
+		it = Common::find(partStart, str.end(), ' ');
+		if (it != str.end()) {
+			while (*it == ' ' && it != str.end()) {
+				++it;
+			}
+		}
+
+		Common::String part(partStart, it); // Word + following whitespace
+		Common::String line = lines.back() + part;
+		if (getWidth(line) <= maxLineWidth) {
+			// The part fits in the current line
+			lines.back() = line;
+		} else {
+			// The part must go to the next line
+			lines.push_back(part);
+		}
+	}
+}
+
+uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) const {
 	return baseColor + glyphColor - 0x10;
 }
 
@@ -113,7 +153,7 @@ SystemFont::SystemFont() : Font("sysfnt.aft", 1, 7) {}
 
 SpeechFont::SpeechFont() : Font("font1.aft", -1, -1) {}
 
-uint8 SpeechFont::transformColor(uint8 baseColor, uint8 glyphColor) {
+uint8 SpeechFont::transformColor(uint8 baseColor, uint8 glyphColor) const {
 	// Hack in original game.
 	if (glyphColor == 0x11) {
 		return 0xC0;
diff --git a/engines/mutationofjb/font.h b/engines/mutationofjb/font.h
index 5aa6a4f..a27303e 100644
--- a/engines/mutationofjb/font.h
+++ b/engines/mutationofjb/font.h
@@ -37,17 +37,20 @@ class Font {
 public:
 	Font(const Common::String &fileName, int horizSpacing, int vertSpacing);
 	virtual ~Font() {}
-	void drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf);
+	int getLineHeight() const;
+	int16 getWidth(const Common::String &text) const;
+	void drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) const;
+	void wordWrap(const Common::String &str, int16 maxLineWidth, Common::Array<Common::String> &lines) const;
 
 protected:
-	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor);
+	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) const;
 
 private:
-	void drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf);
+	void drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) const;
 	bool load(const Common::String &fileName);
 
 	int _horizSpacing;
-	int _vertSpacing;
+	int _lineHeight;
 	typedef Common::HashMap<uint8, Graphics::ManagedSurface> GlyphMap;
 	GlyphMap _glyphs;
 };
@@ -62,7 +65,7 @@ public:
 	SpeechFont();
 
 protected:
-	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) override;
+	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) const override;
 };
 
 }
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
index 2ef4cdf..fcef6c3 100644
--- a/engines/mutationofjb/tasks/saytask.cpp
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -27,6 +27,7 @@
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/room.h"
+#include "mutationofjb/util.h"
 
 #include "graphics/managed_surface.h"
 #include "graphics/screen.h"
@@ -36,8 +37,7 @@ namespace MutationOfJB {
 SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(1000) {}
 
 void SayTask::start() {
-
-	getTaskManager()->getGame().getAssets().getSpeechFont().drawString(_toSay, _color, 0, 0, getTaskManager()->getGame().getScreen());
+	drawSubtitle(_toSay, 160, 0, _color); // TODO: Respect PTALK and LTALK commands.
 	_timer.start();
 	setState(RUNNING);
 }
@@ -52,4 +52,39 @@ void SayTask::update() {
 	}
 }
 
+void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color) {
+	const int MAX_LINE_WIDTH = 250;
+
+	const Font &font = getTaskManager()->getGame().getAssets().getSpeechFont();
+
+	Common::Array<Common::String> lines;
+	font.wordWrap(text, MAX_LINE_WIDTH, lines);
+
+	int16 x = talkX;
+	int16 y = talkY - (lines.size() - 1) * font.getLineHeight() - 15; // Get the top y
+
+	// Clamp to screen edges
+	y = MAX<int16>(y, 3);
+	int16 maxWidth = 0;
+	for (uint i = 0; i < lines.size(); i++) {
+		int16 lineWidth = font.getWidth(lines[i]);
+		if (lineWidth > maxWidth) {
+			maxWidth = lineWidth;
+		}
+		x = MAX<int16>(x, 3 + lineWidth / 2);
+		x = MIN<int16>(x, 317 - lineWidth / 2);
+	}
+
+	// Draw lines
+	for (uint i = 0; i < lines.size(); i++) {
+		font.drawString(lines[i], color, x - font.getWidth(lines[i]) / 2, y + i * font.getLineHeight(), getTaskManager()->getGame().getScreen());
+	}
+
+	// Remember the area occupied by the text
+	_boundingBox.top = x - maxWidth / 2;
+	_boundingBox.left = y;
+	_boundingBox.setWidth(maxWidth);
+	_boundingBox.setHeight(lines.size() * font.getLineHeight());
+}
+
 }
diff --git a/engines/mutationofjb/tasks/saytask.h b/engines/mutationofjb/tasks/saytask.h
index a7ac96d..ef66f5b 100644
--- a/engines/mutationofjb/tasks/saytask.h
+++ b/engines/mutationofjb/tasks/saytask.h
@@ -24,6 +24,7 @@
 
 #include "mutationofjb/timer.h"
 
+#include "common/rect.h"
 #include "common/str.h"
 
 namespace MutationOfJB {
@@ -36,9 +37,12 @@ public:
 	virtual void update() override;
 
 private:
+	void drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color);
+
 	Common::String _toSay;
 	uint8 _color;
 	Timer _timer;
+	Common::Rect _boundingBox;
 };
 
 }


Commit: cda1f0dd3a553dbd4480b87a054d388c98740585
    https://github.com/scummvm/scummvm/commit/cda1f0dd3a553dbd4480b87a054d388c98740585
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Animate objects.

Changed paths:
  A engines/mutationofjb/tasks/objectanimationtask.cpp
  A engines/mutationofjb/tasks/objectanimationtask.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/module.mk
    engines/mutationofjb/tasks/saytask.cpp
    engines/mutationofjb/tasks/saytask.h
    engines/mutationofjb/timer.cpp
    engines/mutationofjb/timer.h


diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 0cb6f53..652336f 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -21,16 +21,19 @@
  */
 
 #include "mutationofjb/game.h"
-#include "mutationofjb/gamedata.h"
+
+#include "mutationofjb/commands/command.h"
 #include "mutationofjb/encryptedfile.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/mutationofjb.h"
 #include "mutationofjb/room.h"
 #include "mutationofjb/script.h"
+#include "mutationofjb/tasks/objectanimationtask.h"
 #include "mutationofjb/util.h"
-#include "mutationofjb/commands/command.h"
-#include "common/util.h"
+
 #include "common/str.h"
 #include "common/translation.h"
+#include "common/util.h"
 
 namespace MutationOfJB {
 
@@ -57,6 +60,8 @@ Game::Game(MutationOfJBEngine *vm)
 	_room = new Room(this, _vm->getScreen());
 
 	_gui.init();
+
+	_taskManager.addTask(new ObjectAnimationTask);
 }
 
 Common::RandomSource &Game::getRandomSource() {
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 2ac4fab..1913fc6 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -23,6 +23,7 @@ MODULE_OBJS := \
 	commands/talkcommand.o \
 	commands/randomcommand.o \
 	tasks/conversationtask.o \
+	tasks/objectanimationtask.o \
 	tasks/saytask.o \
 	tasks/taskmanager.o \
 	widgets/buttonwidget.o \
diff --git a/engines/mutationofjb/tasks/objectanimationtask.cpp b/engines/mutationofjb/tasks/objectanimationtask.cpp
new file mode 100644
index 0000000..75c15bf
--- /dev/null
+++ b/engines/mutationofjb/tasks/objectanimationtask.cpp
@@ -0,0 +1,91 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/objectanimationtask.h"
+
+#include "mutationofjb/tasks/taskmanager.h"
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/room.h"
+
+namespace MutationOfJB {
+
+static const int TICK_MILLIS = 100;
+
+ObjectAnimationTask::ObjectAnimationTask() : _timer(TICK_MILLIS) {
+}
+
+void ObjectAnimationTask::start() {
+	setState(RUNNING);
+	_timer.start();
+}
+
+void ObjectAnimationTask::update() {
+	_timer.update();
+	if (_timer.isFinished()) {
+		_timer.start();
+		updateObjects();
+	}
+}
+
+void ObjectAnimationTask::updateObjects() {
+	Scene *const scene = getTaskManager()->getGame().getGameData().getCurrentScene();
+	if (!scene) {
+		return;
+	}
+
+	for (uint8 i = 1; i <= scene->getNoObjects(); ++i) {
+		Object *const object = scene->getObject(i);
+		// Skip if object animation not active.
+		if (!object->_AC)
+			continue;
+
+		// Number of framers must be higher than 1.
+		if (object->_NA <= 1)
+			continue;
+
+		const uint8 currentAnimOffset = object->_CA - object->_FA;
+
+		const bool randomized = object->_FR != 0;
+		const bool belowRandomFrame = currentAnimOffset < (object->_FR - 1);
+
+		uint8 maxAnimOffset = object->_NA - 1;
+		if (randomized && belowRandomFrame) {
+			maxAnimOffset = object->_FR - 2;
+		}
+
+		uint8 nextAnimationOffset = currentAnimOffset + 1;
+		if (currentAnimOffset == maxAnimOffset) {
+			if (randomized && object->_unknown != 0 && getTaskManager()->getGame().getRandomSource().getRandomNumber(object->_unknown) == 0)
+				nextAnimationOffset = object->_FR - 1;
+			else
+				nextAnimationOffset = 0;
+		}
+
+		// TODO: Hardcoded animations.
+
+		object->_CA = nextAnimationOffset + object->_FA;
+		getTaskManager()->getGame().getRoom().drawObjectAnimation(i, nextAnimationOffset);
+	}
+}
+
+}
diff --git a/engines/mutationofjb/tasks/objectanimationtask.h b/engines/mutationofjb/tasks/objectanimationtask.h
new file mode 100644
index 0000000..39f80a3
--- /dev/null
+++ b/engines/mutationofjb/tasks/objectanimationtask.h
@@ -0,0 +1,47 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_OBJECTANIMATIONTASK_H
+#define MUTATIONOFJB_OBJECTANIMATIONTASK_H
+
+#include "mutationofjb/tasks/task.h"
+
+#include "mutationofjb/timer.h"
+
+namespace MutationOfJB {
+
+class ObjectAnimationTask : public Task {
+public:
+	ObjectAnimationTask();
+
+	virtual void start() override;
+	virtual void update() override;
+
+	void updateObjects();
+
+private:
+	Timer _timer;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
index fcef6c3..c9c2a95 100644
--- a/engines/mutationofjb/tasks/saytask.cpp
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -45,7 +45,7 @@ void SayTask::start() {
 void SayTask::update() {
 	_timer.update();
 
-	if (_timer.isFnished()) {
+	if (_timer.isFinished()) {
 		getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
 		setState(FINISHED);
 		return;
diff --git a/engines/mutationofjb/tasks/saytask.h b/engines/mutationofjb/tasks/saytask.h
index ef66f5b..17e773d 100644
--- a/engines/mutationofjb/tasks/saytask.h
+++ b/engines/mutationofjb/tasks/saytask.h
@@ -20,6 +20,9 @@
  *
  */
 
+#ifndef MUTATIONOFJB_SAYTASK_H
+#define MUTATIONOFJB_SAYTASK_H
+
 #include "mutationofjb/tasks/task.h"
 
 #include "mutationofjb/timer.h"
@@ -46,3 +49,5 @@ private:
 };
 
 }
+
+#endif
diff --git a/engines/mutationofjb/timer.cpp b/engines/mutationofjb/timer.cpp
index 8375445..d5a306a 100644
--- a/engines/mutationofjb/timer.cpp
+++ b/engines/mutationofjb/timer.cpp
@@ -34,7 +34,7 @@ void Timer::start() {
 	_state = RUNNING;
 }
 
-bool Timer::isFnished() const {
+bool Timer::isFinished() const {
 	return _state == FINISHED;
 }
 
diff --git a/engines/mutationofjb/timer.h b/engines/mutationofjb/timer.h
index b12fe22..313823a 100644
--- a/engines/mutationofjb/timer.h
+++ b/engines/mutationofjb/timer.h
@@ -30,7 +30,7 @@ public:
 
 	void start();
 
-	bool isFnished() const;
+	bool isFinished() const;
 	bool isRunning() const;
 
 	void update();


Commit: 578a6794de9ba7679966fee9aec02c6b2bdbce94
    https://github.com/scummvm/scummvm/commit/578a6794de9ba7679966fee9aec02c6b2bdbce94
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Improve documentation, rename cryptic variables.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/room.cpp
    engines/mutationofjb/tasks/objectanimationtask.cpp
    engines/mutationofjb/tasks/saytask.cpp


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index a2d4526..d7a8f9c 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -381,22 +381,22 @@ Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scri
 
 	switch (_register) {
 	case AC:
-		object->_AC = _value._byteVal;
+		object->_active = _value._byteVal;
 		break;
 	case FA:
-		object->_FA = _value._byteVal;
+		object->_firstFrame = _value._byteVal;
 		break;
 	case FR:
-		object->_FR = _value._byteVal;
+		object->_randomFrame = _value._byteVal;
 		break;
 	case NA:
-		object->_NA = _value._byteVal;
+		object->_numFrames = _value._byteVal;
 		break;
 	case FS:
-		object->_FS = _value._byteVal;
+		object->_roomFrameLSB = _value._byteVal;
 		break;
 	case CA:
-		object->_CA = _value._byteVal;
+		object->_currentFrame = _value._byteVal;
 		break;
 	case XX:
 		object->_x = _value._wordVal;
@@ -405,16 +405,16 @@ Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scri
 		object->_y = _value._byteVal;
 		break;
 	case XL:
-		object->_XL = _value._wordVal;
+		object->_width = _value._wordVal;
 		break;
 	case YL:
-		object->_YL = _value._byteVal;
+		object->_height = _value._byteVal;
 		break;
 	case WX:
 		object->_WX = _value._wordVal;
 		break;
 	case WY:
-		object->_WY = _value._byteVal;
+		object->_roomFrameMSB = _value._byteVal;
 		break;
 	case SP:
 		object->_SP = _value._byteVal;
@@ -468,7 +468,7 @@ Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scri
 		stat->_walkToY = _value._byteVal;
 		break;
 	case SP:
-		stat->_SP = _value._byteVal;
+		stat->_walkToFrame = _value._byteVal;
 		break;
 	default:
 		warning("Object does not support changing this register.");
@@ -493,7 +493,7 @@ Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scrip
 		scene->_startup = _value._byteVal;
 		break;
 	case DL:
-		scene->_DL = _value._byteVal;
+		scene->_delay = _value._byteVal;
 		break;
 	case ND:
 		scene->_noDoors = _value._byteVal;
@@ -505,13 +505,13 @@ Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scrip
 		scene->_noStatics = _value._byteVal;
 		break;
 	case PF:
-		scene->_palRotStart = _value._byteVal;
+		scene->_palRotFirst = _value._byteVal;
 		break;
 	case PL:
-		scene->_palRotEnd = _value._byteVal;
+		scene->_palRotLast = _value._byteVal;
 		break;
 	case PD:
-		scene->_palRotPeriod = _value._byteVal;
+		scene->_palRotDelay = _value._byteVal;
 		break;
 	default:
 		warning("Scene does not support changing this register.");
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index b1c96ef..b4f00c9 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -318,14 +318,14 @@ bool Console::cmd_dumpsceneinfo(int argc, const char **argv) {
 		Scene *scene = _vm->getGame().getGameData().getScene(sceneId);
 		if (scene) {
 			debugPrintf("Startup: %u\n", (unsigned int) scene->_startup);
-			debugPrintf("Delay: %u\n", (unsigned int) scene->_DL);
+			debugPrintf("Delay: %u\n", (unsigned int) scene->_delay);
 			debugPrintf("Doors: %u\n", (unsigned int) scene->_noDoors);
 			debugPrintf("Objects: %u\n", (unsigned int) scene->_noObjects);
 			debugPrintf("Statics: %u\n", (unsigned int) scene->_noStatics);
 			debugPrintf("ObstacleY1: %u\n", (unsigned int) scene->_obstacleY1);
-			debugPrintf("PalRotStart: %u\n", (unsigned int) scene->_palRotStart);
-			debugPrintf("PalRotEnd: %u\n", (unsigned int) scene->_palRotEnd);
-			debugPrintf("PalRotPeriod: %u\n", (unsigned int) scene->_palRotPeriod);
+			debugPrintf("PalRotFirst: %u\n", (unsigned int) scene->_palRotFirst);
+			debugPrintf("PalRotLast: %u\n", (unsigned int) scene->_palRotLast);
+			debugPrintf("PalRotDelay: %u\n", (unsigned int) scene->_palRotDelay);
 		} else {
 			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
 		}
@@ -377,19 +377,19 @@ bool Console::cmd_dumpobjectinfo(int argc, const char **argv) {
 		if (scene) {
 			Object *const object = scene->getObject(objectId);
 			if (object) {
-				debugPrintf("AC: %u\n", (unsigned int) object->_AC);
-				debugPrintf("FA: %u\n", (unsigned int) object->_FA);
-				debugPrintf("FR: %u\n", (unsigned int) object->_FR);
-				debugPrintf("NA: %u\n", (unsigned int) object->_NA);
-				debugPrintf("FS: %u\n", (unsigned int) object->_FS);
-				debugPrintf("Unknown: %u\n", (unsigned int) object->_unknown);
-				debugPrintf("CA: %u\n", (unsigned int) object->_CA);
+				debugPrintf("AC: %u\n", (unsigned int) object->_active);
+				debugPrintf("FA: %u\n", (unsigned int) object->_firstFrame);
+				debugPrintf("FR: %u\n", (unsigned int) object->_randomFrame);
+				debugPrintf("NA: %u\n", (unsigned int) object->_numFrames);
+				debugPrintf("FS: %u\n", (unsigned int) object->_roomFrameLSB);
+				debugPrintf("Jump chance: %u\n", (unsigned int) object->_jumpChance);
+				debugPrintf("CA: %u\n", (unsigned int) object->_currentFrame);
 				debugPrintf("X: %u\n", (unsigned int) object->_x);
 				debugPrintf("Y: %u\n", (unsigned int) object->_y);
-				debugPrintf("XL: %u\n", (unsigned int) object->_XL);
-				debugPrintf("YL: %u\n", (unsigned int) object->_YL);
+				debugPrintf("XL: %u\n", (unsigned int) object->_width);
+				debugPrintf("YL: %u\n", (unsigned int) object->_height);
 				debugPrintf("WX: %u\n", (unsigned int) object->_WX);
-				debugPrintf("WY: %u\n", (unsigned int) object->_WY);
+				debugPrintf("WY: %u\n", (unsigned int) object->_roomFrameMSB);
 				debugPrintf("SP: %u\n", (unsigned int) object->_SP);
 			} else {
 				debugPrintf(_("Object %u not found.\n"), (unsigned int) objectId);
@@ -421,7 +421,7 @@ bool Console::cmd_dumpstaticinfo(int argc, const char **argv) {
 				debugPrintf("Height: %u\n", (unsigned int) stat->_height);
 				debugPrintf("WalkToX: %u\n", (unsigned int) stat->_walkToY);
 				debugPrintf("WalkToY: %u\n", (unsigned int) stat->_walkToX);
-				debugPrintf("WalkToFrame: %u\n", (unsigned int) stat->_SP);
+				debugPrintf("WalkToFrame: %u\n", (unsigned int) stat->_walkToFrame);
 			} else {
 				debugPrintf(_("Static %u not found.\n"), (unsigned int) staticId);
 			}
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index 6a9c166..b6295a7 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -58,19 +58,19 @@ bool Door::loadFromStream(Common::ReadStream &stream) {
 }
 
 bool Object::loadFromStream(Common::ReadStream &stream) {
-	_AC = stream.readByte();
-	_FA = stream.readByte();
-	_FR = stream.readByte();
-	_NA = stream.readByte();
-	_FS = stream.readByte();
-	_unknown = stream.readByte();
-	_CA = stream.readByte();
+	_active = stream.readByte();
+	_firstFrame = stream.readByte();
+	_randomFrame = stream.readByte();
+	_numFrames = stream.readByte();
+	_roomFrameLSB = stream.readByte();
+	_jumpChance = stream.readByte();
+	_currentFrame = stream.readByte();
 	_x = stream.readUint16LE();
 	_y = stream.readByte();
-	_XL = stream.readUint16LE();
-	_YL = stream.readByte();
+	_width = stream.readUint16LE();
+	_height = stream.readByte();
 	_WX = stream.readUint16LE();
-	_WY = stream.readByte();
+	_roomFrameMSB = stream.readByte();
 	_SP = stream.readByte();
 
 	return true;
@@ -85,13 +85,13 @@ bool Static::loadFromStream(Common::ReadStream &stream) {
 	_height = stream.readByte();
 	_walkToX = stream.readUint16LE();
 	_walkToY = stream.readByte();
-	_SP = stream.readByte();
+	_walkToFrame = stream.readByte();
 
 	return true;
 }
 
 bool Bitmap::loadFromStream(Common::ReadStream &stream) {
-	_frame = stream.readByte();
+	_roomFrame = stream.readByte();
 	_isVisible = stream.readByte();
 	_x1 = stream.readUint16LE();
 	_y1 = stream.readByte();
@@ -108,7 +108,7 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	_unknown001 = stream.readByte();
 	_unknown002 = stream.readByte();
 	_unknown003 = stream.readByte();
-	_DL = stream.readByte();
+	_delay = stream.readByte();
 
 	_noDoors = stream.readByte();
 	_noDoors = MIN(_noDoors, (uint8) ARRAYSIZE(_doors));
@@ -133,9 +133,9 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	}
 
 	_obstacleY1 = stream.readUint16LE();
-	_palRotStart = stream.readByte();
-	_palRotEnd = stream.readByte();
-	_palRotPeriod = stream.readByte();
+	_palRotFirst = stream.readByte();
+	_palRotLast = stream.readByte();
+	_palRotDelay = stream.readByte();
 	_exhaustedChoiceNext = stream.readByte();
 
 	for (i = 0; i < 79; ++i) {
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 62c66a1..99174ee 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -36,40 +36,49 @@ enum {
 	MAX_ENTITY_NAME_LENGTH = 0x14
 };
 
-/*
-	There are 4 types of entities present in the game data:
-	- Door
-	- Object
-	- Static
-	- Bitmap
-*/
+/** @file gamedata.h
+ * There are 4 types of entities present in the game data:
+ * - Door
+ * - Object
+ * - Static
+ * - Bitmap
+ */
 
+/**
+ * An interactable scene changer with no visual representation.
+ */
 struct Door {
-	/*
-		Door name.
-		Can be empty - deactivates door completely.
-	*/
+	/**
+	 * Door name (NM register).
+	 *
+	 * Can be empty - deactivates door completely (you can't mouse over or interact with it at all).
+	 *
+	 * If it ends with '+', using the "go" verb on the door will not implicitly change the scene,
+	 * but the player will still walk towards the door.
+	 *
+	 * TODO: Implement the '+' restriction.
+	 */
 	char _name[MAX_ENTITY_NAME_LENGTH + 1];
-	/*
-		Scene ID where the door leads.
-		Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
-	*/
+	/**
+	 * Scene ID where the door leads (LT register).
+	 * Can be 0 - you can hover your mouse over it, but clicking it doesn't do anything (unless scripted).
+	 */
 	uint8  _destSceneId;
-	/* X coordinate for player's position after going through the door. */
+	/** X coordinate for player's position after going through the door (SX register). */
 	uint16 _destX;
-	/* Y coordinate for player's position after going through the door. */
+	/** Y coordinate for player's position after going through the door (SY register). */
 	uint16 _destY;
-	/* X coordinate of the door rectangle. */
+	/** X coordinate of the door rectangle (XX register). */
 	uint16 _x;
-	/* Y coordinate of the door rectangle. */
+	/** Y coordinate of the door rectangle (YY register). */
 	uint8  _y;
-	/* Width of the door rectangle. */
+	/** Width of the door rectangle (XL register). */
 	uint16 _width;
-	/* Height of the door rectangle. */
+	/** Height of the door rectangle (YL register). */
 	uint8  _height;
-	/* X coordinate for position towards player will walk after clicking the door. */
+	/** X coordinate for position player will walk towards after clicking the door (WX register). */
 	uint16 _walkToX;
-	/* Y coordinate for position towards player will walk after clicking the door. */
+	/** Y coordinate for position player will walk towards after clicking the door (WY register). */
 	uint8  _walkToY;
 	/* Unknown for now - likely not even used. */
 	uint8  _SP;
@@ -77,56 +86,148 @@ struct Door {
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+/**
+ * An animated image in the scene.
+ *
+ * Object frames consist of surfaces carved out of room frames (starting from _roomFrame
+ * up until _roomFrame + _numFrames - 1) based on the object's rectangle. They are stored
+ * in the shared object frame space that each object occupies a continous part of from
+ * the beginning.
+ *
+ * By using the term "frame" alone we will be referring to an object frame, not a room
+ * frame.
+ *
+ * For details regarding animation playback, see objectanimationtask.cpp.
+ */
 struct Object {
-	uint8  _AC;
-	uint8  _FA;
-	uint8  _FR;
-	uint8  _NA;
-	uint8  _FS;
-	uint8  _unknown;
-	uint8  _CA;
+	/** Controls whether the animation is playing. */
+	uint8  _active;
+	/**
+	 * Number of the first frame this object has in the shared object frame space (FA register).
+	 *
+	 * For the first object, it is equal to 1.
+	 * For any subsequent object, it is equal to (_firstFrame + _numFrames) of the previous object.
+	 *
+	 * @note The numbering starts from 1.
+	 * @note Technically this field is useless because it can be calculated.
+	 */
+	uint8  _firstFrame;
+	/**
+	 * The frame that is jumped to randomly based on _jumpChance (FR register).
+	 *
+	 * @note Numbered from 1 and relative to _firstFrame.
+	 * @note A value of 0 disables randomness completely.
+	 * @see objectanimationtask.cpp
+	 * @see _jumpChance
+	 */
+	uint8  _randomFrame;
+	/** Number of animation frames (NA register). */
+	uint8  _numFrames;
+	/**
+	 * Low 8 bits of the 16-bit starting room frame (FS register).
+	 *
+	 * @see _roomFrameMSB
+	 */
+	uint8  _roomFrameLSB;
+	/**
+	 * Chance (1 in x) of the animation jumping to _randomFrame.
+	 *
+	 * @see objectanimationtask.cpp
+	*/
+	uint8  _jumpChance;
+	/**
+	 * Current animation frame (CA register).
+	 *
+	 * @note Absolute index to the frame space. Numbered from 1.
+	 */
+	uint8  _currentFrame;
+	/** X coordinate of the object rectangle (XX register). */
 	uint16 _x;
+	/** Y coordinate of the object rectangle (YY register). */
 	uint8  _y;
-	uint16 _XL;
-	uint8  _YL;
+	/** Width of the object rectangle (XL register). */
+	uint16 _width;
+	/** Height of the object rectangle (YL register). */
+	uint8  _height;
+	/** A general-purpose register for use in scripts. Nothing to do with animation. */
 	uint16 _WX;
-	uint8  _WY;
+	/**
+	 * High 8 bits of the 16-bit starting room frame (WY register).
+	 *
+	 * @see _roomFrameLSB
+	 */
+	uint8  _roomFrameMSB;
+	/* Unknown. TODO: Figure out what this does. */
 	uint8  _SP;
 
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+/**
+ * An interactable area without a visual representation.
+ */
 struct Static {
+	/** Whether you can mouse over and interact with the static (AC register). */
 	uint8  _active;
+	/**
+	 * Static name (NM register).
+	 *
+	 * If it starts with '~', the static has an implicit "pickup" action that adds
+	 * an item with the same name (except '`' replaces '~') to your inventory and
+	 * disables the static. If there is a matching scripted "pickup" action, it
+	 * overrides the implicit action.
+	 *
+	 * TODO: Support '~' statics.
+	 */
 	char _name[MAX_ENTITY_NAME_LENGTH + 1];
+	/** X coordinate of the static rectangle (XX register). */
 	uint16 _x;
+	/** Y coordinate of the static rectangle (YY register). */
 	uint8  _y;
+	/** Width of the static rectangle (XL register). */
 	uint16 _width;
+	/** Height of the static rectangle (YL register). */
 	uint8  _height;
+	/** X coordinate of the position the player will walk towards after clicking the static (WX register). */
 	uint16 _walkToX;
+	/** Y coordinate of the position the player will walk towards after clicking the static (WY register). */
 	uint8  _walkToY;
-	uint8  _SP;
+	/** Player frame (rotation) set after the player finishes walking towards the walk to position (SP register). */
+	uint8  _walkToFrame;
 
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+/**
+ * A static image that is carved out of a room frame based on its rectangle.
+ * The bitmap rectangle also specifies where to blit it on the screen.
+ */
 struct Bitmap {
-	uint8  _frame;
+	/** Room frame that this bitmap carves out of. */
+	uint8  _roomFrame;
+	/** Whether to draw the bitmap. */
 	uint8  _isVisible;
+	/** X coordinate of the top left corner of the bitmap rectangle. */
 	uint16 _x1;
+	/** Y coordinate of the top left corner of the bitmap rectangle. */
 	uint8  _y1;
+	/** X coordinate of the bottom right corner of the bitmap rectangle. */
 	uint16 _x2;
+	/** Y coordinate of the bottom right corner of the bitmap rectangle. */
 	uint8  _y2;
 
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
+/**
+ * Encoded exhausted choice.
+ */
 struct ExhaustedChoice {
-	/*
-		1 bit - context
-		3 bits - choice index
-		4 bits - choice list index
-	*/
+	/**
+	 * 1 bit - context
+	 * 3 bits - choice index
+	 * 4 bits - choice list index
+	 */
 	uint8 _encodedData;
 
 	uint8 getContext() const { return (_encodedData >> 7) & 0x1; }
@@ -154,29 +255,41 @@ struct Scene {
 	void addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList);
 	bool isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) const;
 
+	/** Refers to the script block that will be executed when you enter this scene (DS register). */
 	uint8 _startup;
+	/**
+	 * These three variables control downscaling of the player character depending on his Y.
+	 * TODO: Find out more.
+	*/
 	uint8 _unknown001;
 	uint8 _unknown002;
 	uint8 _unknown003;
-	uint8 _DL;
+	uint8 _delay; /**< Delay between object animation advancements (DL register). */
+
+	uint8 _noDoors; /**< Number of doors in the scene (ND register). */
+	Door _doors[5]; /**< Door definitions. */
 
-	uint8 _noDoors;
-	Door _doors[5];
+	uint8 _noObjects; /**< Number of animated objects in the scene (NO register). */
+	Object _objects[9]; /**< Object definitions. */
 
-	uint8 _noObjects;
-	Object _objects[9];
+	uint8 _noStatics; /**< Number of statics in the scene (NS register). */
+	Static _statics[15]; /**< Static definitions. */
 
-	uint8 _noStatics;
-	Static _statics[15];
+	Bitmap _bitmaps[10]; /**< Bitmap definitions. There is no corresponding _noBitmaps field. */
 
-	Bitmap _bitmaps[10];
+	uint16 _obstacleY1; /**< Fixed Y coordinate for all static obstacles in the scene. Always 0 in data files. */
 
-	uint16 _obstacleY1;
-	uint8 _palRotStart;
-	uint8 _palRotEnd;
-	uint8 _palRotPeriod;
+	/** First index (inclusive and 0-indexed) of the rotating portion of the palette (PF register). */
+	uint8 _palRotFirst;
+	/** Last index (inclusive and 0-indexed) of the rotating portion of the palette (PL register). */
+	uint8 _palRotLast;
+	/** Delay between each right rotation of the palette portion (PD register). */
+	uint8 _palRotDelay;
 
-	/* Points to the first free item in exhausted choices list. */
+	/**
+	 * Points to the first free item in exhausted choices list.
+	 * @note Indexed from 1.
+	 */
 	uint8 _exhaustedChoiceNext;
 	ExhaustedChoice _exhaustedChoices[79];
 
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 009c4be..414a367 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -71,12 +71,12 @@ void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surfa
 		const uint8 noObjects = scene->getNoObjects();
 		for (int i = 0; i < noObjects; ++i) {
 			Object &object = scene->_objects[i];
-			const uint16 startFrame = (object._WY << 8) + object._FS;
-			if (frameNo1 >= startFrame && frameNo1 < startFrame + object._NA) {
+			const uint16 startFrame = (object._roomFrameMSB << 8) + object._roomFrameLSB;
+			if (frameNo1 >= startFrame && frameNo1 < startFrame + object._numFrames) {
 				const int x = object._x;
 				const int y = object._y;
-				const int w = (object._XL + 3) / 4 * 4; // Original code uses this to round up width to a multiple of 4.
-				const int h = object._YL;
+				const int w = (object._width + 3) / 4 * 4; // Original code uses this to round up width to a multiple of 4.
+				const int h = object._height;
 				Common::Rect rect(x, y, x + w, y + h);
 
 				const Graphics::Surface sharedSurface = surface.getSubArea(rect);
@@ -100,11 +100,11 @@ bool Room::load(uint8 roomNumber, bool roomB) {
 		for (int i = 0; i < noObjects; ++i) {
 			uint8 firstIndex = 0;
 			if (i != 0) {
-				firstIndex = _objectsStart[i - 1] + scene->_objects[i - 1]._NA;
+				firstIndex = _objectsStart[i - 1] + scene->_objects[i - 1]._numFrames;
 			}
 			_objectsStart.push_back(firstIndex);
 
-			uint8 numAnims = scene->_objects[i]._NA;
+			uint8 numAnims = scene->_objects[i]._numFrames;
 			while (numAnims--) {
 				_surfaces.push_back(Graphics::Surface());
 			}
@@ -142,7 +142,7 @@ void Room::redraw() {
 	Scene *const currentScene = _game->getGameData().getCurrentScene();
 	for (int i = 0; i < currentScene->getNoObjects(); ++i) {
 		Object *const obj = currentScene->getObject(i + 1);
-		if (obj->_AC) {
+		if (obj->_active) {
 			drawObjectAnimation(i + 1, 0);
 		}
 	}
diff --git a/engines/mutationofjb/tasks/objectanimationtask.cpp b/engines/mutationofjb/tasks/objectanimationtask.cpp
index 75c15bf..90438f8 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.cpp
+++ b/engines/mutationofjb/tasks/objectanimationtask.cpp
@@ -31,6 +31,7 @@ namespace MutationOfJB {
 
 static const int TICK_MILLIS = 100;
 
+// TODO: Respect currentScene._delay.
 ObjectAnimationTask::ObjectAnimationTask() : _timer(TICK_MILLIS) {
 }
 
@@ -47,6 +48,17 @@ void ObjectAnimationTask::update() {
 	}
 }
 
+/**
+ * Advances every object animation in the current scene to the next frame.
+ *
+ * Normally the animation restarts after the last object frame. However, some animations have random
+ * elements to them. If _randomFrame is set, the animation restarts when _randomFrame is reached.
+ * Additionally, there is a chance with each frame until _randomFrame that the animation may jump
+ * straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
+ *
+ * Randomness is used to introduce variety - e.g. in the starting scene a perched bird occassionally
+ * spreads its wings.
+ */
 void ObjectAnimationTask::updateObjects() {
 	Scene *const scene = getTaskManager()->getGame().getGameData().getCurrentScene();
 	if (!scene) {
@@ -56,34 +68,34 @@ void ObjectAnimationTask::updateObjects() {
 	for (uint8 i = 1; i <= scene->getNoObjects(); ++i) {
 		Object *const object = scene->getObject(i);
 		// Skip if object animation not active.
-		if (!object->_AC)
+		if (!object->_active)
 			continue;
 
-		// Number of framers must be higher than 1.
-		if (object->_NA <= 1)
+		// Number of frames must be higher than 1.
+		if (object->_numFrames <= 1)
 			continue;
 
-		const uint8 currentAnimOffset = object->_CA - object->_FA;
+		const uint8 currentAnimOffset = object->_currentFrame - object->_firstFrame;
 
-		const bool randomized = object->_FR != 0;
-		const bool belowRandomFrame = currentAnimOffset < (object->_FR - 1);
+		const bool randomized = object->_randomFrame != 0;
+		const bool belowRandomFrame = currentAnimOffset < (object->_randomFrame - 1);
 
-		uint8 maxAnimOffset = object->_NA - 1;
+		uint8 maxAnimOffset = object->_numFrames - 1;
 		if (randomized && belowRandomFrame) {
-			maxAnimOffset = object->_FR - 2;
+			maxAnimOffset = object->_randomFrame - 2;
 		}
 
 		uint8 nextAnimationOffset = currentAnimOffset + 1;
 		if (currentAnimOffset == maxAnimOffset) {
-			if (randomized && object->_unknown != 0 && getTaskManager()->getGame().getRandomSource().getRandomNumber(object->_unknown) == 0)
-				nextAnimationOffset = object->_FR - 1;
+			if (randomized && object->_jumpChance != 0 && getTaskManager()->getGame().getRandomSource().getRandomNumber(object->_jumpChance) == 0)
+				nextAnimationOffset = object->_randomFrame - 1;
 			else
 				nextAnimationOffset = 0;
 		}
 
 		// TODO: Hardcoded animations.
 
-		object->_CA = nextAnimationOffset + object->_FA;
+		object->_currentFrame = nextAnimationOffset + object->_firstFrame;
 		getTaskManager()->getGame().getRoom().drawObjectAnimation(i, nextAnimationOffset);
 	}
 }
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
index c9c2a95..bd89805 100644
--- a/engines/mutationofjb/tasks/saytask.cpp
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -60,10 +60,12 @@ void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY,
 	Common::Array<Common::String> lines;
 	font.wordWrap(text, MAX_LINE_WIDTH, lines);
 
+	// Get the x, y coordinates of the top center point of the text's bounding box
+	// from the (rather strange) talk coordinates coming from scripts.
 	int16 x = talkX;
-	int16 y = talkY - (lines.size() - 1) * font.getLineHeight() - 15; // Get the top y
+	int16 y = talkY - (lines.size() - 1) * font.getLineHeight() - 15;
 
-	// Clamp to screen edges
+	// Clamp to screen edges.
 	y = MAX<int16>(y, 3);
 	int16 maxWidth = 0;
 	for (uint i = 0; i < lines.size(); i++) {
@@ -75,12 +77,12 @@ void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY,
 		x = MIN<int16>(x, 317 - lineWidth / 2);
 	}
 
-	// Draw lines
+	// Draw lines.
 	for (uint i = 0; i < lines.size(); i++) {
 		font.drawString(lines[i], color, x - font.getWidth(lines[i]) / 2, y + i * font.getLineHeight(), getTaskManager()->getGame().getScreen());
 	}
 
-	// Remember the area occupied by the text
+	// Remember the area occupied by the text.
 	_boundingBox.top = x - maxWidth / 2;
 	_boundingBox.left = y;
 	_boundingBox.setWidth(maxWidth);


Commit: 9aa911314fb54e71b9b37b8787626d25afbcd289
    https://github.com/scummvm/scummvm/commit/9aa911314fb54e71b9b37b8787626d25afbcd289
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Handle hardcoded animations.

Changed paths:
    engines/mutationofjb/tasks/objectanimationtask.cpp
    engines/mutationofjb/tasks/objectanimationtask.h


diff --git a/engines/mutationofjb/tasks/objectanimationtask.cpp b/engines/mutationofjb/tasks/objectanimationtask.cpp
index 90438f8..e93602c 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.cpp
+++ b/engines/mutationofjb/tasks/objectanimationtask.cpp
@@ -93,11 +93,81 @@ void ObjectAnimationTask::updateObjects() {
 				nextAnimationOffset = 0;
 		}
 
-		// TODO: Hardcoded animations.
-
 		object->_currentFrame = nextAnimationOffset + object->_firstFrame;
-		getTaskManager()->getGame().getRoom().drawObjectAnimation(i, nextAnimationOffset);
+
+		const bool drawObject = handleHardcodedAnimation(object);
+		if (drawObject) {
+			getTaskManager()->getGame().getRoom().drawObjectAnimation(i, nextAnimationOffset);
+		}
+	}
+}
+
+/**
+ * Nasty, hacky stuff the original game does to make some complex animations
+ * in the Carnival and Tavern Earthquake scenes possible.
+ *
+ * @param object Object to process.
+ * @return Whether to draw the object. It's important to respect this, otherwise
+ * some of the hardcoded animations would suffer from graphical glitches.
+ */
+bool ObjectAnimationTask::handleHardcodedAnimation(Object *const object) {
+	GameData &gameData = getTaskManager()->getGame().getGameData();
+	Scene *const scene = gameData.getCurrentScene();
+
+	const bool carnivalScene = gameData._currentScene == 30 && !gameData._partB;
+	const bool tavernScene = gameData._currentScene == 8 && gameData._partB;
+
+	if (carnivalScene) {
+		// This alternates between the two burglars' talking animations.
+		// Each burglar gets to talk for a varying amount of time since
+		// the switch occurs when his random frame is reached.
+		if (object->_WX == 1 && object->_currentFrame == 79) {
+			object->_currentFrame = 68;
+			object->_active = 0;
+			scene->getObject(6)->_active = 1;
+			scene->getObject(7)->_active = 0;
+			scene->getObject(8)->_active = 1;
+			return false;
+		} else if (object->_WX == 2 && object->_currentFrame == 91) {
+			object->_currentFrame = 80;
+			object->_active = 0;
+			scene->getObject(5)->_active = 1;
+			scene->getObject(7)->_active = 1;
+			scene->getObject(8)->_active = 0;
+			return false;
+		}
+
+		// The following makes sure you can't interact with the glass
+		// while the scientist is drinking from it.
+		if (scene->getObject(4)->_currentFrame > 52 && scene->getObject(4)->_active) {
+			scene->getStatic(9)->_active = 0; // disable scientist's glass
+		} else {
+			scene->getStatic(9)->_active = 1; // enable scientist's glass
+		}
+
+		if (!scene->getObject(4)->_active) {
+			scene->getStatic(9)->_active = 0; // disable scientist's glass
+		}
+	} else if (tavernScene) {
+		// Similarly to the carnival burglars, this alternates between
+		// the talking animations of the two soldiers in the tavern.
+		//
+		// At some point the script disables their conversation
+		// by nulling their _WX registers.
+		if (object->_WX == 3 && object->_currentFrame == 46) {
+			object->_currentFrame = 30;
+			object->_active = 0;
+			scene->getObject(3)->_active = 1;
+			return false;
+		} else if (object->_WX == 4 && object->_currentFrame == 63) {
+			object->_currentFrame = 47;
+			object->_active = 0;
+			scene->getObject(2)->_active = 1;
+			return false;
+		}
 	}
+
+	return true;
 }
 
 }
diff --git a/engines/mutationofjb/tasks/objectanimationtask.h b/engines/mutationofjb/tasks/objectanimationtask.h
index 39f80a3..320868f 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.h
+++ b/engines/mutationofjb/tasks/objectanimationtask.h
@@ -29,6 +29,8 @@
 
 namespace MutationOfJB {
 
+class Object;
+
 class ObjectAnimationTask : public Task {
 public:
 	ObjectAnimationTask();
@@ -37,6 +39,7 @@ public:
 	virtual void update() override;
 
 	void updateObjects();
+	bool handleHardcodedAnimation(Object *const object);
 
 private:
 	Timer _timer;


Commit: 96fbe2f8817303e25e0436641e23b0f7b4275658
    https://github.com/scummvm/scummvm/commit/96fbe2f8817303e25e0436641e23b0f7b4275658
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix crash when static/door name is set to empty string.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index d7a8f9c..4eac41e 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -52,7 +52,7 @@ bool ChangeCommandParser::parseValueString(const Common::String &valueString, bo
 	if (changeEntity) {
 		entityId = atoi(valueString.c_str() + 6);
 	}
-	const char *val = nullptr;
+	const char *val = "";
 	if (changeEntity) {
 		if (valueString.size() >= 9) {
 			val = valueString.c_str() + 9;


Commit: 215b87ccca4c4888cd70c990271fe62177d054f0
    https://github.com/scummvm/scummvm/commit/215b87ccca4c4888cd70c990271fe62177d054f0
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: When redrawing room, draw object animations at their current frame instead of their first frame.

Changed paths:
    engines/mutationofjb/room.cpp


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 414a367..1da2730 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -143,7 +143,7 @@ void Room::redraw() {
 	for (int i = 0; i < currentScene->getNoObjects(); ++i) {
 		Object *const obj = currentScene->getObject(i + 1);
 		if (obj->_active) {
-			drawObjectAnimation(i + 1, 0);
+			drawObjectAnimation(i + 1, obj->_currentFrame - _objectsStart[i] - 1);
 		}
 	}
 }


Commit: 6c4ae7f19883dd7fa6de4fc3234351f1e33ba7a3
    https://github.com/scummvm/scummvm/commit/6c4ae7f19883dd7fa6de4fc3234351f1e33ba7a3
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement multiple speeches in one response line.

Changed paths:
  A engines/mutationofjb/tasks/sequentialtask.cpp
  A engines/mutationofjb/tasks/sequentialtask.h
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/commands/talkcommand.h
    engines/mutationofjb/conversationlinelist.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/module.mk
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/conversationtask.h
    engines/mutationofjb/tasks/task.h
    engines/mutationofjb/tasks/taskmanager.cpp
    engines/mutationofjb/tasks/taskmanager.h


diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index 18ce956..8411e90 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -56,14 +56,13 @@ bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &,
 
 Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) {
 	if (!_task) {
-		_task = new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo);
+		_task = TaskPtr(new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo, _mode));
 		scriptExeCtx.getGame().getTaskManager().addTask(_task);
 	}
 
 	if (_task->getState() == Task::FINISHED) {
 		scriptExeCtx.getGame().getTaskManager().removeTask(_task);
-		delete _task;
-		_task = nullptr;
+		_task.reset();
 
 		return Command::Finished;
 	}
diff --git a/engines/mutationofjb/commands/talkcommand.h b/engines/mutationofjb/commands/talkcommand.h
index 09424b2..15b185c 100644
--- a/engines/mutationofjb/commands/talkcommand.h
+++ b/engines/mutationofjb/commands/talkcommand.h
@@ -25,6 +25,7 @@
 
 #include "mutationofjb/commands/seqcommand.h"
 #include "common/scummsys.h"
+#include "mutationofjb/tasks/task.h"
 
 namespace MutationOfJB {
 
@@ -43,13 +44,13 @@ public:
 		CARNIVAL_TICKET_SELLER_MODE
 	};
 
-	TalkCommand(Mode mode) : _mode(mode), _task(nullptr) {}
+	TalkCommand(Mode mode) : _mode(mode) {}
 	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
 private:
 	Mode _mode;
-	ConversationTask *_task;
+	TaskPtr _task;
 };
 
 }
diff --git a/engines/mutationofjb/conversationlinelist.h b/engines/mutationofjb/conversationlinelist.h
index 9ec446e..0f8346f 100644
--- a/engines/mutationofjb/conversationlinelist.h
+++ b/engines/mutationofjb/conversationlinelist.h
@@ -39,8 +39,9 @@ public:
 		bool isSecondSpeaker() const { return _text.firstChar() == '`'; }
 	};
 
+	typedef Common::Array<Speech> Speeches;
 	struct Line {
-		Common::Array<Speech> _speeches;
+		Speeches _speeches;
 		Common::String _extra;
 	};
 
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 652336f..9f5f469 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -61,7 +61,7 @@ Game::Game(MutationOfJBEngine *vm)
 
 	_gui.init();
 
-	_taskManager.addTask(new ObjectAnimationTask);
+	_taskManager.addTask(TaskPtr(new ObjectAnimationTask));
 }
 
 Common::RandomSource &Game::getRandomSource() {
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 1913fc6..60d450e 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -25,6 +25,7 @@ MODULE_OBJS := \
 	tasks/conversationtask.o \
 	tasks/objectanimationtask.o \
 	tasks/saytask.o \
+	tasks/sequentialtask.o \
 	tasks/taskmanager.o \
 	widgets/buttonwidget.o \
 	widgets/conversationwidget.o \
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index d675b2e..a2a0a73 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -23,12 +23,12 @@
 #include "mutationofjb/tasks/conversationtask.h"
 
 #include "mutationofjb/assets.h"
-#include "mutationofjb/conversationlinelist.h"
 #include "mutationofjb/game.h"
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/gui.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/tasks/saytask.h"
+#include "mutationofjb/tasks/sequentialtask.h"
 #include "mutationofjb/tasks/taskmanager.h"
 #include "mutationofjb/util.h"
 #include "mutationofjb/widgets/conversationwidget.h"
@@ -55,8 +55,7 @@ void ConversationTask::update() {
 	if (_sayTask) {
 		if (_sayTask->getState() == Task::FINISHED) {
 			getTaskManager()->removeTask(_sayTask);
-			delete _sayTask;
-			_sayTask = nullptr;
+			_sayTask.reset();
 
 			switch (_substate) {
 			case SAYING_NO_CHOICES:
@@ -66,9 +65,9 @@ void ConversationTask::update() {
 				const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList();
 				const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
 
-				_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
-				getTaskManager()->addTask(_sayTask);
 				_substate = SAYING_RESPONSE;
+				createSayTasks(line);
+				getTaskManager()->addTask(_sayTask);
 				break;
 			}
 			case SAYING_RESPONSE: {
@@ -102,14 +101,14 @@ void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint
 	convWidget->clearChoices();
 
 	const ConversationLineList& toSayList = getTaskManager()->getGame().getAssets().getToSayList();
-	const ConversationLineList::Speech &speech = toSayList.getLine(item._choice)->_speeches[0];
+	const ConversationLineList::Line *line = toSayList.getLine(item._choice);
 
-	_sayTask = new SayTask(speech._text, _convInfo._color);
-	getTaskManager()->addTask(_sayTask);
 	_substate = SAYING_CHOICE;
+	createSayTasks(line);
+	getTaskManager()->addTask(_sayTask);
 	_currentItem = &item;
 
-	if (!speech.isRepeating()) {
+	if (!line->_speeches[0].isRepeating()) {
 		getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1);
 	}
 }
@@ -176,9 +175,9 @@ void ConversationTask::showChoicesOrPick() {
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()];
 		const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
 
-		_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
-		getTaskManager()->addTask(_sayTask);
 		_substate = SAYING_CHOICE;
+		createSayTasks(line);
+		getTaskManager()->addTask(_sayTask);
 		_currentItem = &item;
 
 		if (!line->_speeches[0].isRepeating()) {
@@ -191,9 +190,9 @@ void ConversationTask::showChoicesOrPick() {
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()];
 		const ConversationLineList::Line *const line = responseList.getLine(item._response);
 
-		_sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color);
-		getTaskManager()->addTask(_sayTask);
 		_substate = SAYING_RESPONSE;
+		createSayTasks(line);
+		getTaskManager()->addTask(_sayTask);
 		_currentItem = &item;
 
 		_haveChoices = true;
@@ -204,7 +203,7 @@ void ConversationTask::showChoicesOrPick() {
 		if (_haveChoices) {
 			finish();
 		} else {
-			_sayTask = new SayTask("Nothing to talk about.", _convInfo._color); // TODO: This is hardcoded in executable. Load it.
+			_sayTask = TaskPtr(new SayTask("Nothing to talk about.", _convInfo._color)); // TODO: This is hardcoded in executable. Load it.
 			getTaskManager()->addTask(_sayTask);
 			_substate = SAYING_NO_CHOICES;
 			_currentItem = nullptr;
@@ -256,4 +255,32 @@ void ConversationTask::gotoNextLine() {
 	}
 }
 
+void ConversationTask::createSayTasks(const ConversationLineList::Line *line) {
+	if (line->_speeches.size() == 1) {
+		const ConversationLineList::Speech &speech = line->_speeches[0];
+		_sayTask = TaskPtr(new SayTask(speech._text, getSpeechColor(speech)));
+	} else {
+		TaskPtrs tasks;
+		for (ConversationLineList::Speeches::const_iterator it = line->_speeches.begin(); it != line->_speeches.end(); ++it) {
+			tasks.push_back(TaskPtr(new SayTask(it->_text, getSpeechColor(*it))));
+		}
+		_sayTask = TaskPtr(new SequentialTask(tasks));
+	}
+}
+
+uint8 ConversationTask::getSpeechColor(const ConversationLineList::Speech &speech) {
+	uint8 color = WHITE;
+	if (_substate == SAYING_RESPONSE) {
+		color = _convInfo._color;
+		if (_mode == TalkCommand::RAY_AND_BUTTLEG_MODE) {
+			if (speech.isFirstSpeaker()) {
+				color = GREEN;
+			} else if (speech.isSecondSpeaker()) {
+				color = LIGHTBLUE;
+			}
+		}
+	}
+	return color;
+}
+
 }
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
index 41c8284..e4bbdc3 100644
--- a/engines/mutationofjb/tasks/conversationtask.h
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -20,8 +20,10 @@
  *
  */
 
-#include "mutationofjb/tasks/task.h"
+#include "mutationofjb/commands/talkcommand.h"
+#include "mutationofjb/conversationlinelist.h"
 #include "mutationofjb/gamedata.h"
+#include "mutationofjb/tasks/task.h"
 #include "mutationofjb/widgets/conversationwidget.h"
 
 namespace MutationOfJB {
@@ -31,7 +33,7 @@ class ScriptExecutionContext;
 
 class ConversationTask : public Task, public ConversationWidgetCallback {
 public:
-	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo) : _sceneId(sceneId), _convInfo(convInfo), _currentLineIndex(0), _currentItem(nullptr), _sayTask(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
+	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentLineIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
 	virtual ~ConversationTask() {}
 
 	virtual void start() override;
@@ -44,12 +46,15 @@ private:
 	void finish();
 	void startExtra();
 	void gotoNextLine();
+	void createSayTasks(const ConversationLineList::Line *line);
+	uint8 getSpeechColor(const ConversationLineList::Speech &speech);
 
 	uint8 _sceneId;
 	const ConversationInfo &_convInfo;
+	TalkCommand::Mode _mode;
 	uint _currentLineIndex;
 	const ConversationInfo::Item *_currentItem;
-	SayTask* _sayTask;
+	TaskPtr _sayTask;
 
 	enum Substate {
 		IDLE,
diff --git a/engines/mutationofjb/tasks/sequentialtask.cpp b/engines/mutationofjb/tasks/sequentialtask.cpp
new file mode 100644
index 0000000..57efbb5
--- /dev/null
+++ b/engines/mutationofjb/tasks/sequentialtask.cpp
@@ -0,0 +1,62 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/tasks/sequentialtask.h"
+
+#include "mutationofjb/tasks/taskmanager.h"
+
+namespace MutationOfJB {
+
+SequentialTask::SequentialTask(const TaskPtrs &tasks) : _tasks(tasks) {
+}
+
+void SequentialTask::start() {
+	setState(RUNNING);
+	runTasks();
+}
+
+void SequentialTask::update() {
+	runTasks();
+}
+
+void SequentialTask::runTasks() {
+	while (true) {
+		if (_tasks.empty()) {
+			setState(FINISHED);
+			return;
+		}
+
+		const TaskPtr &task = _tasks.front();
+		switch (task->getState()) {
+		case IDLE:
+			getTaskManager()->addTask(task);
+			break;
+		case RUNNING:
+			return;
+		case FINISHED:
+			_tasks.remove_at(0);
+			break;
+		}
+	}
+}
+
+}
diff --git a/engines/mutationofjb/tasks/sequentialtask.h b/engines/mutationofjb/tasks/sequentialtask.h
new file mode 100644
index 0000000..078fd8a
--- /dev/null
+++ b/engines/mutationofjb/tasks/sequentialtask.h
@@ -0,0 +1,45 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_SEQUENTIALTASK_H
+#define MUTATIONOFJB_SEQUENTIALTASK_H
+
+#include "mutationofjb/tasks/task.h"
+
+namespace MutationOfJB {
+
+class SequentialTask : public Task {
+public:
+	SequentialTask(const TaskPtrs &tasks);
+
+	virtual void start() override;
+	virtual void update() override;
+
+private:
+	void runTasks();
+
+	TaskPtrs _tasks;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
index 1b98097..9f2acb3 100644
--- a/engines/mutationofjb/tasks/task.h
+++ b/engines/mutationofjb/tasks/task.h
@@ -24,6 +24,8 @@
 #define MUTATIONOFJB_TASK_H
 
 #include "common/scummsys.h"
+#include "common/ptr.h"
+#include "common/array.h"
 
 namespace MutationOfJB {
 
@@ -37,7 +39,7 @@ public:
 		FINISHED
 	};
 
-	Task() : _taskManager(nullptr) {}
+	Task() : _taskManager(nullptr), _state(IDLE) {}
 	virtual ~Task() {}
 
 	virtual void start() = 0;
@@ -56,6 +58,9 @@ private:
 	State _state;
 };
 
+typedef Common::SharedPtr<Task> TaskPtr;
+typedef Common::Array<Common::SharedPtr<Task> > TaskPtrs;
+
 }
 
 #endif
diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp
index 7fbf64d..1167500 100644
--- a/engines/mutationofjb/tasks/taskmanager.cpp
+++ b/engines/mutationofjb/tasks/taskmanager.cpp
@@ -25,24 +25,31 @@
 
 namespace MutationOfJB {
 
-void TaskManager::addTask(Task *task) {
+void TaskManager::addTask(const TaskPtr &task) {
 	_tasks.push_back(task);
 	task->setTaskManager(this);
 	task->start();
 }
 
-void TaskManager::removeTask(Task *task) {
-	Tasks::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
+void TaskManager::removeTask(const TaskPtr &task) {
+	TaskPtrs::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
 	if (it != _tasks.end()) {
 		_tasks.erase(it);
 	}
 }
 
 void TaskManager::update() {
-	for (Tasks::const_iterator it = _tasks.begin(); it != _tasks.end(); ++it) {
-		if ((*it)->getState() == Task::RUNNING) {
+	for (TaskPtrs::iterator it = _tasks.begin(); it != _tasks.end();) {
+		const Task::State state = (*it)->getState();
+		if (state == Task::RUNNING) {
 			(*it)->update();
 		}
+
+		if (state == Task::FINISHED) {
+			it = _tasks.erase(it);
+		} else {
+			++it;
+		}
 	}
 }
 
diff --git a/engines/mutationofjb/tasks/taskmanager.h b/engines/mutationofjb/tasks/taskmanager.h
index 1f6be36..299a271 100644
--- a/engines/mutationofjb/tasks/taskmanager.h
+++ b/engines/mutationofjb/tasks/taskmanager.h
@@ -24,6 +24,7 @@
 #define MUTATIONOFJB_TASKMANAGER_H
 
 #include "common/array.h"
+#include "task.h"
 
 namespace MutationOfJB {
 
@@ -34,15 +35,14 @@ class TaskManager {
 public:
 	TaskManager(Game &game) : _game(game) {}
 
-	void addTask(Task *task);
-	void removeTask(Task *task);
+	void addTask(const TaskPtr &task);
+	void removeTask(const TaskPtr &task);
 	void update();
 
 	Game &getGame() { return _game; }
 
 private:
-	typedef Common::Array<Task *> Tasks;
-	Tasks _tasks;
+	TaskPtrs _tasks;
 	Game &_game;
 };
 


Commit: 2e656e69b3b9416f5164f0963951df203f4978e5
    https://github.com/scummvm/scummvm/commit/2e656e69b3b9416f5164f0963951df203f4978e5
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Blit with threshold.

Changed paths:
    engines/mutationofjb/room.cpp
    engines/mutationofjb/util.cpp
    engines/mutationofjb/util.h


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index 1da2730..dd9a6db 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -28,6 +28,7 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/util.h"
 
+#include "common/rect.h"
 #include "common/str.h"
 #include "common/translation.h"
 
@@ -117,6 +118,13 @@ bool Room::load(uint8 roomNumber, bool roomB) {
 	return decoder.decode(&callback);
 }
 
+// TODO: Take the threshold value from ATN data.
+struct ThresholdBlitOperation {
+	bool operator()(const byte /*srcColor*/, const byte destColor) {
+		return destColor <= 0xBF;
+	}
+};
+
 void Room::drawObjectAnimation(uint8 objectId, int animOffset) {
 	Scene *const scene = _game->getGameData().getCurrentScene();
 	if (!scene) {
@@ -129,8 +137,8 @@ void Room::drawObjectAnimation(uint8 objectId, int animOffset) {
 
 	const int startFrame = _objectsStart[objectId - 1];
 	const int animFrame = startFrame + animOffset;
-	// TODO: Threshold.
-	_screen->blitFrom(_surfaces[animFrame], Common::Point(object->_x, object->_y));
+
+	blit_if(_surfaces[animFrame], *_screen, Common::Point(object->_x, object->_y), ThresholdBlitOperation());
 }
 
 void Room::redraw() {
@@ -140,7 +148,7 @@ void Room::redraw() {
 	}
 
 	Scene *const currentScene = _game->getGameData().getCurrentScene();
-	for (int i = 0; i < currentScene->getNoObjects(); ++i) {
+	for (uint8 i = 0; i < currentScene->getNoObjects(); ++i) {
 		Object *const obj = currentScene->getObject(i + 1);
 		if (obj->_active) {
 			drawObjectAnimation(i + 1, obj->_currentFrame - _objectsStart[i] - 1);
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 6d7c02a..72d2d0f 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -21,8 +21,10 @@
  */
 
 #include "mutationofjb/util.h"
+
 #include "common/str.h"
 #include "common/translation.h"
+
 #include "engines/engine.h"
 
 namespace MutationOfJB {
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 86ee10f..06d1a0b 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -23,15 +23,51 @@
 #ifndef MUTATIONOFJB_UTIL_H
 #define MUTATIONOFJB_UTIL_H
 
+#include "common/rect.h"
+
+#include "graphics/managed_surface.h"
+#include "graphics/surface.h"
+
 namespace Common {
 class String;
 }
 
+
 namespace MutationOfJB {
 
 void reportFileMissingError(const char *fileName);
 Common::String toUpperCP895(const Common::String &str);
 
+template <typename BlitOp>
+void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
+	const Common::Rect srcBounds = srcRect;
+	const Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
+
+	assert(srcRect.isValidRect());
+	assert(dest.format == src.format);
+
+	for (int y = 0; y < srcBounds.height(); ++y) {
+		const byte *srcP = reinterpret_cast<const byte *>(src.getBasePtr(srcBounds.left, srcBounds.top + y));
+		const byte *srcEndP = srcP + srcBounds.width();
+		byte *destP = reinterpret_cast<byte *>(dest.getBasePtr(destBounds.left, destBounds.top + y));
+
+		while (srcP != srcEndP) {
+			if (blitOp(*srcP, *destP)) {
+				*destP = *srcP;
+			}
+			++srcP;
+			++destP;
+		}
+	}
+
+	dest.getSubArea(destBounds); // This is a hack to invalidate the destination rectangle.
+}
+
+template <typename BlitOp>
+void blit_if(const Graphics::Surface &src, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
+	blit_if(src, Common::Rect(0, 0, src.w, src.h), dest, destPos, blitOp);
+}
+
 }
 
 #endif


Commit: 3306cbfeaa2f1c6fd471daaee054290df7e44280
    https://github.com/scummvm/scummvm/commit/3306cbfeaa2f1c6fd471daaee054290df7e44280
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement SayCommand::execute.

Changed paths:
    engines/mutationofjb/commands/saycommand.cpp
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/font.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/game.h
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/saytask.cpp
    engines/mutationofjb/tasks/saytask.h
    engines/mutationofjb/tasks/sequentialtask.cpp
    engines/mutationofjb/tasks/task.h
    engines/mutationofjb/tasks/taskmanager.cpp
    engines/mutationofjb/tasks/taskmanager.h


diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
index 854c957..0474a3e 100644
--- a/engines/mutationofjb/commands/saycommand.cpp
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -21,7 +21,13 @@
  */
 
 #include "mutationofjb/commands/saycommand.h"
+
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
+#include "mutationofjb/tasks/saytask.h"
+#include "mutationofjb/tasks/taskmanager.h"
+
 #include "common/str.h"
 #include "common/debug.h"
 #include "common/debug-channels.h"
@@ -140,9 +146,18 @@ bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &par
 }
 
 
-Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &) {
-	// TODO: Actual implementation.
-	debug("%s [%s]", _lineToSay.c_str(), _voiceFile.c_str());
+Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	Game &game = scriptExecCtx.getGame();
+
+	if (_waitForPrevious) {
+		if (game.getActiveSayTask()) {
+			return InProgress;
+		}
+	}
+
+	TaskPtr task(new SayTask(_lineToSay, game.getGameData()._color));
+	game.getTaskManager().startTask(task);
+
 	return Finished;
 }
 
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index 8411e90..6a3b204 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -57,11 +57,10 @@ bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &,
 Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) {
 	if (!_task) {
 		_task = TaskPtr(new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo, _mode));
-		scriptExeCtx.getGame().getTaskManager().addTask(_task);
+		scriptExeCtx.getGame().getTaskManager().startTask(_task);
 	}
 
 	if (_task->getState() == Task::FINISHED) {
-		scriptExeCtx.getGame().getTaskManager().removeTask(_task);
 		_task.reset();
 
 		return Command::Finished;
diff --git a/engines/mutationofjb/font.cpp b/engines/mutationofjb/font.cpp
index 4350b19..4f4a0ee 100644
--- a/engines/mutationofjb/font.cpp
+++ b/engines/mutationofjb/font.cpp
@@ -77,7 +77,7 @@ bool Font::load(const Common::String &fileName) {
 void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) const {
 	GlyphMap::iterator it = _glyphs.find(glyph);
 	if (it == _glyphs.end()) {
-		warning("Glyph %d not found", glyph);
+		// Missing glyph is a common situation in the game and it's okay to ignore it.
 		return;
 	}
 
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 9f5f469..787bb7b 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -42,8 +42,8 @@ Game::Game(MutationOfJBEngine *vm)
 	_randomSource("mutationofjb"),
 	_delayedLocalScript(nullptr),
 	_gui(*this, _vm->getScreen()),
-	_scriptExecCtx(*this),
 	_currentAction(ActionInfo::Walk),
+	_scriptExecCtx(*this),
 	_taskManager(*this),
 	_assets(*this) {
 
@@ -61,7 +61,7 @@ Game::Game(MutationOfJBEngine *vm)
 
 	_gui.init();
 
-	_taskManager.addTask(TaskPtr(new ObjectAnimationTask));
+	_taskManager.startTask(TaskPtr(new ObjectAnimationTask));
 }
 
 Common::RandomSource &Game::getRandomSource() {
@@ -240,9 +240,16 @@ Assets &Game::getAssets() {
 	return _assets;
 }
 
-Graphics::Screen &Game::getScreen()
-{
+Graphics::Screen &Game::getScreen() {
 	return *_vm->getScreen();
 }
 
+TaskPtr Game::getActiveSayTask() const {
+	return _activeSayTask;
+}
+
+void Game::setActiveSayTask(const TaskPtr &sayTask) {
+	_activeSayTask = sayTask;
+}
+
 }
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index d70bd09..316dc75 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -23,13 +23,15 @@
 #ifndef MUTATIONOFJB_GAME_H
 #define MUTATIONOFJB_GAME_H
 
-#include "common/random.h"
-#include "common/scummsys.h"
 #include "mutationofjb/assets.h"
 #include "mutationofjb/gui.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/tasks/taskmanager.h"
 
+#include "common/ptr.h"
+#include "common/random.h"
+#include "common/scummsys.h"
+
 namespace Common {
 class String;
 }
@@ -44,6 +46,7 @@ class Room;
 class Door;
 class Static;
 class Bitmap;
+class SayTask;
 
 class Game {
 public:
@@ -75,6 +78,9 @@ public:
 
 	Graphics::Screen &getScreen();
 
+	TaskPtr getActiveSayTask() const;
+	void setActiveSayTask(const TaskPtr &sayTask);
+
 private:
 	bool loadGameData(bool partB);
 	void runActiveCommand();
@@ -96,6 +102,7 @@ private:
 
 	TaskManager _taskManager;
 	Assets _assets;
+	TaskPtr _activeSayTask;
 };
 
 }
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index b6295a7..54335d1 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -247,7 +247,8 @@ GameData::GameData()
 	: _currentScene(0),
 	_lastScene(0),
 	_partB(false),
-	_inventory()
+	_inventory(),
+	_color(WHITE)
 	{}
 
 Scene *GameData::getScene(uint8 sceneId) {
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 99174ee..47272c6 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -323,14 +323,16 @@ public:
 
 	bool loadFromStream(Common::ReadStream &stream);
 
-	uint8 _currentScene;
+	uint8 _currentScene; // Persistent.
 	uint8 _lastScene;
-	bool _partB;
-	Inventory _inventory;
-	Common::String _currentAPK;
+	bool _partB; // Persistent.
+	Inventory _inventory; // Persistent.
+	Common::String _currentAPK; // Persistent.
 	ConversationInfo _conversationInfo;
+	/** Current SayCommand color. */
+	uint8 _color;
 private:
-	Scene _scenes[45];
+	Scene _scenes[45]; // Persistent.
 };
 
 enum Colors {
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 39c4859..069545c 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -154,6 +154,7 @@ Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
 		warning(_("Trying to start command while another one is running."));
 		return Command::Finished;
 	}
+	getGameData()._color = WHITE; // The original game resets the color to WHITE beforing running script sections.
 	clear();
 	_activeCommand = cmd;
 	return runActiveCommand();
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index a2a0a73..d4fab56 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -54,7 +54,6 @@ void ConversationTask::start() {
 void ConversationTask::update() {
 	if (_sayTask) {
 		if (_sayTask->getState() == Task::FINISHED) {
-			getTaskManager()->removeTask(_sayTask);
 			_sayTask.reset();
 
 			switch (_substate) {
@@ -67,7 +66,7 @@ void ConversationTask::update() {
 
 				_substate = SAYING_RESPONSE;
 				createSayTasks(line);
-				getTaskManager()->addTask(_sayTask);
+				getTaskManager()->startTask(_sayTask);
 				break;
 			}
 			case SAYING_RESPONSE: {
@@ -105,7 +104,7 @@ void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint
 
 	_substate = SAYING_CHOICE;
 	createSayTasks(line);
-	getTaskManager()->addTask(_sayTask);
+	getTaskManager()->startTask(_sayTask);
 	_currentItem = &item;
 
 	if (!line->_speeches[0].isRepeating()) {
@@ -177,7 +176,7 @@ void ConversationTask::showChoicesOrPick() {
 
 		_substate = SAYING_CHOICE;
 		createSayTasks(line);
-		getTaskManager()->addTask(_sayTask);
+		getTaskManager()->startTask(_sayTask);
 		_currentItem = &item;
 
 		if (!line->_speeches[0].isRepeating()) {
@@ -192,7 +191,7 @@ void ConversationTask::showChoicesOrPick() {
 
 		_substate = SAYING_RESPONSE;
 		createSayTasks(line);
-		getTaskManager()->addTask(_sayTask);
+		getTaskManager()->startTask(_sayTask);
 		_currentItem = &item;
 
 		_haveChoices = true;
@@ -204,7 +203,7 @@ void ConversationTask::showChoicesOrPick() {
 			finish();
 		} else {
 			_sayTask = TaskPtr(new SayTask("Nothing to talk about.", _convInfo._color)); // TODO: This is hardcoded in executable. Load it.
-			getTaskManager()->addTask(_sayTask);
+			getTaskManager()->startTask(_sayTask);
 			_substate = SAYING_NO_CHOICES;
 			_currentItem = nullptr;
 		}
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
index bd89805..cfa412b 100644
--- a/engines/mutationofjb/tasks/saytask.cpp
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -34,21 +34,31 @@
 
 namespace MutationOfJB {
 
-SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(1000) {}
+SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(50 * toSay.size()) {}
 
 void SayTask::start() {
+	Game &game = getTaskManager()->getGame();
+	if (game.getActiveSayTask()) {
+		getTaskManager()->stopTask(game.getActiveSayTask());
+	}
+	game.setActiveSayTask(getTaskManager()->getTask(this));
+
+	setState(RUNNING);
 	drawSubtitle(_toSay, 160, 0, _color); // TODO: Respect PTALK and LTALK commands.
 	_timer.start();
-	setState(RUNNING);
 }
 
 void SayTask::update() {
 	_timer.update();
 
 	if (_timer.isFinished()) {
-		getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
-		setState(FINISHED);
-		return;
+		finish();
+	}
+}
+
+void SayTask::stop() {
+	if (getState() == RUNNING) {
+		finish();
 	}
 }
 
@@ -89,4 +99,14 @@ void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY,
 	_boundingBox.setHeight(lines.size() * font.getLineHeight());
 }
 
+void SayTask::finish() {
+	getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
+	setState(FINISHED);
+
+	Game &game = getTaskManager()->getGame();
+	if (game.getActiveSayTask().get() == this) {
+		game.setActiveSayTask(Common::SharedPtr<SayTask>());
+	}
+}
+
 }
diff --git a/engines/mutationofjb/tasks/saytask.h b/engines/mutationofjb/tasks/saytask.h
index 17e773d..2200436 100644
--- a/engines/mutationofjb/tasks/saytask.h
+++ b/engines/mutationofjb/tasks/saytask.h
@@ -38,9 +38,11 @@ public:
 
 	virtual void start() override;
 	virtual void update() override;
+	virtual void stop() override;
 
 private:
 	void drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color);
+	void finish();
 
 	Common::String _toSay;
 	uint8 _color;
diff --git a/engines/mutationofjb/tasks/sequentialtask.cpp b/engines/mutationofjb/tasks/sequentialtask.cpp
index 57efbb5..20b5290 100644
--- a/engines/mutationofjb/tasks/sequentialtask.cpp
+++ b/engines/mutationofjb/tasks/sequentialtask.cpp
@@ -48,7 +48,7 @@ void SequentialTask::runTasks() {
 		const TaskPtr &task = _tasks.front();
 		switch (task->getState()) {
 		case IDLE:
-			getTaskManager()->addTask(task);
+			getTaskManager()->startTask(task);
 			break;
 		case RUNNING:
 			return;
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
index 9f2acb3..8c9c0e8 100644
--- a/engines/mutationofjb/tasks/task.h
+++ b/engines/mutationofjb/tasks/task.h
@@ -44,6 +44,7 @@ public:
 
 	virtual void start() = 0;
 	virtual void update() = 0;
+	virtual void stop() { assert(false); } // Assert by default - stopping might not be safe for all tasks.
 
 	void setTaskManager(TaskManager *taskMan) { _taskManager = taskMan; }
 	TaskManager *getTaskManager() { return _taskManager; }
diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp
index 1167500..a6d4dc1 100644
--- a/engines/mutationofjb/tasks/taskmanager.cpp
+++ b/engines/mutationofjb/tasks/taskmanager.cpp
@@ -21,21 +21,39 @@
  */
 
 #include "mutationofjb/tasks/taskmanager.h"
+
 #include "mutationofjb/tasks/task.h"
 
+#include "common/translation.h"
+
 namespace MutationOfJB {
 
-void TaskManager::addTask(const TaskPtr &task) {
+void TaskManager::startTask(const TaskPtr &task) {
 	_tasks.push_back(task);
 	task->setTaskManager(this);
 	task->start();
 }
 
-void TaskManager::removeTask(const TaskPtr &task) {
+void TaskManager::stopTask(const TaskPtr &task) {
 	TaskPtrs::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
-	if (it != _tasks.end()) {
-		_tasks.erase(it);
+	if (it == _tasks.end()) {
+		warning(_("Task is not registered in TaskManager."));
+		return;
+	}
+
+	task->stop();
+	assert(task->getState() != Task::RUNNING);
+	_tasks.erase(it);
+}
+
+TaskPtr TaskManager::getTask(Task *const task) {
+	for (TaskPtrs::iterator it = _tasks.begin(); it != _tasks.end(); ++it) {
+		if (it->get() == task) {
+			return *it;
+		}
 	}
+
+	return TaskPtr();
 }
 
 void TaskManager::update() {
diff --git a/engines/mutationofjb/tasks/taskmanager.h b/engines/mutationofjb/tasks/taskmanager.h
index 299a271..29e854a 100644
--- a/engines/mutationofjb/tasks/taskmanager.h
+++ b/engines/mutationofjb/tasks/taskmanager.h
@@ -35,8 +35,30 @@ class TaskManager {
 public:
 	TaskManager(Game &game) : _game(game) {}
 
-	void addTask(const TaskPtr &task);
-	void removeTask(const TaskPtr &task);
+	/**
+	 * Adds the task to the internal list and starts it.
+	 *
+	 * When the task is finished, it is automatically removed from the list.
+	 * stopTask does not need to be called for that.
+	 */
+	void startTask(const TaskPtr &task);
+
+	/**
+	 * Stops the task and removes it from the internal list.
+	 *
+	 * Call this only if you need to explicitly stop the task (usually before it's finished).
+	 */
+	void stopTask(const TaskPtr &task);
+
+	/**
+	 * Gets task shared pointer from raw pointer.
+	 *
+	 * Since task lifetime is under control of SharedPtr, raw pointers shouldn't be used.
+	 * However, if only a raw pointer is available (e.g. this),
+	 * the method can be used to obtain a SharedPtr.
+	 */
+	TaskPtr getTask(Task* task);
+
 	void update();
 
 	Game &getGame() { return _game; }


Commit: 32df911b4d29342552556bc22dda7f2e13968d0d
    https://github.com/scummvm/scummvm/commit/32df911b4d29342552556bc22dda7f2e13968d0d
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Implement SETCOL command.

Changed paths:
  A engines/mutationofjb/commands/setcolorcommand.cpp
  A engines/mutationofjb/commands/setcolorcommand.h
    engines/mutationofjb/game.cpp
    engines/mutationofjb/module.mk
    engines/mutationofjb/script.cpp


diff --git a/engines/mutationofjb/commands/setcolorcommand.cpp b/engines/mutationofjb/commands/setcolorcommand.cpp
new file mode 100644
index 0000000..dbcb2a8
--- /dev/null
+++ b/engines/mutationofjb/commands/setcolorcommand.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mutationofjb/commands/setcolorcommand.h"
+
+#include "mutationofjb/game.h"
+#include "mutationofjb/gamedata.h"
+#include "mutationofjb/script.h"
+
+#include "common/str.h"
+
+/** @file
+ * "SETCOL " <colorString>
+ *
+ * Sets subtitle color used by SayCommand.
+ */
+
+namespace MutationOfJB {
+
+bool SetColorCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
+	if (line.size() < 8 || !line.hasPrefix("SETCOL")) {
+		return false;
+	}
+
+	const char *const colorStr = line.c_str() + 7;
+	const uint8 color = Game::colorFromString(colorStr);
+
+	command = new SetColorCommand(color);
+	return true;
+}
+
+Command::ExecuteResult SetColorCommand::execute(ScriptExecutionContext &scriptExecCtx) {
+	scriptExecCtx.getGameData()._color = _color;
+	return Finished;
+}
+
+Common::String SetColorCommand::debugString() const {
+	return Common::String::format("SETCOL %u", _color);
+}
+
+}
diff --git a/engines/mutationofjb/commands/setcolorcommand.h b/engines/mutationofjb/commands/setcolorcommand.h
new file mode 100644
index 0000000..ace36ff
--- /dev/null
+++ b/engines/mutationofjb/commands/setcolorcommand.h
@@ -0,0 +1,51 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MUTATIONOFJB_SETCOLORCOMMAND_H
+#define MUTATIONOFJB_SETCOLORCOMMAND_H
+
+#include "mutationofjb/commands/seqcommand.h"
+
+#include "common/scummsys.h"
+
+namespace MutationOfJB {
+
+class SetColorCommandParser : public SeqCommandParser {
+public:
+	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) override;
+};
+
+
+class SetColorCommand : public SeqCommand {
+public:
+	SetColorCommand(uint8 color) : _color(color) {}
+
+	virtual Command::ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
+	virtual Common::String debugString() const override;
+
+private:
+	uint8 _color;
+};
+
+}
+
+#endif
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 787bb7b..7b500d3 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -228,6 +228,10 @@ uint8 Game::colorFromString(const char *colorStr) {
 		}
 	}
 
+	if (*colorStr == 'n') {
+		return static_cast<uint8>(atoi(colorStr + 1));
+	}
+
 	warning(_("Color not found"));
 	return 0x00;
 }
diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk
index 60d450e..465f228 100644
--- a/engines/mutationofjb/module.mk
+++ b/engines/mutationofjb/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS := \
 	commands/renamecommand.o \
 	commands/saycommand.o \
 	commands/seqcommand.o \
+	commands/setcolorcommand.o \
 	commands/talkcommand.o \
 	commands/randomcommand.o \
 	tasks/conversationtask.o \
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 069545c..3e93c47 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -46,6 +46,7 @@
 #include "mutationofjb/commands/definestructcommand.h"
 #include "mutationofjb/commands/talkcommand.h"
 #include "mutationofjb/commands/randomcommand.h"
+#include "mutationofjb/commands/setcolorcommand.h"
 #include "mutationofjb/game.h"
 
 namespace MutationOfJB {
@@ -74,6 +75,7 @@ static CommandParser **getParsers() {
 		new LabelCommandParser,
 		new RandomCommandParser,
 		new RandomBlockStartParser,
+		new SetColorCommandParser,
 		nullptr
 	};
 


Commit: 4fbbaf944a392d67047dac64835f37cfcf518ecf
    https://github.com/scummvm/scummvm/commit/4fbbaf944a392d67047dac64835f37cfcf518ecf
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Extend blit_if to support both ManagedSurface and Surface.

Changed paths:
    engines/mutationofjb/room.cpp
    engines/mutationofjb/util.h


diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index dd9a6db..a396a64 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -120,8 +120,14 @@ bool Room::load(uint8 roomNumber, bool roomB) {
 
 // TODO: Take the threshold value from ATN data.
 struct ThresholdBlitOperation {
-	bool operator()(const byte /*srcColor*/, const byte destColor) {
-		return destColor <= 0xBF;
+	byte operator()(const byte srcColor, const byte destColor) {
+		if (destColor <= 0xBF) {
+			// Within threshold - replace destination with source color.
+			return srcColor;
+		}
+
+		// Outside of threshold - keep destination color.
+		return destColor;
 	}
 };
 
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 06d1a0b..51c532b 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -38,29 +38,73 @@ namespace MutationOfJB {
 void reportFileMissingError(const char *fileName);
 Common::String toUpperCP895(const Common::String &str);
 
+template <typename SurfaceType>
+void clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &destSurf) {
+	// Clip the bounds if source is too big to fit into destination.
+	if (destBounds.right > destSurf.w) {
+		srcBounds.right -= destBounds.right - destSurf.w;
+		destBounds.right = destSurf.w;
+	}
+
+	if (destBounds.bottom > destSurf.h) {
+		srcBounds.bottom -= destBounds.bottom - destSurf.h;
+		destBounds.bottom = destSurf.h;
+	}
+
+	if (destBounds.top < 0) {
+		srcBounds.top += -destBounds.top;
+		destBounds.top = 0;
+	}
+
+	if (destBounds.left < 0) {
+		srcBounds.left += -destBounds.left;
+		destBounds.left = 0;
+	}
+}
+
 template <typename BlitOp>
-void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
-	const Common::Rect srcBounds = srcRect;
-	const Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
+void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::Surface &dest, const Common::Point &destPos, BlitOp blitOp) {
+	Common::Rect srcBounds = srcRect;
+	Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
 
 	assert(srcRect.isValidRect());
 	assert(dest.format == src.format);
 
+	clipBounds(srcBounds, destBounds, dest);
+
 	for (int y = 0; y < srcBounds.height(); ++y) {
 		const byte *srcP = reinterpret_cast<const byte *>(src.getBasePtr(srcBounds.left, srcBounds.top + y));
 		const byte *srcEndP = srcP + srcBounds.width();
 		byte *destP = reinterpret_cast<byte *>(dest.getBasePtr(destBounds.left, destBounds.top + y));
 
 		while (srcP != srcEndP) {
-			if (blitOp(*srcP, *destP)) {
-				*destP = *srcP;
+			const byte newColor = blitOp(*srcP, *destP);
+			if (*destP != newColor) {
+				*destP = newColor;
 			}
 			++srcP;
 			++destP;
 		}
 	}
+}
 
-	dest.getSubArea(destBounds); // This is a hack to invalidate the destination rectangle.
+template <typename BlitOp>
+void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
+	Common::Rect srcBounds = srcRect;
+	Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
+
+	assert(srcRect.isValidRect());
+	assert(dest.format == src.format);
+
+	clipBounds(srcBounds, destBounds, dest);
+
+	Graphics::Surface destSurf = dest.getSubArea(destBounds); // This will invalidate the rectangle.
+	blit_if(src, srcRect, destSurf, Common::Point(0, 0), blitOp);
+}
+
+template <typename BlitOp>
+void blit_if(const Graphics::Surface &src, Graphics::Surface &dest, const Common::Point &destPos, BlitOp blitOp) {
+	blit_if(src, Common::Rect(0, 0, src.w, src.h), dest, destPos, blitOp);
 }
 
 template <typename BlitOp>


Commit: cd15dd82a252c3bca1f96a96c4d2c5a2bf4387d9
    https://github.com/scummvm/scummvm/commit/cd15dd82a252c3bca1f96a96c4d2c5a2bf4387d9
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Check for out of bounds destination in blit_if.

Changed paths:
    engines/mutationofjb/util.h


diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index 51c532b..ac1239d 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -38,8 +38,13 @@ namespace MutationOfJB {
 void reportFileMissingError(const char *fileName);
 Common::String toUpperCP895(const Common::String &str);
 
+// Taken from ManagedSurface::clip.
 template <typename SurfaceType>
-void clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &destSurf) {
+bool clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &destSurf) {
+	if (destBounds.left >= destSurf.w || destBounds.top >= destSurf.h ||
+			destBounds.right <= 0 || destBounds.bottom <= 0)
+		return false;
+
 	// Clip the bounds if source is too big to fit into destination.
 	if (destBounds.right > destSurf.w) {
 		srcBounds.right -= destBounds.right - destSurf.w;
@@ -60,6 +65,8 @@ void clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &
 		srcBounds.left += -destBounds.left;
 		destBounds.left = 0;
 	}
+
+	return true;
 }
 
 template <typename BlitOp>
@@ -70,7 +77,8 @@ void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics
 	assert(srcRect.isValidRect());
 	assert(dest.format == src.format);
 
-	clipBounds(srcBounds, destBounds, dest);
+	if (!clipBounds(srcBounds, destBounds, dest))
+		return;
 
 	for (int y = 0; y < srcBounds.height(); ++y) {
 		const byte *srcP = reinterpret_cast<const byte *>(src.getBasePtr(srcBounds.left, srcBounds.top + y));
@@ -96,7 +104,8 @@ void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics
 	assert(srcRect.isValidRect());
 	assert(dest.format == src.format);
 
-	clipBounds(srcBounds, destBounds, dest);
+	if (!clipBounds(srcBounds, destBounds, dest))
+		return;
 
 	Graphics::Surface destSurf = dest.getSubArea(destBounds); // This will invalidate the rectangle.
 	blit_if(src, srcRect, destSurf, Common::Point(0, 0), blitOp);


Commit: 298bfc3d109110b8f4332083b4f9aca850f43eea
    https://github.com/scummvm/scummvm/commit/298bfc3d109110b8f4332083b4f9aca850f43eea
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Subclass Graphics::Font to reuse existing code.

Changed paths:
    engines/mutationofjb/font.cpp
    engines/mutationofjb/font.h
    engines/mutationofjb/tasks/saytask.cpp
    engines/mutationofjb/widgets/conversationwidget.cpp


diff --git a/engines/mutationofjb/font.cpp b/engines/mutationofjb/font.cpp
index 4f4a0ee..f87f25b 100644
--- a/engines/mutationofjb/font.cpp
+++ b/engines/mutationofjb/font.cpp
@@ -29,7 +29,8 @@ namespace MutationOfJB {
 
 Font::Font(const Common::String &fileName, int horizSpacing, int lineHeight) :
 	_horizSpacing(horizSpacing),
-	_lineHeight(lineHeight) {
+	_lineHeight(lineHeight),
+	_maxCharWidth(0) {
 
 	load(fileName);
 }
@@ -62,6 +63,10 @@ bool Font::load(const Common::String &fileName) {
 			file.read(surf.getBasePtr(0, h), width);
 		}
 
+		if (width > _maxCharWidth) {
+			_maxCharWidth = width;
+		}
+
 		if (height > maxHeight) {
 			maxHeight = height;
 		}
@@ -74,75 +79,69 @@ bool Font::load(const Common::String &fileName) {
 	return true;
 }
 
-void Font::drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) const {
-	GlyphMap::iterator it = _glyphs.find(glyph);
+int Font::getFontHeight() const {
+	return _lineHeight;
+}
+
+int Font::getMaxCharWidth() const {
+	return _maxCharWidth;
+}
+
+int Font::getCharWidth(uint32 chr) const {
+	GlyphMap::iterator it = _glyphs.find(chr);
 	if (it == _glyphs.end()) {
-		// Missing glyph is a common situation in the game and it's okay to ignore it.
-		return;
+		return 0;
 	}
+	return it->_value.w;
+}
 
-	Graphics::ManagedSurface &glyphSurface = it->_value;
+int Font::getKerningOffset(uint32 left, uint32 right) const {
+	if (left == 0) {
+		// Do not displace the first character.
+		return 0;
+	}
 
-	Graphics::ManagedSurface tmp(glyphSurface);
-	for (int h = 0; h < tmp.h; ++h) {
-		uint8 *ptr = reinterpret_cast<uint8 *>(tmp.getBasePtr(0, h));
-		for (int w = 0; w < tmp.w; ++w) {
-			if (*ptr != 0) {
-				*ptr = transformColor(baseColor, *ptr);
-			}
-			ptr++;
-		}
+	if (_glyphs.find(left) == _glyphs.end()) {
+		// Missing glyphs must not create extra displacement.
+		// FIXME: This way is not completely correct, as if the last character is
+		// missing a glyph, it will still create extra displacement. This should
+		// not affect the visuals but it might affect getStringWidth() / getBoundingBox().
+		return 0;
 	}
-	surf.transBlitFrom(tmp.rawSurface(), Common::Point(x, y));
 
-	x += glyphSurface.w + _horizSpacing;
+	return _horizSpacing;
 }
 
-int16 Font::getWidth(const Common::String &str) const {
-	int16 width = 0;
-	for (uint i = 0; i < str.size(); ++i) {
-		GlyphMap::iterator it = _glyphs.find(str[i]);
-		if (it == _glyphs.end()) {
-			continue;
+class FontBlitOperation {
+public:
+	FontBlitOperation(const Font &font, const byte baseColor)
+		: _font(font),
+		  _baseColor(baseColor) {}
+
+	byte operator()(const byte srcColor, const byte destColor) {
+		if (srcColor == 0) {
+			// Transparency - keep destination color.
+			return destColor;
 		}
 
-		width += it->_value.w + _horizSpacing;
+		// Replace destination with transformed source color.
+		return _font.transformColor(_baseColor, srcColor);
 	}
-	return width;
-}
 
-int Font::getLineHeight() const {
-	return _lineHeight;
-}
+private:
+	const Font &_font;
+	const byte _baseColor;
+};
 
-void Font::drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) const {
-	for (uint i = 0; i < str.size(); ++i) {
-		drawGlyph(str[i], baseColor, x, y, surf); // "x" is updated.
+void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	GlyphMap::iterator it = _glyphs.find(chr);
+	if (it == _glyphs.end()) {
+		// Missing glyph is a common situation in the game and it's okay to ignore it.
+		return;
 	}
-}
 
-void Font::wordWrap(const Common::String &str, int16 maxLineWidth, Common::Array<Common::String> &lines) const {
-	lines.push_back("");
-
-	for (Common::String::const_iterator it = str.begin(); it != str.end();) {
-		Common::String::const_iterator partStart = it;
-		it = Common::find(partStart, str.end(), ' ');
-		if (it != str.end()) {
-			while (*it == ' ' && it != str.end()) {
-				++it;
-			}
-		}
-
-		Common::String part(partStart, it); // Word + following whitespace
-		Common::String line = lines.back() + part;
-		if (getWidth(line) <= maxLineWidth) {
-			// The part fits in the current line
-			lines.back() = line;
-		} else {
-			// The part must go to the next line
-			lines.push_back(part);
-		}
-	}
+	Graphics::ManagedSurface &glyphSurface = it->_value;
+	blit_if(glyphSurface, *dst, Common::Point(x, y), FontBlitOperation(*this, color));
 }
 
 uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) const {
diff --git a/engines/mutationofjb/font.h b/engines/mutationofjb/font.h
index a27303e..51825db 100644
--- a/engines/mutationofjb/font.h
+++ b/engines/mutationofjb/font.h
@@ -25,7 +25,9 @@
 
 #include "common/scummsys.h"
 #include "common/hashmap.h"
+#include "graphics/font.h"
 #include "graphics/managed_surface.h"
+#include "graphics/surface.h"
 
 namespace Common {
 class String;
@@ -33,24 +35,26 @@ class String;
 
 namespace MutationOfJB {
 
-class Font {
+class Font : public Graphics::Font {
+	friend class FontBlitOperation;
 public:
-	Font(const Common::String &fileName, int horizSpacing, int vertSpacing);
-	virtual ~Font() {}
-	int getLineHeight() const;
-	int16 getWidth(const Common::String &text) const;
-	void drawString(const Common::String &str, uint8 baseColor, int16 x, int16 y, Graphics::ManagedSurface &surf) const;
-	void wordWrap(const Common::String &str, int16 maxLineWidth, Common::Array<Common::String> &lines) const;
+	Font(const Common::String &fileName, int horizSpacing, int lineHeight);
+
+	virtual int getFontHeight() const override;
+	virtual int getMaxCharWidth() const override;
+	virtual int getCharWidth(uint32 chr) const override;
+	virtual int getKerningOffset(uint32 left, uint32 right) const override;
+	virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
 protected:
 	virtual uint8 transformColor(uint8 baseColor, uint8 glyphColor) const;
 
 private:
-	void drawGlyph(uint8 glyph, uint8 baseColor, int16 &x, int16 &y, Graphics::ManagedSurface &surf) const;
 	bool load(const Common::String &fileName);
 
 	int _horizSpacing;
 	int _lineHeight;
+	int _maxCharWidth;
 	typedef Common::HashMap<uint8, Graphics::ManagedSurface> GlyphMap;
 	GlyphMap _glyphs;
 };
diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp
index cfa412b..21a7dea 100644
--- a/engines/mutationofjb/tasks/saytask.cpp
+++ b/engines/mutationofjb/tasks/saytask.cpp
@@ -68,35 +68,27 @@ void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY,
 	const Font &font = getTaskManager()->getGame().getAssets().getSpeechFont();
 
 	Common::Array<Common::String> lines;
-	font.wordWrap(text, MAX_LINE_WIDTH, lines);
+	const int16 actualMaxWidth = font.wordWrapText(text, MAX_LINE_WIDTH, lines);
 
 	// Get the x, y coordinates of the top center point of the text's bounding box
 	// from the (rather strange) talk coordinates coming from scripts.
 	int16 x = talkX;
-	int16 y = talkY - (lines.size() - 1) * font.getLineHeight() - 15;
+	int16 y = talkY - (lines.size() - 1) * font.getFontHeight() - 15;
 
 	// Clamp to screen edges.
+	x = CLIP<int16>(x, 3 + actualMaxWidth / 2, 317 - actualMaxWidth / 2);
 	y = MAX<int16>(y, 3);
-	int16 maxWidth = 0;
-	for (uint i = 0; i < lines.size(); i++) {
-		int16 lineWidth = font.getWidth(lines[i]);
-		if (lineWidth > maxWidth) {
-			maxWidth = lineWidth;
-		}
-		x = MAX<int16>(x, 3 + lineWidth / 2);
-		x = MIN<int16>(x, 317 - lineWidth / 2);
-	}
+
+	// Remember the area occupied by the text.
+	_boundingBox.left = x - actualMaxWidth / 2;
+	_boundingBox.top = y;
+	_boundingBox.setWidth(actualMaxWidth);
+	_boundingBox.setHeight(lines.size() * font.getFontHeight());
 
 	// Draw lines.
 	for (uint i = 0; i < lines.size(); i++) {
-		font.drawString(lines[i], color, x - font.getWidth(lines[i]) / 2, y + i * font.getLineHeight(), getTaskManager()->getGame().getScreen());
+		font.drawString(&getTaskManager()->getGame().getScreen(), lines[i], _boundingBox.left, _boundingBox.top + i * font.getFontHeight(), _boundingBox.width(), color, Graphics::kTextAlignCenter);
 	}
-
-	// Remember the area occupied by the text.
-	_boundingBox.top = x - maxWidth / 2;
-	_boundingBox.left = y;
-	_boundingBox.setWidth(maxWidth);
-	_boundingBox.setHeight(lines.size() * font.getLineHeight());
 }
 
 void SayTask::finish() {
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index a46b9c5..24bb5e1 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -69,7 +69,7 @@ void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
 		}
 
 		// TODO: Active line should be WHITE.
-		_gui.getGame().getAssets().getSystemFont().drawString(str, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface);
+		_gui.getGame().getAssets().getSystemFont().drawString(&surface, str, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, _area.width(), LIGHTGRAY);
 	}
 }
 


Commit: 6ff609c51478c07a00018e57d8149f09d97677e7
    https://github.com/scummvm/scummvm/commit/6ff609c51478c07a00018e57d8149f09d97677e7
Author: Miroslav Remák (miroslavr256 at gmail.com)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Improve documentation for statics.

Changed paths:
    engines/mutationofjb/gamedata.h


diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 47272c6..fc755f7 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -164,7 +164,7 @@ struct Object {
 };
 
 /**
- * An interactable area without a visual representation.
+ * An interactable area, usually without a visual representation.
  */
 struct Static {
 	/** Whether you can mouse over and interact with the static (AC register). */
@@ -175,9 +175,14 @@ struct Static {
 	 * If it starts with '~', the static has an implicit "pickup" action that adds
 	 * an item with the same name (except '`' replaces '~') to your inventory and
 	 * disables the static. If there is a matching scripted "pickup" action, it
-	 * overrides the implicit action.
+	 * overrides the implicit action. This kind of static also has graphics in the
+	 * form of its rectangle extracted from room frame 2 (and 3 after pickup).
+	 *
+	 * If it ends with '[', the "use" action allows combining the static with another
+	 * entity.
 	 *
 	 * TODO: Support '~' statics.
+	 * TODO: Support combinable statics.
 	 */
 	char _name[MAX_ENTITY_NAME_LENGTH + 1];
 	/** X coordinate of the static rectangle (XX register). */


Commit: a25715a29b0587ae6795eef63a575172e2cb971d
    https://github.com/scummvm/scummvm/commit/a25715a29b0587ae6795eef63a575172e2cb971d
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix code formatting issues (with astyle).

Changed paths:
    engines/mutationofjb/animationdecoder.cpp
    engines/mutationofjb/assets.cpp
    engines/mutationofjb/assets.h
    engines/mutationofjb/commands/additemcommand.cpp
    engines/mutationofjb/commands/callmacrocommand.cpp
    engines/mutationofjb/commands/camefromcommand.cpp
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/changecommand.h
    engines/mutationofjb/commands/definestructcommand.cpp
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/gotocommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifcommand.h
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.h
    engines/mutationofjb/commands/ifpiggycommand.cpp
    engines/mutationofjb/commands/labelcommand.cpp
    engines/mutationofjb/commands/newroomcommand.cpp
    engines/mutationofjb/commands/randomcommand.cpp
    engines/mutationofjb/commands/removeallitemscommand.cpp
    engines/mutationofjb/commands/removeitemcommand.cpp
    engines/mutationofjb/commands/renamecommand.cpp
    engines/mutationofjb/commands/saycommand.cpp
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/conversationlinelist.h
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/encryptedfile.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/gui.cpp
    engines/mutationofjb/gui.h
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/script.h
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/conversationtask.h
    engines/mutationofjb/tasks/task.h
    engines/mutationofjb/tasks/taskmanager.h
    engines/mutationofjb/util.cpp
    engines/mutationofjb/util.h
    engines/mutationofjb/widgets/buttonwidget.cpp
    engines/mutationofjb/widgets/conversationwidget.cpp
    engines/mutationofjb/widgets/conversationwidget.h
    engines/mutationofjb/widgets/inventorywidget.cpp
    engines/mutationofjb/widgets/inventorywidget.h


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
index 8132385..712cb42 100644
--- a/engines/mutationofjb/animationdecoder.cpp
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -114,7 +114,7 @@ void AnimationDecoder::loadPalette(Common::SeekableReadStream &file) {
 		copyCount = PALETTE_COLORS;
 	}
 
-	while(packets--) {
+	while (packets--) {
 		file.read(_palette + skipCount * 3, copyCount * 3);
 
 		for (int j = skipCount * 3; j < (skipCount + copyCount) * 3; ++j) {
@@ -145,7 +145,7 @@ void AnimationDecoder::loadFullFrame(EncryptedFile &file, uint32 size) {
 				// RLE - Copy color n times.
 				uint8 color = file.readByte();
 				readBytes++;
-				while(n--) {
+				while (n--) {
 					*ptr++ = color;
 				}
 			} else {
diff --git a/engines/mutationofjb/assets.cpp b/engines/mutationofjb/assets.cpp
index d532469..2adda51 100644
--- a/engines/mutationofjb/assets.cpp
+++ b/engines/mutationofjb/assets.cpp
@@ -26,11 +26,11 @@ namespace MutationOfJB {
 
 Assets::Assets(Game &game) : _game(game), _toSayList("tosay.ger"), _responseList("response.ger") {}
 
-Font& Assets::getSystemFont() {
+Font &Assets::getSystemFont() {
 	return _systemFont;
 }
 
-Font& Assets::getSpeechFont() {
+Font &Assets::getSpeechFont() {
 	return _speechFont;
 }
 
diff --git a/engines/mutationofjb/assets.h b/engines/mutationofjb/assets.h
index 1d47641..2cd0612 100644
--- a/engines/mutationofjb/assets.h
+++ b/engines/mutationofjb/assets.h
@@ -34,11 +34,11 @@ class Assets {
 public:
 	Assets(Game &game);
 
-	Font& getSystemFont();
-	Font& getSpeechFont();
+	Font &getSystemFont();
+	Font &getSpeechFont();
 
-	ConversationLineList& getToSayList();
-	ConversationLineList& getResponseList();
+	ConversationLineList &getToSayList();
+	ConversationLineList &getResponseList();
 
 private:
 	Game &_game;
diff --git a/engines/mutationofjb/commands/additemcommand.cpp b/engines/mutationofjb/commands/additemcommand.cpp
index 1a670f7..67d3e13 100644
--- a/engines/mutationofjb/commands/additemcommand.cpp
+++ b/engines/mutationofjb/commands/additemcommand.cpp
@@ -24,11 +24,11 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 
-/*
-	"ADDITEM" " " <item>
-
-	Adds item to inventory.
-*/
+/** @file
+ * "ADDITEM " <item>
+ *
+ * Adds item to inventory.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/callmacrocommand.cpp b/engines/mutationofjb/commands/callmacrocommand.cpp
index 49b948c..01470f2 100644
--- a/engines/mutationofjb/commands/callmacrocommand.cpp
+++ b/engines/mutationofjb/commands/callmacrocommand.cpp
@@ -25,11 +25,11 @@
 #include "mutationofjb/game.h"
 #include "common/translation.h"
 
-/*
-	"_" <name>
-
-	Calls macro with the specified name.
-*/
+/** @file
+ * "_" <name>
+ *
+ * Calls macro with the specified name.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/camefromcommand.cpp b/engines/mutationofjb/commands/camefromcommand.cpp
index 0583187..485e0c5 100644
--- a/engines/mutationofjb/commands/camefromcommand.cpp
+++ b/engines/mutationofjb/commands/camefromcommand.cpp
@@ -25,13 +25,13 @@
 #include "mutationofjb/script.h"
 #include "common/str.h"
 
-/*
-	"CAMEFROM" <sceneId>
-
-	This command tests whether last scene (the scene player came from) is sceneId.
-	If true, the execution continues after this command.
-	Otherwise the execution continues after first '#' found.
-*/
+/** @file
+ * "CAMEFROM" <sceneId>
+ *
+ * This command tests whether last scene (the scene player came from) is sceneId.
+ * If true, the execution continues after this command.
+ * Otherwise the execution continues after first '#' found.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index 4eac41e..5d6833b 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -27,15 +27,21 @@
 
 namespace MutationOfJB {
 
-// CHANGEe rr ss ii val
-// <e>   1B  Entity to change register for.
-//           D door
-//           O object
-//           S static
-// <rr>  2B  Register name.
-// <ss>  2B  Scene ID.
-// <ii>  2B  Entity ID.
-// <val> VL  Value.
+/** @file
+ * "CHANGE" <entity> " " <register> " " <sceneId> " " <entityId> " " <value>
+ *
+ * Changes entity register value for specified scene.
+ * <entity>   1B  Entity to change register for.
+ *                Possible values:
+ *                  'D' - door
+ *                  'O' - object
+ *                  'S' - static
+ *                  ''  - scene
+ * <register> 2B  Register name.
+ * <sceneId>  2B  Scene ID.
+ * <entityid> 2B  Entity ID.
+ * <value>    *B  Value (variable length).
+ */
 
 bool ChangeCommandParser::parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
 	if (changeEntity) {
@@ -102,7 +108,7 @@ bool ChangeCommandParser::parseValueString(const Common::String &valueString, bo
 		ccv._byteVal = parseInteger(val, op);
 	} else if (valueString.hasPrefix("FR")) {
 		reg = ChangeCommand::FR;
-		ccv._byteVal = parseInteger(val, op); 
+		ccv._byteVal = parseInteger(val, op);
 	} else if (valueString.hasPrefix("NA")) {
 		reg = ChangeCommand::NA;
 		ccv._byteVal = parseInteger(val, op);
@@ -234,32 +240,58 @@ int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOper
 
 const char *ChangeCommand::getRegisterAsString() const {
 	switch (_register) {
-	case NM: return "NM";
-	case LT: return "LT";
-	case SX: return "SX";
-	case SY: return "SY";
-	case XX: return "XX";
-	case YY: return "YY";
-	case XL: return "XL";
-	case YL: return "YL";
-	case WX: return "WX";
-	case WY: return "WY";
-	case SP: return "SP";
-	case AC: return "AC";
-	case FA: return "FA";
-	case FR: return "FR";
-	case NA: return "NA";
-	case FS: return "FS";
-	case CA: return "CA";
-	case DS: return "DS";
-	case DL: return "DL";
-	case ND: return "ND";
-	case NO: return "NO";
-	case NS: return "NS";
-	case PF: return "PF";
-	case PL: return "PL";
-	case PD: return "PD";
-	default: return "(unknown)";
+	case NM:
+		return "NM";
+	case LT:
+		return "LT";
+	case SX:
+		return "SX";
+	case SY:
+		return "SY";
+	case XX:
+		return "XX";
+	case YY:
+		return "YY";
+	case XL:
+		return "XL";
+	case YL:
+		return "YL";
+	case WX:
+		return "WX";
+	case WY:
+		return "WY";
+	case SP:
+		return "SP";
+	case AC:
+		return "AC";
+	case FA:
+		return "FA";
+	case FR:
+		return "FR";
+	case NA:
+		return "NA";
+	case FS:
+		return "FS";
+	case CA:
+		return "CA";
+	case DS:
+		return "DS";
+	case DL:
+		return "DL";
+	case ND:
+		return "ND";
+	case NO:
+		return "NO";
+	case NS:
+		return "NS";
+	case PF:
+		return "PF";
+	case PL:
+		return "PL";
+	case PD:
+		return "PD";
+	default:
+		return "(unknown)";
 	}
 }
 
diff --git a/engines/mutationofjb/commands/changecommand.h b/engines/mutationofjb/commands/changecommand.h
index 6fa090e..a88cdc1 100644
--- a/engines/mutationofjb/commands/changecommand.h
+++ b/engines/mutationofjb/commands/changecommand.h
@@ -44,7 +44,7 @@ public:
 		YL, // Height
 		WX, // Walk to X
 		WY, // Walk to Y
-		SP, // 
+		SP, //
 		AC, // Active
 		FA, // First animation
 		FR,
@@ -67,7 +67,7 @@ public:
 		SubtractValue
 	};
 
-	ChangeCommand(uint8 sceneId, uint8 entityId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue& val) :
+	ChangeCommand(uint8 sceneId, uint8 entityId, ChangeRegister reg, ChangeOperation op, const ChangeCommandValue &val) :
 		_sceneId(sceneId), _entityId(entityId), _register(reg), _operation(op), _value(val)
 	{}
 protected:
diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp
index 93dbfc8..4ccf2e8 100644
--- a/engines/mutationofjb/commands/definestructcommand.cpp
+++ b/engines/mutationofjb/commands/definestructcommand.cpp
@@ -50,7 +50,7 @@ bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseCon
 			continue;
 		}
 
-		const char* linePtr = convLineStr.c_str();
+		const char *linePtr = convLineStr.c_str();
 
 		ConversationInfo::Line convLine;
 
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index b883bee..c7fbd41 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -27,29 +27,29 @@
 #include "common/debug.h"
 #include "common/translation.h"
 
-/*
-	("#L " | "-L ") <object>
-	("#W " | "-W ") <object>
-	("#T " | "-T ") <object>
-	("#P " | "-P ") <object1>
-	("#U " | "-U ") <object1> [<object2>]
-	("#ELSE" | "-ELSE") [<tag>]
-	"#MACRO " <name>
-	"#EXTRA" <name>
-
-	If a line starts with '#', '=', '-', it is treated as the end of a section.
-	However, at the same time it can also start a new section depending on what follows.
-
-	#L (look), #W (walk), #T (talk), #U (use) sections are executed
-	when the user starts corresponding action on the object or in case of "use" up to two objects.
-	The difference between '#' and '-' version is whether the player walks towards the object ('#') or not ('-').
-
-	#ELSE is used by conditional commands (see comments for IfCommand and others).
-
-	#MACRO starts a new macro. Global script can call macros from local script and vice versa.
-
-	#EXTRA defines an "extra" section. This is called from dialog responses ("TALK TO HIM" command).
-*/
+/** @file
+ * ("#L " | "-L ") <object>
+ * ("#W " | "-W ") <object>
+ * ("#T " | "-T ") <object>
+ * ("#P " | "-P ") <object1>
+ * ("#U " | "-U ") <object1> [<object2>]
+ * ("#ELSE" | "-ELSE") [<tag>]
+ * "#MACRO " <name>
+ * "#EXTRA" <name>
+ *
+ * If a line starts with '#', '=', '-', it is treated as the end of a section.
+ * However, at the same time it can also start a new section depending on what follows.
+ *
+ * #L (look), #W (walk), #T (talk), #U (use) sections are executed
+ * when the user starts corresponding action on the object or in case of "use" up to two objects.
+ * The difference between '#' and '-' version is whether the player walks towards the object ('#') or not ('-').
+ *
+ * #ELSE is used by conditional commands (see comments for IfCommand and others).
+ *
+ * #MACRO starts a new macro. Global script can call macros from local script and vice versa.
+ *
+ * #EXTRA defines an "extra" section. This is called from dialog responses ("TALK TO HIM" command).
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/gotocommand.cpp b/engines/mutationofjb/commands/gotocommand.cpp
index bc24e2c..b8d15a7 100644
--- a/engines/mutationofjb/commands/gotocommand.cpp
+++ b/engines/mutationofjb/commands/gotocommand.cpp
@@ -25,11 +25,11 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 
-/*
-	"GOTO " <label>
-
-	Jumps to a label.
-*/
+/** @file
+ * "GOTO " <label>
+ *
+ * Jumps to a label.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index b5f03fc..06fb5ca 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -26,24 +26,24 @@
 #include "common/str.h"
 #include "common/translation.h"
 
-/*
-	"IF" <tag> <sceneId> <objectId> <value> ["!"]
-
-	IF command compares the value of the WX pseudo-register of the object in the specified scene.
-	If the values match, execution continues to the next line.
-	Otherwise execution continues after first "#ELSE" or "=ELSE" with the same <tag>.
-	The logic can be reversed with exclamation mark at the end.
-
-	<tag> is always 1 character long, <sceneId> and <objectId> 2 characters long.
-
-	Please note that this does not work like you are used to from saner languages.
-	IF does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
-		IF something
-		IF something else
-		#ELSE
-		...
-	This is effectively logical AND.
-*/
+/** @file
+ * "IF" <tag> <sceneId> <objectId> <value> ["!"]
+ *
+ * IF command compares the value of the WX pseudo-register of the object in the specified scene.
+ * If the values match, execution continues to the next line.
+ * Otherwise execution continues after first "#ELSE" or "=ELSE" with the same <tag>.
+ * The logic can be reversed with exclamation mark at the end.
+ *
+ * <tag> is always 1 character long, <sceneId> and <objectId> 2 characters long.
+ *
+ * Please note that this does not work like you are used to from saner languages.
+ * IF does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+ *   IF something
+ *   IF something else
+ *   #ELSE
+ *   ...
+ * This is effectively logical AND.
+ */
 
 namespace MutationOfJB {
 
@@ -58,7 +58,7 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &, Co
 	if (line.size() < 10) {
 		return false;
 	}
-	
+
 	if (!line.hasPrefix("IF")) {
 		return false;
 	}
diff --git a/engines/mutationofjb/commands/ifcommand.h b/engines/mutationofjb/commands/ifcommand.h
index b04d8a3..04174d9 100644
--- a/engines/mutationofjb/commands/ifcommand.h
+++ b/engines/mutationofjb/commands/ifcommand.h
@@ -39,7 +39,7 @@ private:
 class IfCommand : public ConditionalCommand {
 public:
 	IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative);
-	
+
 	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index 7a58cee..d70add9 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -27,22 +27,22 @@
 #include "common/str.h"
 #include "common/translation.h"
 
-/*
-	"IFITEM" <item> ["!"]
-
-	IFITEM command tests whether an item is in the inventory.
-	If it is, execution continues to the next line.
-	Otherwise execution continues after first "#ELSE" or "=ELSE".
-	The logic can be reversed with exclamation mark at the end.
-
-	Please note that this does not work like you are used to from saner languages.
-	IFITEM does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
-		IFITEM item1
-		IFITEM item2
-		#ELSE
-		...
-	This is effectively logical AND.
-*/
+/** @file
+ * "IFITEM" <item> ["!"]
+ *
+ * IFITEM command tests whether an item is in the inventory.
+ * If it is, execution continues to the next line.
+ * Otherwise execution continues after first "#ELSE" or "=ELSE".
+ * The logic can be reversed with exclamation mark at the end.
+ *
+ * Please note that this does not work like you are used to from saner languages.
+ * IFITEM does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+ *   IFITEM item1
+ *   IFITEM item2
+ *   #ELSE
+ *   ...
+ * This is effectively logical AND.
+ */
 
 namespace MutationOfJB {
 
@@ -50,7 +50,7 @@ bool IfItemCommandParser::parse(const Common::String &line, ScriptParseContext &
 	if (line.size() < 8) {
 		return false;
 	}
-	
+
 	if (!line.hasPrefix("IFITEM")) {
 		return false;
 	}
diff --git a/engines/mutationofjb/commands/ifitemcommand.h b/engines/mutationofjb/commands/ifitemcommand.h
index df073b9..86a5ad2 100644
--- a/engines/mutationofjb/commands/ifitemcommand.h
+++ b/engines/mutationofjb/commands/ifitemcommand.h
@@ -39,7 +39,7 @@ public:
 class IfItemCommand : public ConditionalCommand {
 public:
 	IfItemCommand(const Common::String &item, bool negative);
-	
+
 	virtual ExecuteResult execute(ScriptExecutionContext &scriptExecCtx) override;
 	virtual Common::String debugString() const;
 
diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
index e30213a..e3107806 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.cpp
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -27,21 +27,21 @@
 #include "common/str.h"
 #include "common/translation.h"
 
-/*
-	"IFPIGGY"
-
-	IFPIGGY command tests whether current loaded APK file (character animation) is "piggy.apk".
-	If it is, execution continues to the next line.
-	Otherwise execution continues after first "#ELSE" or "=ELSE".
-
-	Please note that this does not work like you are used to from saner languages.
-	IFPIGGY does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
-		IFPIGGY
-		IFITEM someitem
-		#ELSE
-		...
-	This is effectively logical AND.
-*/
+/** @file
+ * "IFPIGGY"
+ *
+ * IFPIGGY command tests whether current loaded APK file (character animation) is "piggy.apk".
+ * If it is, execution continues to the next line.
+ * Otherwise execution continues after first "#ELSE" or "=ELSE".
+ *
+ * Please note that this does not work like you are used to from saner languages.
+ * IFPIGGY does not have any blocks. It only searches for first #ELSE, so you can have stuff like:
+ *   IFPIGGY
+ *   IFITEM someitem
+ *   #ELSE
+ *   ...
+ * This is effectively logical AND.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
index de6de02..7593e52 100644
--- a/engines/mutationofjb/commands/labelcommand.cpp
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -24,11 +24,11 @@
 #include "mutationofjb/commands/gotocommand.h"
 #include "mutationofjb/script.h"
 
-/*
-	<label> ":"
-
-	Creates a label.
-*/
+/** @file
+ * <label> ":"
+ *
+ * Creates a label.
+ */
 
 namespace MutationOfJB {
 
@@ -59,8 +59,7 @@ bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &p
 	return true;
 }
 
-const Common::String &LabelCommand::getName() const
-{
+const Common::String &LabelCommand::getName() const {
 	return _name;
 }
 
diff --git a/engines/mutationofjb/commands/newroomcommand.cpp b/engines/mutationofjb/commands/newroomcommand.cpp
index 818ef7d..5aabc67 100644
--- a/engines/mutationofjb/commands/newroomcommand.cpp
+++ b/engines/mutationofjb/commands/newroomcommand.cpp
@@ -26,15 +26,15 @@
 #include "mutationofjb/gamedata.h"
 #include "common/str.h"
 
-/*
-	"NEWROOM " <sceneId> " " <x> " " <y> " " <frame>
-
-	NEWROOM changes the current scene. While doing that, it also executes STARTUP section for the new room.
-	However, after that, the execution goes back to the old script to finish commands after NEWROOM.
-
-	All parameters are supposed to be 3 characters long.
-	SceneId is the scene to load, x and y are the player's new position and frame is the player's new frame (orientation).
-*/
+/** @file
+ * "NEWROOM " <sceneId> " " <x> " " <y> " " <frame>
+ *
+ * NEWROOM changes the current scene. While doing that, it also executes STARTUP section for the new room.
+ * However, after that, the execution goes back to the old script to finish commands after NEWROOM.
+ *
+ * All parameters are supposed to be 3 characters long.
+ * SceneId is the scene to load, x and y are the player's new position and frame is the player's new frame (orientation).
+ */
 
 namespace MutationOfJB {
 
@@ -62,7 +62,7 @@ Command::ExecuteResult NewRoomCommand::execute(ScriptExecutionContext &scriptExe
 	if (!_innerExecCtx) {
 		Script *newScript = game.changeSceneDelayScript(_sceneId, game.getGameData()._partB);
 		_innerExecCtx = new ScriptExecutionContext(scriptExecCtx.getGame(), newScript);
-		res =_innerExecCtx->startStartupSection();
+		res = _innerExecCtx->startStartupSection();
 	} else {
 		res = _innerExecCtx->runActiveCommand();
 	}
diff --git a/engines/mutationofjb/commands/randomcommand.cpp b/engines/mutationofjb/commands/randomcommand.cpp
index b9cc303..467f788 100644
--- a/engines/mutationofjb/commands/randomcommand.cpp
+++ b/engines/mutationofjb/commands/randomcommand.cpp
@@ -28,16 +28,16 @@
 #include "common/random.h"
 #include "common/translation.h"
 
-/*
-	"RANDOM " <numChoices>
-
-	RANDOM command randomly picks one of the command blocks that
-	follow it and jumps to its start.
-
-	These blocks start with "/" and end with "\". The end of a random
-	block also ends the current section. The number of blocks must
-	match numChoices.
-*/
+/** @file
+ * "RANDOM " <numChoices>
+ *
+ * RANDOM command randomly picks one of the command blocks that
+ * follow it and jumps to its start.
+ *
+ * These blocks start with "/" and end with "\". The end of a random
+ * block also ends the current section. The number of blocks must
+ * match numChoices.
+ */
 
 namespace MutationOfJB {
 
@@ -86,8 +86,7 @@ void RandomBlockStartParser::transition(ScriptParseContext &parseCtx, Command *,
 
 RandomCommand::RandomCommand(uint numChoices)
 	: _numChoices(numChoices),
-	  _chosenNext(nullptr)
-{
+	  _chosenNext(nullptr) {
 	_choices.reserve(numChoices);
 }
 
diff --git a/engines/mutationofjb/commands/removeallitemscommand.cpp b/engines/mutationofjb/commands/removeallitemscommand.cpp
index d9ebe45..38e30de 100644
--- a/engines/mutationofjb/commands/removeallitemscommand.cpp
+++ b/engines/mutationofjb/commands/removeallitemscommand.cpp
@@ -24,11 +24,11 @@
 #include "mutationofjb/script.h"
 #include "mutationofjb/gamedata.h"
 
-/*
-	"DELALLITEMS"
-
-	Removes all items from inventory.
-*/
+/** @file
+ * "DELALLITEMS"
+ *
+ * Removes all items from inventory.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/removeitemcommand.cpp b/engines/mutationofjb/commands/removeitemcommand.cpp
index 48dbda9..21d1123 100644
--- a/engines/mutationofjb/commands/removeitemcommand.cpp
+++ b/engines/mutationofjb/commands/removeitemcommand.cpp
@@ -24,11 +24,11 @@
 #include "mutationofjb/script.h"
 #include "mutationofjb/gamedata.h"
 
-/*
-	"DELITEM" " " <item>
-
-	Removes item from inventory.
-*/
+/** @file
+ * "DELITEM" " " <item>
+ *
+ * Removes item from inventory.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/renamecommand.cpp b/engines/mutationofjb/commands/renamecommand.cpp
index 03a8832..7bd5269 100644
--- a/engines/mutationofjb/commands/renamecommand.cpp
+++ b/engines/mutationofjb/commands/renamecommand.cpp
@@ -25,11 +25,12 @@
 #include "mutationofjb/gamedata.h"
 #include "common/algorithm.h"
 
-/*
-	"REN " <oldName> " " <newName>
-	Renames every door, static (in the current scene) and inventory item
-	with the name oldName to newName.
-*/
+/** @file
+ * "REN " <oldName> " " <newName>
+ *
+ * Renames every door, static (in the current scene) and inventory item
+ * with the name oldName to newName.
+ */
 
 namespace MutationOfJB {
 
diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
index 0474a3e..e63ceb1 100644
--- a/engines/mutationofjb/commands/saycommand.cpp
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -32,27 +32,27 @@
 #include "common/debug.h"
 #include "common/debug-channels.h"
 
-/*
-	("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> ["<" <voiceFile> | "<!"]
-	<skipped> " " <lineToSay> ("<" <voiceFile> | "<!")
-
-	Say command comes in four variants: SM, SLM, NM and NLM.
-	Note: In script files, they are usually written as *SM.
-	The asterisk is ignored by the readLine function.
-
-	Each of them plays a voice file (if present) and/or shows a message
-	(if voice file not present or subtitles are enabled).
-
-	The difference between versions starting with "S" and "N" is that
-	the "N" version does not show talking animation.
-
-	The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
-
-	If the line ends with "<!", it means the message continues to the next line.
-	Next line usually has "SM" (or other variant) repeated, but it does not have to.
-	Then we have the rest of the string to say (which is concatenated with the previous line)
-	and possibly the voice file or "<!" again.
-*/
+/** @file
+ * ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> ["<" <voiceFile> | "<!"]
+ * <skipped> " " <lineToSay> ("<" <voiceFile> | "<!")
+ *
+ * Say command comes in four variants: SM, SLM, NM and NLM.
+ * Note: In script files, they are usually written as *SM.
+ * The asterisk is ignored by the readLine function.
+ *
+ * Each of them plays a voice file (if present) and/or shows a message
+ * (if voice file not present or subtitles are enabled).
+ *
+ * The difference between versions starting with "S" and "N" is that
+ * the "N" version does not show talking animation.
+ *
+ * The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
+ *
+ * If the line ends with "<!", it means the message continues to the next line.
+ * Next line usually has "SM" (or other variant) repeated, but it does not have to.
+ * Then we have the rest of the string to say (which is concatenated with the previous line)
+ * and possibly the voice file or "<!" again.
+ */
 
 namespace MutationOfJB {
 
@@ -113,7 +113,7 @@ bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &par
 		Common::String talkStr(currentLine.c_str() + startPos, endPos - startPos);
 
 		if (endPos != currentLine.size()) {
-			const char * end = currentLine.c_str() + endPos + 1;
+			const char *end = currentLine.c_str() + endPos + 1;
 			if (end[0] == '!') {
 				cont = true;
 			} else {
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index 02164dc..4836d03 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -34,8 +34,7 @@ void SeqCommandParser::transition(ScriptParseContext &, Command *oldCommand, Com
 	static_cast<SeqCommand *>(oldCommand)->setNextCommand(newCommand);
 }
 
-void SeqCommand::setNextCommand(Command *nextCommand)
-{
+void SeqCommand::setNextCommand(Command *nextCommand) {
 	_nextCommand = nextCommand;
 }
 
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 04731c2..6c969e9 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -21,7 +21,7 @@
  */
 
 #ifndef MUTATIONOFJB_SEQCOMMAND_H
-#define MUTATIONOFJB_SEQCOMMAND_H 
+#define MUTATIONOFJB_SEQCOMMAND_H
 
 #include "mutationofjb/commands/command.h"
 #include "common/scummsys.h"
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index 6a3b204..5c7eb8e 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -70,7 +70,7 @@ Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx
 }
 
 Common::String TalkCommand::debugString() const {
-	const char * modes[] = {"NORMAL", "RAY_AND_BUTTLEG", "CARNIVAL_TICKET_SELLER"};
+	const char *modes[] = {"NORMAL", "RAY_AND_BUTTLEG", "CARNIVAL_TICKET_SELLER"};
 	return Common::String::format("TALK %s", modes[(int) _mode]);
 }
 
diff --git a/engines/mutationofjb/conversationlinelist.h b/engines/mutationofjb/conversationlinelist.h
index 0f8346f..4f208ad 100644
--- a/engines/mutationofjb/conversationlinelist.h
+++ b/engines/mutationofjb/conversationlinelist.h
@@ -34,9 +34,15 @@ public:
 		Common::String _text;
 		Common::String _voiceFile;
 
-		bool isRepeating() const { return _text.firstChar() == '*'; }
-		bool isFirstSpeaker() const { return _text.firstChar() == '~'; }
-		bool isSecondSpeaker() const { return _text.firstChar() == '`'; }
+		bool isRepeating() const {
+			return _text.firstChar() == '*';
+		}
+		bool isFirstSpeaker() const {
+			return _text.firstChar() == '~';
+		}
+		bool isSecondSpeaker() const {
+			return _text.firstChar() == '`';
+		}
 	};
 
 	typedef Common::Array<Speech> Speeches;
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index b4f00c9..f7b074a 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -42,7 +42,7 @@ static Common::String convertToASCII(const Common::String &str) {
 	static const char conversionTable[] = {
 		'C', 'u', 'e', 'd', 'a', 'D', 'T', 'c', 'e', 'E', 'L', 'I', 'l', 'l', 'A', 'A', /* 0x80-0x8F */
 		'E', 'z', 'Z', 'o', 'o', 'O', 'u', 'U', 'y', 'O', 'U', 'S', 'L', 'Y', 'R', 't', /* 0x90-0x9F */
-		'a', 'i', 'o', 'u', 'n', 'N', 'U', 'O', 's', 'r', 'r', 'R'						/* 0xA0-0xAB */
+		'a', 'i', 'o', 'u', 'n', 'N', 'U', 'O', 's', 'r', 'r', 'R'                      /* 0xA0-0xAB */
 	};
 
 	Common::String ret = str;
@@ -151,9 +151,9 @@ void Console::showCommands(Command *command, int indentLevel) {
 			debugPrintf("ELSE\n");
 			showCommands(condCmd->getFalseCommand(), indentLevel + 1);
 			command = nullptr;
-		} else if (CallMacroCommand* const callMacroCmd = dynamic_cast<CallMacroCommand *>(command)) {
+		} else if (CallMacroCommand *const callMacroCmd = dynamic_cast<CallMacroCommand *>(command)) {
 			command = callMacroCmd->getReturnCommand();
-		} else if (RandomCommand* const randomCmd = dynamic_cast<RandomCommand *>(command)) {
+		} else if (RandomCommand *const randomCmd = dynamic_cast<RandomCommand *>(command)) {
 			const RandomCommand::Choices &choices = randomCmd->getChoices();
 			for (RandomCommand::Choices::size_type i = 0; i < choices.size(); ++i) {
 				showIndent(indentLevel + 1);
@@ -450,7 +450,7 @@ Script *Console::getScriptFromArg(const char *arg) {
 }
 
 bool Console::cmd_listinventory(int, const char **) {
-	Inventory &inventory =_vm->getGame().getGameData().getInventory();
+	Inventory &inventory = _vm->getGame().getGameData().getInventory();
 	const Inventory::Items &items = inventory.getItems();
 	for (Inventory::Items::const_iterator it = items.begin(); it != items.end(); ++it) {
 		debugPrintf("%s\n", convertToASCII(*it).c_str());
diff --git a/engines/mutationofjb/encryptedfile.cpp b/engines/mutationofjb/encryptedfile.cpp
index 0796811..c02bcc7 100644
--- a/engines/mutationofjb/encryptedfile.cpp
+++ b/engines/mutationofjb/encryptedfile.cpp
@@ -23,34 +23,34 @@
 #include "encryptedfile.h"
 
 static const uint8 XOR_TABLE[] = {
-  0x41, 0x2b, 0x7a, 0x0c, 0xc8, 0xe5, 0x0c, 0xde, 0x45, 0xa8, 0x00, 0xad,
-  0x70, 0xac, 0x23, 0xe0, 0x0c, 0xde, 0xac, 0x16, 0xa1, 0x1a, 0x70, 0x17,
-  0x1b, 0x90, 0x34, 0x6e, 0x53, 0xfe, 0xdd, 0x3c, 0xef, 0x74, 0x75, 0x3e,
-  0x2e, 0xb0, 0x3d, 0x2b, 0x74, 0x6d, 0x59, 0xde, 0xc6, 0x20, 0xb8, 0xf3,
-  0x7d, 0xfa, 0x4d, 0xbd, 0xdb, 0x3c, 0xc5, 0xcb, 0x57, 0x13, 0x40, 0x6b,
-  0xb8, 0xad, 0xb9, 0xc1, 0x6a, 0x37, 0x39, 0x80, 0x94, 0xd3, 0xdf, 0x4b,
-  0xe4, 0xe4, 0x7a, 0x4c, 0x0f, 0x21, 0x27, 0x9a, 0x7e, 0x52, 0x35, 0x58,
-  0xb4, 0xbc, 0x5a, 0xc9, 0x48, 0x7f, 0xcc, 0xb6, 0x97, 0x7b, 0xf1, 0xd5,
-  0x88, 0x8c, 0xa9, 0x27, 0xf7, 0x20, 0x68, 0x65, 0xad, 0x6f, 0x9e, 0x07,
-  0xf8, 0xf6, 0x2c, 0x65, 0x4a, 0xe9, 0xd8, 0x13, 0x6a, 0xb5, 0x14, 0x6f,
-  0x29, 0xdc, 0x68, 0xf8, 0xa0, 0xb6, 0x73, 0x03, 0x69, 0xe3, 0x4f, 0xb6,
-  0x59, 0x79, 0xae, 0x93, 0xad, 0x40, 0x27, 0xd0, 0xb5, 0x7a, 0x68, 0x5d,
-  0x5e, 0x19, 0x53, 0x4f, 0x40, 0x56, 0x3d, 0x10, 0xf8, 0x0a, 0xc6, 0x90,
-  0x06, 0x45, 0x13, 0x4a, 0x6a, 0xfe, 0x56, 0xeb, 0xbc, 0xd9, 0xee, 0xe0,
-  0x85, 0x5e, 0x98, 0x23, 0xf9, 0x19, 0x60, 0xf9, 0x7e, 0x8d, 0x61, 0xa0,
-  0x7c, 0xe1, 0x84, 0xf2, 0x7a, 0xb8, 0xbe, 0x8e, 0x81, 0x9e, 0x87, 0x20,
-  0x32, 0xf3, 0x8c, 0xb4, 0x2c, 0x4d, 0xc8, 0x50, 0x9b, 0xa5, 0x9c, 0x27,
-  0x02, 0xd6, 0x7f, 0x2a, 0xaf, 0x46, 0x65, 0xd0, 0x6a, 0xae, 0xfa, 0x53,
-  0x37, 0x6c, 0x49, 0xb9, 0x4d, 0xcd, 0x6c, 0x6b, 0xa7, 0x2d, 0x66, 0x32,
-  0xb4, 0xf5, 0x41, 0xd5, 0x18, 0xc4, 0xfd, 0xbe, 0x8a, 0x47, 0x11, 0x50,
-  0x3d, 0x97, 0x64, 0xda, 0x5a, 0x27, 0x18, 0x60, 0x78, 0x80, 0x86, 0x8a,
-  0x2a, 0x72, 0x40, 0x89
+	0x41, 0x2b, 0x7a, 0x0c, 0xc8, 0xe5, 0x0c, 0xde, 0x45, 0xa8, 0x00, 0xad,
+	0x70, 0xac, 0x23, 0xe0, 0x0c, 0xde, 0xac, 0x16, 0xa1, 0x1a, 0x70, 0x17,
+	0x1b, 0x90, 0x34, 0x6e, 0x53, 0xfe, 0xdd, 0x3c, 0xef, 0x74, 0x75, 0x3e,
+	0x2e, 0xb0, 0x3d, 0x2b, 0x74, 0x6d, 0x59, 0xde, 0xc6, 0x20, 0xb8, 0xf3,
+	0x7d, 0xfa, 0x4d, 0xbd, 0xdb, 0x3c, 0xc5, 0xcb, 0x57, 0x13, 0x40, 0x6b,
+	0xb8, 0xad, 0xb9, 0xc1, 0x6a, 0x37, 0x39, 0x80, 0x94, 0xd3, 0xdf, 0x4b,
+	0xe4, 0xe4, 0x7a, 0x4c, 0x0f, 0x21, 0x27, 0x9a, 0x7e, 0x52, 0x35, 0x58,
+	0xb4, 0xbc, 0x5a, 0xc9, 0x48, 0x7f, 0xcc, 0xb6, 0x97, 0x7b, 0xf1, 0xd5,
+	0x88, 0x8c, 0xa9, 0x27, 0xf7, 0x20, 0x68, 0x65, 0xad, 0x6f, 0x9e, 0x07,
+	0xf8, 0xf6, 0x2c, 0x65, 0x4a, 0xe9, 0xd8, 0x13, 0x6a, 0xb5, 0x14, 0x6f,
+	0x29, 0xdc, 0x68, 0xf8, 0xa0, 0xb6, 0x73, 0x03, 0x69, 0xe3, 0x4f, 0xb6,
+	0x59, 0x79, 0xae, 0x93, 0xad, 0x40, 0x27, 0xd0, 0xb5, 0x7a, 0x68, 0x5d,
+	0x5e, 0x19, 0x53, 0x4f, 0x40, 0x56, 0x3d, 0x10, 0xf8, 0x0a, 0xc6, 0x90,
+	0x06, 0x45, 0x13, 0x4a, 0x6a, 0xfe, 0x56, 0xeb, 0xbc, 0xd9, 0xee, 0xe0,
+	0x85, 0x5e, 0x98, 0x23, 0xf9, 0x19, 0x60, 0xf9, 0x7e, 0x8d, 0x61, 0xa0,
+	0x7c, 0xe1, 0x84, 0xf2, 0x7a, 0xb8, 0xbe, 0x8e, 0x81, 0x9e, 0x87, 0x20,
+	0x32, 0xf3, 0x8c, 0xb4, 0x2c, 0x4d, 0xc8, 0x50, 0x9b, 0xa5, 0x9c, 0x27,
+	0x02, 0xd6, 0x7f, 0x2a, 0xaf, 0x46, 0x65, 0xd0, 0x6a, 0xae, 0xfa, 0x53,
+	0x37, 0x6c, 0x49, 0xb9, 0x4d, 0xcd, 0x6c, 0x6b, 0xa7, 0x2d, 0x66, 0x32,
+	0xb4, 0xf5, 0x41, 0xd5, 0x18, 0xc4, 0xfd, 0xbe, 0x8a, 0x47, 0x11, 0x50,
+	0x3d, 0x97, 0x64, 0xda, 0x5a, 0x27, 0x18, 0x60, 0x78, 0x80, 0x86, 0x8a,
+	0x2a, 0x72, 0x40, 0x89
 };
 
 namespace MutationOfJB {
 
 uint32 EncryptedFile::read(void *dataPtr, uint32 dataSize) {
-	uint8 xorPos = pos() % 256;
+	uint8 xorPos = static_cast<uint8>(pos() % ARRAYSIZE(XOR_TABLE));
 	const uint32 readBytes = Common::File::read(dataPtr, dataSize);
 
 	for (uint32 i = 0; i < readBytes; ++i) {
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 7b500d3..2843ad9 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -39,13 +39,13 @@ namespace MutationOfJB {
 
 Game::Game(MutationOfJBEngine *vm)
 	: _vm(vm),
-	_randomSource("mutationofjb"),
-	_delayedLocalScript(nullptr),
-	_gui(*this, _vm->getScreen()),
-	_currentAction(ActionInfo::Walk),
-	_scriptExecCtx(*this),
-	_taskManager(*this),
-	_assets(*this) {
+	  _randomSource("mutationofjb"),
+	  _delayedLocalScript(nullptr),
+	  _gui(*this, _vm->getScreen()),
+	  _currentAction(ActionInfo::Walk),
+	  _scriptExecCtx(*this),
+	  _taskManager(*this),
+	  _assets(*this) {
 
 	_gameData = new GameData;
 	loadGameData(false);
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index 54335d1..f25eff6 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -245,11 +245,10 @@ bool Scene::isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceList
 
 GameData::GameData()
 	: _currentScene(0),
-	_lastScene(0),
-	_partB(false),
-	_inventory(),
-	_color(WHITE)
-	{}
+	  _lastScene(0),
+	  _partB(false),
+	  _inventory(),
+	  _color(WHITE) {}
 
 Scene *GameData::getScene(uint8 sceneId) {
 	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index fc755f7..8783555 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -235,9 +235,15 @@ struct ExhaustedChoice {
 	 */
 	uint8 _encodedData;
 
-	uint8 getContext() const { return (_encodedData >> 7) & 0x1; }
-	uint8 getChoiceIndex() const { return (_encodedData >> 4) & 0x7; }
-	uint8 getChoiceListIndex() const { return _encodedData & 0xF; }
+	uint8 getContext() const {
+		return (_encodedData >> 7) & 0x1;
+	}
+	uint8 getChoiceIndex() const {
+		return (_encodedData >> 4) & 0x7;
+	}
+	uint8 getChoiceListIndex() const {
+		return _encodedData & 0xF;
+	}
 
 	ExhaustedChoice() : _encodedData(0) {}
 	ExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) :
diff --git a/engines/mutationofjb/gui.cpp b/engines/mutationofjb/gui.cpp
index 41ef94a..6266ff0 100644
--- a/engines/mutationofjb/gui.cpp
+++ b/engines/mutationofjb/gui.cpp
@@ -64,7 +64,7 @@ enum {
 
 Gui::Gui(Game &game, Graphics::Screen *screen)
 	: _game(game),
-	_screen(screen) {}
+	  _screen(screen) {}
 
 Gui::~Gui() {
 	for (Common::Array<Widget *>::iterator it = _widgets.begin(); it != _widgets.end(); ++it) {
@@ -122,7 +122,7 @@ bool Gui::init() {
 	}
 
 	const Common::Rect conversationRect(CONVERSATION_X, CONVERSATION_Y, CONVERSATION_X + CONVERSATION_WIDTH, CONVERSATION_Y + CONVERSATION_HEIGHT);
-	const Graphics::Surface conversationSurface =_hudSurfaces[2].getSubArea(conversationRect);
+	const Graphics::Surface conversationSurface = _hudSurfaces[2].getSubArea(conversationRect);
 	_conversationWidget = new ConversationWidget(*this, conversationRect, conversationSurface);
 	_conversationWidget->setVisible(false);
 	_widgets.push_back(_conversationWidget);
@@ -154,7 +154,7 @@ void Gui::update() {
 	}
 }
 
-ConversationWidget& Gui::getConversationWidget() {
+ConversationWidget &Gui::getConversationWidget() {
 	return *_conversationWidget;
 }
 
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
index 8919a9d..f644517 100644
--- a/engines/mutationofjb/gui.h
+++ b/engines/mutationofjb/gui.h
@@ -65,7 +65,7 @@ public:
 	virtual void onInventoryChanged() override;
 	virtual void onButtonClicked(ButtonWidget *) override;
 
-	ConversationWidget& getConversationWidget();
+	ConversationWidget &getConversationWidget();
 
 private:
 	bool loadInventoryGfx();
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index cdaa037..2320255 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -40,11 +40,11 @@
 namespace MutationOfJB {
 
 MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
-: Engine(syst),
- _console(nullptr),
- _screen(nullptr),
- _mapObjectId(0),
- _cursorState(CURSOR_IDLE) {
+	: Engine(syst),
+	  _console(nullptr),
+	  _screen(nullptr),
+	  _mapObjectId(0),
+	  _cursorState(CURSOR_IDLE) {
 	debug("MutationOfJBEngine::MutationOfJBEngine");
 }
 
@@ -174,7 +174,7 @@ void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 		const int16 y = event.mouse.y;
 
 		int index = 0;
-		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index))	{
+		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index)) {
 			Static *const stat = scene->getStatic(index);
 			if (stat && stat->_active == 1) {
 				_game->startActionSection(ActionInfo::Walk, stat->_name);
@@ -188,7 +188,7 @@ void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 
 		int index = 0;
 		bool found = false;
-		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index))	{
+		if (Bitmap *const bitmap = scene->findBitmap(x, y, &index)) {
 			Static *const stat = scene->getStatic(index);
 			if (stat && stat->_active == 1) {
 				Object *const object = scene->getObject(index);
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 3e93c47..fb148c1 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -103,7 +103,7 @@ bool ScriptParseContext::readLine(Common::String &line) {
 			}
 			return true;
 		}
-	} while(!_stream.eos());
+	} while (!_stream.eos());
 
 	return false;
 }
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 28e0e98..17e1810 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -21,7 +21,7 @@
  */
 
 #ifndef MUTATIONOFJB_SCRIPT_H
-#define MUTATIONOFJB_SCRIPT_H 
+#define MUTATIONOFJB_SCRIPT_H
 
 #include "mutationofjb/commands/command.h"
 #include "common/array.h"
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index d4fab56..8c07b32 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -61,7 +61,7 @@ void ConversationTask::update() {
 				finish();
 				break;
 			case SAYING_CHOICE: {
-				const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList();
+				const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
 				const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
 
 				_substate = SAYING_RESPONSE;
@@ -72,8 +72,7 @@ void ConversationTask::update() {
 			case SAYING_RESPONSE: {
 				startExtra();
 
-				if (_substate != RUNNING_EXTRA)
-				{
+				if (_substate != RUNNING_EXTRA) {
 					gotoNextLine();
 				}
 				break;
@@ -99,7 +98,7 @@ void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint
 	const ConversationInfo::Item &item = getCurrentLine()->_items[data];
 	convWidget->clearChoices();
 
-	const ConversationLineList& toSayList = getTaskManager()->getGame().getAssets().getToSayList();
+	const ConversationLineList &toSayList = getTaskManager()->getGame().getAssets().getToSayList();
 	const ConversationLineList::Line *line = toSayList.getLine(item._choice);
 
 	_substate = SAYING_CHOICE;
@@ -157,7 +156,7 @@ void ConversationTask::showChoicesOrPick() {
 
 	if (itemsWithValidChoices.size() > 1) {
 		ConversationWidget &widget = game.getGui().getConversationWidget();
-		const ConversationLineList& toSayList = game.getAssets().getToSayList();
+		const ConversationLineList &toSayList = game.getAssets().getToSayList();
 
 		for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidChoices.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
 			const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices[i]];
@@ -170,7 +169,7 @@ void ConversationTask::showChoicesOrPick() {
 
 		_haveChoices = true;
 	} else if (itemsWithValidChoices.size() == 1 && _haveChoices) {
-		const ConversationLineList& toSayList = game.getAssets().getToSayList();
+		const ConversationLineList &toSayList = game.getAssets().getToSayList();
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()];
 		const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
 
@@ -185,7 +184,7 @@ void ConversationTask::showChoicesOrPick() {
 
 		_haveChoices = true;
 	} else if (!itemsWithValidResponses.empty() && _haveChoices) {
-		const ConversationLineList& responseList = game.getAssets().getResponseList();
+		const ConversationLineList &responseList = game.getAssets().getResponseList();
 		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()];
 		const ConversationLineList::Line *const line = responseList.getLine(item._response);
 
@@ -224,7 +223,7 @@ void ConversationTask::finish() {
 }
 
 void ConversationTask::startExtra() {
-	const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList();
+	const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
 	const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
 	if (!line->_extra.empty()) {
 		_innerExecCtx = new ScriptExecutionContext(getTaskManager()->getGame());
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
index e4bbdc3..bdfa875 100644
--- a/engines/mutationofjb/tasks/conversationtask.h
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -33,7 +33,7 @@ class ScriptExecutionContext;
 
 class ConversationTask : public Task, public ConversationWidgetCallback {
 public:
-	ConversationTask(uint8 sceneId, const ConversationInfo& convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentLineIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
+	ConversationTask(uint8 sceneId, const ConversationInfo &convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentLineIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
 	virtual ~ConversationTask() {}
 
 	virtual void start() override;
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
index 8c9c0e8..b14fc97 100644
--- a/engines/mutationofjb/tasks/task.h
+++ b/engines/mutationofjb/tasks/task.h
@@ -44,15 +44,26 @@ public:
 
 	virtual void start() = 0;
 	virtual void update() = 0;
-	virtual void stop() { assert(false); } // Assert by default - stopping might not be safe for all tasks.
+	virtual void stop() {
+		assert(false);    // Assert by default - stopping might not be safe for all tasks.
+	}
 
-	void setTaskManager(TaskManager *taskMan) { _taskManager = taskMan; }
-	TaskManager *getTaskManager() { return _taskManager; }
+	void setTaskManager(TaskManager *taskMan) {
+		_taskManager = taskMan;
+	}
 
-	State getState() const { return _state; }
+	TaskManager *getTaskManager() {
+		return _taskManager;
+	}
+
+	State getState() const {
+		return _state;
+	}
 
 protected:
-	void setState(State state) { _state = state; }
+	void setState(State state) {
+		_state = state;
+	}
 
 private:
 	TaskManager *_taskManager;
diff --git a/engines/mutationofjb/tasks/taskmanager.h b/engines/mutationofjb/tasks/taskmanager.h
index 29e854a..2f351c0 100644
--- a/engines/mutationofjb/tasks/taskmanager.h
+++ b/engines/mutationofjb/tasks/taskmanager.h
@@ -57,11 +57,13 @@ public:
 	 * However, if only a raw pointer is available (e.g. this),
 	 * the method can be used to obtain a SharedPtr.
 	 */
-	TaskPtr getTask(Task* task);
+	TaskPtr getTask(Task *task);
 
 	void update();
 
-	Game &getGame() { return _game; }
+	Game &getGame() {
+		return _game;
+	}
 
 private:
 	TaskPtrs _tasks;
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 72d2d0f..2e3b0b3 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -37,9 +37,9 @@ void reportFileMissingError(const char *fileName) {
 
 Common::String toUpperCP895(const Common::String &str) {
 	static const byte conversionTable[] = {
-		0x00, 0x9A, 0x90, 0x85, 0x8E, 0x00, 0x00, 0x80, 0x89, 0x00, 0x00, 0x00, 0x9C, 0x8A, 0x00, 0x00,	/* 0x80-0x8F */
-		0x00, 0x92, 0x00, 0xA7, 0x99, 0x00, 0xA6, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,	/* 0x90-0x9F */
-		0x8F, 0x8B, 0x95, 0x97, 0xA5, 0x00, 0x00, 0x00, 0x9B, 0x9E, 0xAB, 0x00							/* 0xA0-0xAB */
+		0x00, 0x9A, 0x90, 0x85, 0x8E, 0x00, 0x00, 0x80, 0x89, 0x00, 0x00, 0x00, 0x9C, 0x8A, 0x00, 0x00, /* 0x80-0x8F */
+		0x00, 0x92, 0x00, 0xA7, 0x99, 0x00, 0xA6, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, /* 0x90-0x9F */
+		0x8F, 0x8B, 0x95, 0x97, 0xA5, 0x00, 0x00, 0x00, 0x9B, 0x9E, 0xAB, 0x00                          /* 0xA0-0xAB */
 	};
 
 	Common::String ret = str;
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index ac1239d..fe72794 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -39,7 +39,7 @@ void reportFileMissingError(const char *fileName);
 Common::String toUpperCP895(const Common::String &str);
 
 // Taken from ManagedSurface::clip.
-template <typename SurfaceType>
+template<typename SurfaceType>
 bool clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &destSurf) {
 	if (destBounds.left >= destSurf.w || destBounds.top >= destSurf.h ||
 			destBounds.right <= 0 || destBounds.bottom <= 0)
@@ -69,7 +69,7 @@ bool clipBounds(Common::Rect &srcBounds, Common::Rect &destBounds, SurfaceType &
 	return true;
 }
 
-template <typename BlitOp>
+template<typename BlitOp>
 void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::Surface &dest, const Common::Point &destPos, BlitOp blitOp) {
 	Common::Rect srcBounds = srcRect;
 	Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
@@ -96,7 +96,7 @@ void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics
 	}
 }
 
-template <typename BlitOp>
+template<typename BlitOp>
 void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
 	Common::Rect srcBounds = srcRect;
 	Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), destPos.y + srcRect.height());
@@ -111,12 +111,12 @@ void blit_if(const Graphics::Surface &src, const Common::Rect &srcRect, Graphics
 	blit_if(src, srcRect, destSurf, Common::Point(0, 0), blitOp);
 }
 
-template <typename BlitOp>
+template<typename BlitOp>
 void blit_if(const Graphics::Surface &src, Graphics::Surface &dest, const Common::Point &destPos, BlitOp blitOp) {
 	blit_if(src, Common::Rect(0, 0, src.w, src.h), dest, destPos, blitOp);
 }
 
-template <typename BlitOp>
+template<typename BlitOp>
 void blit_if(const Graphics::Surface &src, Graphics::ManagedSurface &dest, const Common::Point &destPos, BlitOp blitOp) {
 	blit_if(src, Common::Rect(0, 0, src.w, src.h), dest, destPos, blitOp);
 }
diff --git a/engines/mutationofjb/widgets/buttonwidget.cpp b/engines/mutationofjb/widgets/buttonwidget.cpp
index 9f9a401..911cb7d 100644
--- a/engines/mutationofjb/widgets/buttonwidget.cpp
+++ b/engines/mutationofjb/widgets/buttonwidget.cpp
@@ -38,9 +38,8 @@ void ButtonWidget::setCallback(ButtonWidgetCallback *callback) {
 }
 
 void ButtonWidget::handleEvent(const Common::Event &event) {
-	switch(event.type) {
-	case Common::EVENT_LBUTTONDOWN:
-	{
+	switch (event.type) {
+	case Common::EVENT_LBUTTONDOWN: {
 		const int16 x = event.mouse.x;
 		const int16 y = event.mouse.y;
 		if (_area.contains(x, y)) {
@@ -49,8 +48,7 @@ void ButtonWidget::handleEvent(const Common::Event &event) {
 		}
 		break;
 	}
-	case Common::EVENT_LBUTTONUP:
-	{
+	case Common::EVENT_LBUTTONUP: {
 		if (_pressed) {
 			_pressed = false;
 			markDirty();
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index 24bb5e1..e0bbf66 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -74,9 +74,8 @@ void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
 }
 
 void ConversationWidget::handleEvent(const Common::Event &event) {
-	switch(event.type) {
-	case Common::EVENT_LBUTTONDOWN:
-	{
+	switch (event.type) {
+	case Common::EVENT_LBUTTONDOWN: {
 		const int16 x = event.mouse.x;
 		const int16 y = event.mouse.y;
 		if (_area.contains(x, y)) {
diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h
index 51ea86e..abde702 100644
--- a/engines/mutationofjb/widgets/conversationwidget.h
+++ b/engines/mutationofjb/widgets/conversationwidget.h
@@ -41,7 +41,9 @@ public:
 	enum { CONVERSATION_MAX_CHOICES = 4 };
 
 	ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface);
-	void setCallback(ConversationWidgetCallback *callback) { _callback = callback; }
+	void setCallback(ConversationWidgetCallback *callback) {
+		_callback = callback;
+	}
 
 	void setChoice(int choiceNo, const Common::String &str, uint32 data = 0);
 	void clearChoices();
diff --git a/engines/mutationofjb/widgets/inventorywidget.cpp b/engines/mutationofjb/widgets/inventorywidget.cpp
index d78ef10..96f35e4 100644
--- a/engines/mutationofjb/widgets/inventorywidget.cpp
+++ b/engines/mutationofjb/widgets/inventorywidget.cpp
@@ -41,7 +41,7 @@ enum {
 	INVENTORY_ITEMS_LINES = 5
 };
 
-InventoryWidget::InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface>& inventorySurfaces) :
+InventoryWidget::InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface> &inventorySurfaces) :
 	Widget(gui, Common::Rect(INVENTORY_START_X, INVENTORY_START_Y, INVENTORY_START_X + Inventory::VISIBLE_ITEMS * INVENTORY_ITEM_WIDTH, INVENTORY_START_Y + INVENTORY_ITEM_HEIGHT)),
 	_inventoryMap(inventoryMap),
 	_surfaces(inventorySurfaces) {}
diff --git a/engines/mutationofjb/widgets/inventorywidget.h b/engines/mutationofjb/widgets/inventorywidget.h
index f0bc4ba..8d6205e 100644
--- a/engines/mutationofjb/widgets/inventorywidget.h
+++ b/engines/mutationofjb/widgets/inventorywidget.h
@@ -34,13 +34,13 @@ namespace MutationOfJB {
 
 class InventoryWidget : public Widget {
 public:
-	InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface>& inventorySurfaces);
+	InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface> &inventorySurfaces);
 	virtual void _draw(Graphics::ManagedSurface &) override;
 
 private:
 	void drawInventoryItem(Graphics::ManagedSurface &surface, const Common::String &item, int pos);
 	Gui::InventoryMap &_inventoryMap;
-	const Common::Array<Graphics::Surface>& _surfaces;
+	const Common::Array<Graphics::Surface> &_surfaces;
 };
 
 }


Commit: 4633b8398642f3005827780313c25479689a72dc
    https://github.com/scummvm/scummvm/commit/4633b8398642f3005827780313c25479689a72dc
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Improve documentation and naming.

Changed paths:
    engines/mutationofjb/commands/camefromcommand.cpp
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/command.h
    engines/mutationofjb/commands/definestructcommand.cpp
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/removeitemcommand.cpp
    engines/mutationofjb/commands/saycommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/gamedata.h
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/conversationtask.h
    engines/mutationofjb/tasks/objectanimationtask.cpp
    engines/mutationofjb/tasks/sequentialtask.h
    engines/mutationofjb/tasks/task.h
    engines/mutationofjb/tasks/taskmanager.h


diff --git a/engines/mutationofjb/commands/camefromcommand.cpp b/engines/mutationofjb/commands/camefromcommand.cpp
index 485e0c5..67033b8 100644
--- a/engines/mutationofjb/commands/camefromcommand.cpp
+++ b/engines/mutationofjb/commands/camefromcommand.cpp
@@ -26,7 +26,7 @@
 #include "common/str.h"
 
 /** @file
- * "CAMEFROM" <sceneId>
+ * "CAMEFROM " <sceneId>
  *
  * This command tests whether last scene (the scene player came from) is sceneId.
  * If true, the execution continues after this command.
diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index 5d6833b..7d2e7e6 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -25,8 +25,6 @@
 #include "mutationofjb/gamedata.h"
 #include "common/translation.h"
 
-namespace MutationOfJB {
-
 /** @file
  * "CHANGE" <entity> " " <register> " " <sceneId> " " <entityId> " " <value>
  *
@@ -43,6 +41,8 @@ namespace MutationOfJB {
  * <value>    *B  Value (variable length).
  */
 
+namespace MutationOfJB {
+
 bool ChangeCommandParser::parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
 	if (changeEntity) {
 		if (valueString.size() < 8) {
diff --git a/engines/mutationofjb/commands/command.h b/engines/mutationofjb/commands/command.h
index 0133d52..b407aa1 100644
--- a/engines/mutationofjb/commands/command.h
+++ b/engines/mutationofjb/commands/command.h
@@ -33,18 +33,56 @@ class Command;
 class ScriptExecutionContext;
 class ScriptParseContext;
 
+/**
+ * Base class for command parsers.
+ *
+ * The parser's main job is to create a Command instance from input line.
+ */
 class CommandParser {
 public:
 	virtual ~CommandParser();
+
+	/**
+	 * Parses the specified line and possibly returns a Command instance.
+	 *
+	 * @param line Line to parse.
+	 * @param parseCtx Parse context.
+	 * @param command Output parameter for newly created command.
+	 * @return True if the line has been successfully parsed by this parser, false otherwise.
+	 * @note You may return true and set command to nullptr.
+	 * That means the line has been successfully parsed, but no command is needed.
+	 */
 	virtual bool parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) = 0;
 
-	/* Old command - created by this parser. */
+	/**
+	 * Called when transitioning parsing between two commands.
+	 *
+	 * For example, cmdParserA->transition(parseCtx, cmdA, cmdB, cmdParserB) is called after command B is done parsing
+	 * to notify command A parser about the transition from command A to command B.
+	 * This is useful for sequential commands, because at the time command A is being parsed,
+	 * we don't have any information about command B, so we cannot set the next pointer.
+	 * Transition method can be used to set the next pointer after command B is available.
+	 *
+	 * @param parseCtx Parse context.
+	 * @param oldCommand Old command (created by this parser).
+	 * @param newCommand New command (created by newCommandParser).
+	 * @param newCommandParser Command parser which created the new command.
+	 */
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser);
 
-	/* Called after parsing. */
+	/**
+	 * Called after the whole script is parsed.
+	 *
+	 * Can be used for cleanup.
+	 *
+	 * @param parseCtx Parse context.
+	 */
 	virtual void finish(ScriptParseContext &parseCtx);
 };
 
+/**
+ * Base class for script commands.
+ */
 class Command {
 public:
 	enum ExecuteResult {
@@ -60,6 +98,7 @@ public:
 
 	virtual Common::String debugString() const = 0;
 };
+
 }
 
 #endif
diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp
index 4ccf2e8..bea3497 100644
--- a/engines/mutationofjb/commands/definestructcommand.cpp
+++ b/engines/mutationofjb/commands/definestructcommand.cpp
@@ -25,6 +25,22 @@
 #include "mutationofjb/game.h"
 #include "common/debug.h"
 
+/** @file
+ * "DEFINE_STRUCT " <numItemGroups> " " <context> " " <objectId> " " <colorString> <CRLF>
+ * <itemGroup> { <CRLF> <itemGroup> }
+ *
+ * item ::= <questionIndex> " " <responseIndex> " " <nextGroup>
+ * itemGroup ::= <item> " " <item> " " <item> " " <item> " " <item>
+ *
+ * Defines the flow of an interactive conversation.
+ *
+ * Every item group consists of 5 conversation items.
+ * "questionIndex" and "responseIndex" specify what the player and the responder say when the conversation item is selected.
+ * They refer to the line numbers of TOSAY.GER and RESPONSE.GER, respectively.
+ * "nextGroup" refers to the group that follows when the conversation item is selected. A value of 0 indicates end of
+ * conversation.
+ */
+
 namespace MutationOfJB {
 
 bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
@@ -52,19 +68,19 @@ bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseCon
 
 		const char *linePtr = convLineStr.c_str();
 
-		ConversationInfo::Line convLine;
+		ConversationInfo::ItemGroup convGroup;
 
 		for (int j = 0; j < 5; ++j) {
 			ConversationInfo::Item convItem;
-			convItem._choice = atoi(linePtr);
+			convItem._question = atoi(linePtr);
 			linePtr += 6;
 			convItem._response = atoi(linePtr);
 			linePtr += 6;
-			convItem._nextLineIndex = atoi(linePtr);
+			convItem._nextGroupIndex = atoi(linePtr);
 			linePtr += 3;
-			convLine._items.push_back(convItem);
+			convGroup.push_back(convItem);
 		}
-		convInfo._lines.push_back(convLine);
+		convInfo._itemGroups.push_back(convGroup);
 	}
 
 	command = new DefineStructCommand(convInfo);
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index c7fbd41..8ad11f8 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -28,16 +28,19 @@
 #include "common/translation.h"
 
 /** @file
- * ("#L " | "-L ") <object>
- * ("#W " | "-W ") <object>
- * ("#T " | "-T ") <object>
- * ("#P " | "-P ") <object1>
- * ("#U " | "-U ") <object1> [<object2>]
- * ("#ELSE" | "-ELSE") [<tag>]
- * "#MACRO " <name>
- * "#EXTRA" <name>
+ * <look> | <walk> | <talk> | <pickup> | <use> | <else> | <macro> | <extra> | <endRandom>
  *
- * If a line starts with '#', '=', '-', it is treated as the end of a section.
+ * look ::= ("#L " | "-L ") <object>
+ * walk ::= ("#W " | "-W ") <object>
+ * talk ::= ("#T " | "-T ") <object>
+ * pickup ::= ("#P " | "-P ") <object1>
+ * use ::= ("#U " | "-U ") <object1> [<object2>]
+ * else ::= ("#ELSE" | "-ELSE") [<tag>]
+ * macro ::= "#MACRO " <name>
+ * extra ::= "#EXTRA" <name>
+ * endRandom ::= "\"
+ *
+ * If a line starts with '#', '=', '-', '\' it is treated as the end of a section.
  * However, at the same time it can also start a new section depending on what follows.
  *
  * #L (look), #W (walk), #T (talk), #U (use) sections are executed
@@ -49,6 +52,8 @@
  * #MACRO starts a new macro. Global script can call macros from local script and vice versa.
  *
  * #EXTRA defines an "extra" section. This is called from dialog responses ("TALK TO HIM" command).
+ *
+ * TODO: TIMERPROC.
  */
 
 namespace MutationOfJB {
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index d70add9..eec4621 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -28,7 +28,7 @@
 #include "common/translation.h"
 
 /** @file
- * "IFITEM" <item> ["!"]
+ * "IFITEM " <item> [ "!" ]
  *
  * IFITEM command tests whether an item is in the inventory.
  * If it is, execution continues to the next line.
diff --git a/engines/mutationofjb/commands/removeitemcommand.cpp b/engines/mutationofjb/commands/removeitemcommand.cpp
index 21d1123..be8ea23 100644
--- a/engines/mutationofjb/commands/removeitemcommand.cpp
+++ b/engines/mutationofjb/commands/removeitemcommand.cpp
@@ -25,7 +25,7 @@
 #include "mutationofjb/gamedata.h"
 
 /** @file
- * "DELITEM" " " <item>
+ * "DELITEM " <item>
  *
  * Removes item from inventory.
  */
diff --git a/engines/mutationofjb/commands/saycommand.cpp b/engines/mutationofjb/commands/saycommand.cpp
index e63ceb1..e99b4f2 100644
--- a/engines/mutationofjb/commands/saycommand.cpp
+++ b/engines/mutationofjb/commands/saycommand.cpp
@@ -33,8 +33,10 @@
 #include "common/debug-channels.h"
 
 /** @file
- * ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> ["<" <voiceFile> | "<!"]
- * <skipped> " " <lineToSay> ("<" <voiceFile> | "<!")
+ * <firstLine> { <CRLF> <additionalLine> }
+ *
+ * firstLine ::= ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> [ "<" <voiceFile> | "<!" ]
+ * additionalLine ::= <skipped> " " <lineToSay> ( "<" <voiceFile> | "<!" )
  *
  * Say command comes in four variants: SM, SLM, NM and NLM.
  * Note: In script files, they are usually written as *SM.
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index 6c969e9..e37f77f 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -33,6 +33,9 @@ public:
 	virtual void transition(ScriptParseContext &parseCtx, Command *oldCommand, Command *newCommand, CommandParser *newCommandParser) override;
 };
 
+/**
+ * Base class for sequential commands.
+ */
 class SeqCommand : public Command {
 public:
 	SeqCommand() : _nextCommand(nullptr) {}
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index 5c7eb8e..9857f38 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -29,6 +29,17 @@
 
 #include "common/str.h"
 
+/** @file
+ * "TALK TO HIM" [ " " <mode> ]
+ *
+ * Begins interactive conversation defined by DefineStructCommand.
+ * The command supports multiple modes:
+ *   0 - normal mode,
+ *   1 - Ray and Buttleg mode (two responders),
+ *   2 - unknown (unused) mode,
+ *   3 - carnival ticket seller mode (special animation).
+ */
+
 namespace MutationOfJB {
 
 bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index f25eff6..d91c0fb 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -136,10 +136,10 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	_palRotFirst = stream.readByte();
 	_palRotLast = stream.readByte();
 	_palRotDelay = stream.readByte();
-	_exhaustedChoiceNext = stream.readByte();
+	_exhaustedConvItemNext = stream.readByte();
 
 	for (i = 0; i < 79; ++i) {
-		_exhaustedChoices[i]._encodedData = stream.readByte();
+		_exhaustedConvItems[i]._encodedData = stream.readByte();
 	}
 
 	return true;
@@ -226,15 +226,15 @@ Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) {
 	return nullptr;
 }
 
-void Scene::addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) {
-	_exhaustedChoices[_exhaustedChoiceNext - 1] = ExhaustedChoice(context, choiceIndex, choiceIndexList);
-	_exhaustedChoiceNext++;
+void Scene::addExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) {
+	_exhaustedConvItems[_exhaustedConvItemNext - 1] = ExhaustedConvItem(context, convItemIndex, convGroupIndex);
+	_exhaustedConvItemNext++;
 }
 
-bool Scene::isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) const {
-	for (uint i = 0; i < _exhaustedChoiceNext - 1; ++i) {
-		const ExhaustedChoice &choice = _exhaustedChoices[i];
-		if (choice.getContext() == context && choice.getChoiceIndex() == choiceIndex && choice.getChoiceListIndex() == choiceListIndex) {
+bool Scene::isConvItemExhausted(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) const {
+	for (uint8 i = 0; i < _exhaustedConvItemNext - 1; ++i) {
+		const ExhaustedConvItem &convItem = _exhaustedConvItems[i];
+		if (convItem.getContext() == context && convItem.getConvItemIndex() == convItemIndex && convItem.getConvGroupIndex() == convGroupIndex) {
 			return true;
 		}
 	}
diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h
index 8783555..8088969 100644
--- a/engines/mutationofjb/gamedata.h
+++ b/engines/mutationofjb/gamedata.h
@@ -225,29 +225,29 @@ struct Bitmap {
 };
 
 /**
- * Encoded exhausted choice.
+ * Encoded exhausted convesation item.
  */
-struct ExhaustedChoice {
+struct ExhaustedConvItem {
 	/**
-	 * 1 bit - context
-	 * 3 bits - choice index
-	 * 4 bits - choice list index
+	 * 1 bit - context.
+	 * 3 bits - conversation item index.
+	 * 4 bits - conversation group index.
 	 */
 	uint8 _encodedData;
 
 	uint8 getContext() const {
 		return (_encodedData >> 7) & 0x1;
 	}
-	uint8 getChoiceIndex() const {
+	uint8 getConvItemIndex() const {
 		return (_encodedData >> 4) & 0x7;
 	}
-	uint8 getChoiceListIndex() const {
+	uint8 getConvGroupIndex() const {
 		return _encodedData & 0xF;
 	}
 
-	ExhaustedChoice() : _encodedData(0) {}
-	ExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) :
-		_encodedData(((context & 0x1) << 7) | ((choiceIndex & 0x7) << 4) | (choiceListIndex & 0xF)) {}
+	ExhaustedConvItem() : _encodedData(0) {}
+	ExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) :
+		_encodedData(((context & 0x1) << 7) | ((convItemIndex & 0x7) << 4) | (convGroupIndex & 0xF)) {}
 };
 
 struct Scene {
@@ -263,8 +263,8 @@ struct Scene {
 	Static *findStatic(int16 x, int16 y, int *index = nullptr);
 	Bitmap *findBitmap(int16 x, int16 y, int *index = nullptr);
 
-	void addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList);
-	bool isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) const;
+	void addExhaustedConvItem(uint8 context, uint8 convItemIndex, uint8 convGroupIndex);
+	bool isConvItemExhausted(uint8 context, uint8 convItemIndex, uint8 convGroupIndex) const;
 
 	/** Refers to the script block that will be executed when you enter this scene (DS register). */
 	uint8 _startup;
@@ -298,28 +298,25 @@ struct Scene {
 	uint8 _palRotDelay;
 
 	/**
-	 * Points to the first free item in exhausted choices list.
+	 * Points to the first free item in exhausted conversation item array.
 	 * @note Indexed from 1.
 	 */
-	uint8 _exhaustedChoiceNext;
-	ExhaustedChoice _exhaustedChoices[79];
+	uint8 _exhaustedConvItemNext;
+	ExhaustedConvItem _exhaustedConvItems[79];
 
 	bool loadFromStream(Common::ReadStream &stream);
 };
 
 struct ConversationInfo {
 	struct Item {
-		uint8 _choice;
+		uint8 _question;
 		uint8 _response;
-		uint8 _nextLineIndex;
+		uint8 _nextGroupIndex;
 	};
 
-	typedef Common::Array<Item> Items;
-	struct Line {
-		Items _items;
-	};
+	typedef Common::Array<Item> ItemGroup;
 
-	Common::Array<Line> _lines;
+	Common::Array<ItemGroup> _itemGroups;
 	uint8 _context;
 	uint8 _objectId;
 	uint8 _color;
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 8c07b32..3b1dcc5 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -46,7 +46,7 @@ void ConversationTask::start() {
 	widget.setCallback(this);
 	widget.setVisible(true);
 
-	_currentLineIndex = 0;
+	_currentGroupIndex = 0;
 
 	showChoicesOrPick();
 }
@@ -57,10 +57,10 @@ void ConversationTask::update() {
 			_sayTask.reset();
 
 			switch (_substate) {
-			case SAYING_NO_CHOICES:
+			case SAYING_NO_QUESTIONS:
 				finish();
 				break;
-			case SAYING_CHOICE: {
+			case SAYING_QUESTION: {
 				const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
 				const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
 
@@ -73,7 +73,7 @@ void ConversationTask::update() {
 				startExtra();
 
 				if (_substate != RUNNING_EXTRA) {
-					gotoNextLine();
+					gotoNextGroup();
 				}
 				break;
 			}
@@ -89,25 +89,25 @@ void ConversationTask::update() {
 			delete _innerExecCtx;
 			_innerExecCtx = nullptr;
 
-			gotoNextLine();
+			gotoNextGroup();
 		}
 	}
 }
 
 void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) {
-	const ConversationInfo::Item &item = getCurrentLine()->_items[data];
+	const ConversationInfo::Item &item = getCurrentGroup()[data];
 	convWidget->clearChoices();
 
 	const ConversationLineList &toSayList = getTaskManager()->getGame().getAssets().getToSayList();
-	const ConversationLineList::Line *line = toSayList.getLine(item._choice);
+	const ConversationLineList::Line *line = toSayList.getLine(item._question);
 
-	_substate = SAYING_CHOICE;
+	_substate = SAYING_QUESTION;
 	createSayTasks(line);
 	getTaskManager()->startTask(_sayTask);
 	_currentItem = &item;
 
 	if (!line->_speeches[0].isRepeating()) {
-		getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1);
+		getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, data + 1, _currentGroupIndex + 1);
 	}
 }
 
@@ -116,33 +116,33 @@ void ConversationTask::showChoicesOrPick() {
 	GameData &gameData = game.getGameData();
 	Scene *const scene = gameData.getScene(_sceneId);
 
-	Common::Array<uint32> itemsWithValidChoices;
+	Common::Array<uint32> itemsWithValidQuestions;
 	Common::Array<uint32> itemsWithValidResponses;
 	Common::Array<uint32> itemsWithValidNext;
 
 	/*
-		Collect valid "to say" choices (not exhausted and not empty).
+		Collect valid questions (not exhausted and not empty).
 		Collect valid responses (not exhausted and not empty).
-		If there are at least two visible choices, we show them.
-		If there is just one visible choice, pick it automatically ONLY if this is not the first choice in this conversation.
+		If there are at least two visible questions, we show them.
+		If there is just one visible question, pick it automatically ONLY if this is not the first question in this conversation.
 		Otherwise we don't start the conversation.
-		If there are no visible choices, automatically pick first valid response.
+		If there are no visible questions, automatically pick the first valid response.
 		If nothing above applies, don't start the conversation.
 	*/
 
-	const ConversationInfo::Line *const currentLine = getCurrentLine();
-	for (ConversationInfo::Items::size_type i = 0; i < currentLine->_items.size(); ++i) {
-		const ConversationInfo::Item &item = currentLine->_items[i];
+	const ConversationInfo::ItemGroup &currentGroup = getCurrentGroup();
+	for (ConversationInfo::ItemGroup::size_type i = 0; i < currentGroup.size(); ++i) {
+		const ConversationInfo::Item &item = currentGroup[i];
 
-		if (scene->isChoiceExhausted(_convInfo._context, (uint8) i + 1, (uint8) _currentLineIndex + 1)) {
+		if (scene->isConvItemExhausted(_convInfo._context, (uint8) i + 1, (uint8) _currentGroupIndex + 1)) {
 			continue;
 		}
-		const uint8 choice = item._choice;
+		const uint8 toSay = item._question;
 		const uint8 response = item._response;
-		const uint8 next = item._nextLineIndex;
+		const uint8 next = item._nextGroupIndex;
 
-		if (choice != 0) {
-			itemsWithValidChoices.push_back(i);
+		if (toSay != 0) {
+			itemsWithValidQuestions.push_back(i);
 		}
 
 		if (response != 0) {
@@ -154,38 +154,38 @@ void ConversationTask::showChoicesOrPick() {
 		}
 	}
 
-	if (itemsWithValidChoices.size() > 1) {
+	if (itemsWithValidQuestions.size() > 1) {
 		ConversationWidget &widget = game.getGui().getConversationWidget();
 		const ConversationLineList &toSayList = game.getAssets().getToSayList();
 
-		for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidChoices.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
-			const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices[i]];
-			const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
+		for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidQuestions.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
+			const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions[i]];
+			const ConversationLineList::Line *const line = toSayList.getLine(item._question);
 			const Common::String widgetText = toUpperCP895(line->_speeches[0]._text);
-			widget.setChoice((int) i, widgetText, itemsWithValidChoices[i]);
+			widget.setChoice((int) i, widgetText, itemsWithValidQuestions[i]);
 		}
 		_substate = IDLE;
 		_currentItem = nullptr;
 
 		_haveChoices = true;
-	} else if (itemsWithValidChoices.size() == 1 && _haveChoices) {
+	} else if (itemsWithValidQuestions.size() == 1 && _haveChoices) {
 		const ConversationLineList &toSayList = game.getAssets().getToSayList();
-		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()];
-		const ConversationLineList::Line *const line = toSayList.getLine(item._choice);
+		const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions.front()];
+		const ConversationLineList::Line *const line = toSayList.getLine(item._question);
 
-		_substate = SAYING_CHOICE;
+		_substate = SAYING_QUESTION;
 		createSayTasks(line);
 		getTaskManager()->startTask(_sayTask);
 		_currentItem = &item;
 
 		if (!line->_speeches[0].isRepeating()) {
-			game.getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, itemsWithValidChoices.front() + 1, _currentLineIndex + 1);
+			game.getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, itemsWithValidQuestions.front() + 1, _currentGroupIndex + 1);
 		}
 
 		_haveChoices = true;
 	} else if (!itemsWithValidResponses.empty() && _haveChoices) {
 		const ConversationLineList &responseList = game.getAssets().getResponseList();
-		const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()];
+		const ConversationInfo::Item &item = currentGroup[itemsWithValidResponses.front()];
 		const ConversationLineList::Line *const line = responseList.getLine(item._response);
 
 		_substate = SAYING_RESPONSE;
@@ -195,7 +195,7 @@ void ConversationTask::showChoicesOrPick() {
 
 		_haveChoices = true;
 	} else if (!itemsWithValidNext.empty() && _haveChoices) {
-		_currentLineIndex = currentLine->_items[itemsWithValidNext.front()]._nextLineIndex - 1;
+		_currentGroupIndex = currentGroup[itemsWithValidNext.front()]._nextGroupIndex - 1;
 		showChoicesOrPick();
 	} else {
 		if (_haveChoices) {
@@ -203,14 +203,15 @@ void ConversationTask::showChoicesOrPick() {
 		} else {
 			_sayTask = TaskPtr(new SayTask("Nothing to talk about.", _convInfo._color)); // TODO: This is hardcoded in executable. Load it.
 			getTaskManager()->startTask(_sayTask);
-			_substate = SAYING_NO_CHOICES;
+			_substate = SAYING_NO_QUESTIONS;
 			_currentItem = nullptr;
 		}
 	}
 }
 
-const ConversationInfo::Line *ConversationTask::getCurrentLine() const {
-	return &_convInfo._lines[_currentLineIndex];
+const ConversationInfo::ItemGroup &ConversationTask::getCurrentGroup() const {
+	assert(_currentGroupIndex < _convInfo._itemGroups.size());
+	return _convInfo._itemGroups[_currentGroupIndex];
 }
 
 void ConversationTask::finish() {
@@ -244,11 +245,11 @@ void ConversationTask::startExtra() {
 	}
 }
 
-void ConversationTask::gotoNextLine() {
-	if (_currentItem->_nextLineIndex == 0) {
+void ConversationTask::gotoNextGroup() {
+	if (_currentItem->_nextGroupIndex == 0) {
 		finish();
 	} else {
-		_currentLineIndex = _currentItem->_nextLineIndex - 1;
+		_currentGroupIndex = _currentItem->_nextGroupIndex - 1;
 		showChoicesOrPick();
 	}
 }
diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h
index bdfa875..9522876 100644
--- a/engines/mutationofjb/tasks/conversationtask.h
+++ b/engines/mutationofjb/tasks/conversationtask.h
@@ -33,7 +33,7 @@ class ScriptExecutionContext;
 
 class ConversationTask : public Task, public ConversationWidgetCallback {
 public:
-	ConversationTask(uint8 sceneId, const ConversationInfo &convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentLineIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
+	ConversationTask(uint8 sceneId, const ConversationInfo &convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentGroupIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
 	virtual ~ConversationTask() {}
 
 	virtual void start() override;
@@ -42,25 +42,25 @@ public:
 	virtual void onChoiceClicked(ConversationWidget *, int response, uint32 data) override;
 private:
 	void showChoicesOrPick();
-	const ConversationInfo::Line *getCurrentLine() const;
+	const ConversationInfo::ItemGroup &getCurrentGroup() const;
 	void finish();
 	void startExtra();
-	void gotoNextLine();
+	void gotoNextGroup();
 	void createSayTasks(const ConversationLineList::Line *line);
 	uint8 getSpeechColor(const ConversationLineList::Speech &speech);
 
 	uint8 _sceneId;
 	const ConversationInfo &_convInfo;
 	TalkCommand::Mode _mode;
-	uint _currentLineIndex;
+	uint _currentGroupIndex;
 	const ConversationInfo::Item *_currentItem;
 	TaskPtr _sayTask;
 
 	enum Substate {
 		IDLE,
-		SAYING_CHOICE,
+		SAYING_QUESTION,
 		SAYING_RESPONSE,
-		SAYING_NO_CHOICES,
+		SAYING_NO_QUESTIONS,
 		RUNNING_EXTRA
 	};
 
diff --git a/engines/mutationofjb/tasks/objectanimationtask.cpp b/engines/mutationofjb/tasks/objectanimationtask.cpp
index e93602c..2b4bf80 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.cpp
+++ b/engines/mutationofjb/tasks/objectanimationtask.cpp
@@ -56,7 +56,7 @@ void ObjectAnimationTask::update() {
  * Additionally, there is a chance with each frame until _randomFrame that the animation may jump
  * straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
  *
- * Randomness is used to introduce variety - e.g. in the starting scene a perched bird occassionally
+ * Randomness is used to introduce variety - e.g. in the starting scene a perched bird occasionally
  * spreads its wings.
  */
 void ObjectAnimationTask::updateObjects() {
diff --git a/engines/mutationofjb/tasks/sequentialtask.h b/engines/mutationofjb/tasks/sequentialtask.h
index 078fd8a..1c00751 100644
--- a/engines/mutationofjb/tasks/sequentialtask.h
+++ b/engines/mutationofjb/tasks/sequentialtask.h
@@ -27,6 +27,9 @@
 
 namespace MutationOfJB {
 
+/**
+ * Queues multiple tasks.
+ */
 class SequentialTask : public Task {
 public:
 	SequentialTask(const TaskPtrs &tasks);
diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h
index b14fc97..115668a 100644
--- a/engines/mutationofjb/tasks/task.h
+++ b/engines/mutationofjb/tasks/task.h
@@ -31,6 +31,9 @@ namespace MutationOfJB {
 
 class TaskManager;
 
+/**
+ * Base class for tasks.
+ */
 class Task {
 public:
 	enum State {
diff --git a/engines/mutationofjb/tasks/taskmanager.h b/engines/mutationofjb/tasks/taskmanager.h
index 2f351c0..c88da59 100644
--- a/engines/mutationofjb/tasks/taskmanager.h
+++ b/engines/mutationofjb/tasks/taskmanager.h
@@ -31,6 +31,11 @@ namespace MutationOfJB {
 class Game;
 class Task;
 
+/**
+ * Handles task management.
+ *
+ * Tasks are a way run game logic asynchronously.
+ */
 class TaskManager {
 public:
 	TaskManager(Game &game) : _game(game) {}


Commit: 9f1c628d4bfbd6600ceb67b5b872cf08825dc1e4
    https://github.com/scummvm/scummvm/commit/9f1c628d4bfbd6600ceb67b5b872cf08825dc1e4
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix forward declarations of structs.

Changed paths:
    engines/mutationofjb/commands/endblockcommand.h
    engines/mutationofjb/game.h
    engines/mutationofjb/gui.h
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/script.h
    engines/mutationofjb/widgets/widget.h


diff --git a/engines/mutationofjb/commands/endblockcommand.h b/engines/mutationofjb/commands/endblockcommand.h
index 55656aa..bda50fb 100644
--- a/engines/mutationofjb/commands/endblockcommand.h
+++ b/engines/mutationofjb/commands/endblockcommand.h
@@ -29,7 +29,7 @@
 
 namespace MutationOfJB {
 
-class ActionInfo;
+struct ActionInfo;
 
 class EndBlockCommandParser : public CommandParser {
 public:
diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h
index 316dc75..9ab52ef 100644
--- a/engines/mutationofjb/game.h
+++ b/engines/mutationofjb/game.h
@@ -40,13 +40,13 @@ namespace MutationOfJB {
 
 class Command;
 class MutationOfJBEngine;
-class GameData;
 class Script;
 class Room;
-class Door;
-class Static;
-class Bitmap;
 class SayTask;
+struct GameData;
+struct Door;
+struct Static;
+struct Bitmap;
 
 class Game {
 public:
diff --git a/engines/mutationofjb/gui.h b/engines/mutationofjb/gui.h
index f644517..354d04f 100644
--- a/engines/mutationofjb/gui.h
+++ b/engines/mutationofjb/gui.h
@@ -31,7 +31,7 @@
 #include "mutationofjb/widgets/buttonwidget.h"
 
 namespace Common {
-class Event;
+struct Event;
 }
 
 namespace Graphics {
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 050800e..91a3138 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -27,7 +27,7 @@
 #include "mutationofjb/script.h"
 
 namespace Common {
-class Event;
+struct Event;
 }
 
 namespace Graphics {
diff --git a/engines/mutationofjb/script.h b/engines/mutationofjb/script.h
index 17e1810..86f2450 100644
--- a/engines/mutationofjb/script.h
+++ b/engines/mutationofjb/script.h
@@ -39,13 +39,13 @@ namespace MutationOfJB {
 class Command;
 class LabelCommand;
 class Game;
-class GameData;
 class GotoCommand;
 class ConditionalCommand;
 class Script;
 class RandomCommand;
-typedef Common::Array<Command *> Commands;
+struct GameData;
 
+typedef Common::Array<Command *> Commands;
 
 struct ActionInfo {
 	enum Action {
diff --git a/engines/mutationofjb/widgets/widget.h b/engines/mutationofjb/widgets/widget.h
index ed3a3ac..a88f628 100644
--- a/engines/mutationofjb/widgets/widget.h
+++ b/engines/mutationofjb/widgets/widget.h
@@ -27,7 +27,7 @@
 #include <common/rect.h>
 
 namespace Common {
-class Event;
+struct Event;
 }
 
 namespace Graphics {


Commit: 561309eaa2fb2d5298567366068ac9daafc7c2f7
    https://github.com/scummvm/scummvm/commit/561309eaa2fb2d5298567366068ac9daafc7c2f7
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix missing lines between block ends.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/definestructcommand.cpp
    engines/mutationofjb/commands/definestructcommand.h
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/ifpiggycommand.cpp
    engines/mutationofjb/commands/labelcommand.cpp
    engines/mutationofjb/commands/newroomcommand.cpp
    engines/mutationofjb/commands/randomcommand.cpp
    engines/mutationofjb/commands/seqcommand.h
    engines/mutationofjb/debug.h
    engines/mutationofjb/detection.cpp
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/script.cpp
    engines/mutationofjb/util.cpp
    engines/mutationofjb/util.h
    engines/mutationofjb/widgets/buttonwidget.cpp
    engines/mutationofjb/widgets/buttonwidget.h
    engines/mutationofjb/widgets/conversationwidget.cpp
    engines/mutationofjb/widgets/conversationwidget.h
    engines/mutationofjb/widgets/imagewidget.cpp
    engines/mutationofjb/widgets/imagewidget.h
    engines/mutationofjb/widgets/inventorywidget.cpp
    engines/mutationofjb/widgets/inventorywidget.h
    engines/mutationofjb/widgets/widget.cpp
    engines/mutationofjb/widgets/widget.h


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index 7d2e7e6..e3c8a99 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -556,4 +556,5 @@ Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scrip
 Common::String ChangeSceneCommand::debugString() const {
 	return Common::String::format("SCENE%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
 }
+
 }
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index 27b2714..2a6b36a 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -46,9 +46,7 @@ void ConditionalCommandParser::finish(ScriptParseContext &) {
 ConditionalCommand::ConditionalCommand() :
 	_trueCommand(nullptr),
 	_falseCommand(nullptr),
-	_cachedResult(false)
-{}
-
+	_cachedResult(false) {}
 
 Command *ConditionalCommand::getTrueCommand() const {
 	return _trueCommand;
@@ -73,4 +71,5 @@ Command *ConditionalCommand::next() const {
 		return _falseCommand;
 	}
 }
+
 }
diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp
index bea3497..455e243 100644
--- a/engines/mutationofjb/commands/definestructcommand.cpp
+++ b/engines/mutationofjb/commands/definestructcommand.cpp
@@ -96,4 +96,5 @@ Command::ExecuteResult DefineStructCommand::execute(ScriptExecutionContext &scri
 Common::String DefineStructCommand::debugString() const {
 	return "DEFINE_STRUCT <data omitted>";
 }
+
 }
diff --git a/engines/mutationofjb/commands/definestructcommand.h b/engines/mutationofjb/commands/definestructcommand.h
index 13fc910..651f943 100644
--- a/engines/mutationofjb/commands/definestructcommand.h
+++ b/engines/mutationofjb/commands/definestructcommand.h
@@ -38,4 +38,5 @@ public:
 private:
 	ConversationInfo _conversationInfo;
 };
+
 }
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 06fb5ca..9c43588 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -77,12 +77,12 @@ bool IfCommandParser::parse(const Common::String &line, ScriptParseContext &, Co
 	return true;
 }
 
+
 IfCommand::IfCommand(uint8 sceneId, uint8 objectId, uint16 value, bool negative) :
 	_sceneId(sceneId),
 	_objectId(objectId),
 	_value(value),
-	_negative(negative)
-{}
+	_negative(negative) {}
 
 Command::ExecuteResult IfCommand::execute(ScriptExecutionContext &scriptExecCtx) {
 	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
@@ -108,4 +108,3 @@ Common::String IfCommand::debugString() const {
 }
 
 }
-
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index eec4621..ccbf09e 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -70,8 +70,7 @@ bool IfItemCommandParser::parse(const Common::String &line, ScriptParseContext &
 
 IfItemCommand::IfItemCommand(const Common::String &item, bool negative) :
 	_item(item),
-	_negative(negative)
-{}
+	_negative(negative) {}
 
 Command::ExecuteResult IfItemCommand::execute(ScriptExecutionContext &scriptExecCtx) {
 	_cachedResult = scriptExecCtx.getGameData()._inventory.hasItem(_item);
@@ -87,4 +86,3 @@ Common::String IfItemCommand::debugString() const {
 }
 
 }
-
diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
index e3107806..d500b4a 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.cpp
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -68,4 +68,3 @@ Common::String IfPiggyCommand::debugString() const {
 }
 
 }
-
diff --git a/engines/mutationofjb/commands/labelcommand.cpp b/engines/mutationofjb/commands/labelcommand.cpp
index 7593e52..ef2410a 100644
--- a/engines/mutationofjb/commands/labelcommand.cpp
+++ b/engines/mutationofjb/commands/labelcommand.cpp
@@ -59,6 +59,7 @@ bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &p
 	return true;
 }
 
+
 const Common::String &LabelCommand::getName() const {
 	return _name;
 }
diff --git a/engines/mutationofjb/commands/newroomcommand.cpp b/engines/mutationofjb/commands/newroomcommand.cpp
index 5aabc67..0f4d214 100644
--- a/engines/mutationofjb/commands/newroomcommand.cpp
+++ b/engines/mutationofjb/commands/newroomcommand.cpp
@@ -80,4 +80,3 @@ Common::String NewRoomCommand::debugString() const {
 }
 
 }
-
diff --git a/engines/mutationofjb/commands/randomcommand.cpp b/engines/mutationofjb/commands/randomcommand.cpp
index 467f788..260fd3a 100644
--- a/engines/mutationofjb/commands/randomcommand.cpp
+++ b/engines/mutationofjb/commands/randomcommand.cpp
@@ -61,6 +61,7 @@ bool RandomCommandParser::parse(const Common::String &line, ScriptParseContext &
 	return true;
 }
 
+
 bool RandomBlockStartParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&) {
 	if (line != "/") {
 		return false;
@@ -84,6 +85,7 @@ void RandomBlockStartParser::transition(ScriptParseContext &parseCtx, Command *,
 	}
 }
 
+
 RandomCommand::RandomCommand(uint numChoices)
 	: _numChoices(numChoices),
 	  _chosenNext(nullptr) {
diff --git a/engines/mutationofjb/commands/seqcommand.h b/engines/mutationofjb/commands/seqcommand.h
index e37f77f..f61f33b 100644
--- a/engines/mutationofjb/commands/seqcommand.h
+++ b/engines/mutationofjb/commands/seqcommand.h
@@ -49,4 +49,3 @@ private:
 }
 
 #endif
-
diff --git a/engines/mutationofjb/debug.h b/engines/mutationofjb/debug.h
index 24b1e95..b5c40c0 100644
--- a/engines/mutationofjb/debug.h
+++ b/engines/mutationofjb/debug.h
@@ -60,4 +60,3 @@ private:
 }
 
 #endif
-
diff --git a/engines/mutationofjb/detection.cpp b/engines/mutationofjb/detection.cpp
index f95f696..5e80760 100644
--- a/engines/mutationofjb/detection.cpp
+++ b/engines/mutationofjb/detection.cpp
@@ -110,4 +110,3 @@ public:
 #else
 	REGISTER_PLUGIN_STATIC(MUTATIONOFJB, PLUGIN_TYPE_ENGINE, MutationOfJBMetaEngine);
 #endif
-
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 2320255..1d88269 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -52,7 +52,6 @@ MutationOfJBEngine::~MutationOfJBEngine() {
 	debug("MutationOfJBEngine::~MutationOfJBEngine");
 }
 
-
 void MutationOfJBEngine::setupCursor() {
 	const uint8 cursor[] = {
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 91a3138..21f8094 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -72,7 +72,6 @@ private:
 	CursorState _cursorState;
 };
 
-
 }
 
 #endif
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index fb148c1..01aef2c 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -87,8 +87,7 @@ ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) :
 	_stream(stream),
 	_currentCommand(nullptr),
 	_lastCommand(nullptr),
-	_pendingRandomCommand(nullptr)
-{}
+	_pendingRandomCommand(nullptr) {}
 
 bool ScriptParseContext::readLine(Common::String &line) {
 	do {
diff --git a/engines/mutationofjb/util.cpp b/engines/mutationofjb/util.cpp
index 2e3b0b3..1483fb2 100644
--- a/engines/mutationofjb/util.cpp
+++ b/engines/mutationofjb/util.cpp
@@ -58,4 +58,3 @@ Common::String toUpperCP895(const Common::String &str) {
 }
 
 }
-
diff --git a/engines/mutationofjb/util.h b/engines/mutationofjb/util.h
index fe72794..a98f6ff 100644
--- a/engines/mutationofjb/util.h
+++ b/engines/mutationofjb/util.h
@@ -32,7 +32,6 @@ namespace Common {
 class String;
 }
 
-
 namespace MutationOfJB {
 
 void reportFileMissingError(const char *fileName);
diff --git a/engines/mutationofjb/widgets/buttonwidget.cpp b/engines/mutationofjb/widgets/buttonwidget.cpp
index 911cb7d..e2a9dd3 100644
--- a/engines/mutationofjb/widgets/buttonwidget.cpp
+++ b/engines/mutationofjb/widgets/buttonwidget.cpp
@@ -62,7 +62,9 @@ void ButtonWidget::handleEvent(const Common::Event &event) {
 		break;
 	}
 }
-void ButtonWidget::_draw(Graphics::ManagedSurface &surface) {
+
+void ButtonWidget::draw(Graphics::ManagedSurface &surface) {
 	surface.blitFrom(_pressed ? _pressedSurface : _normalSurface, Common::Point(_area.left, _area.top));
 }
+
 }
diff --git a/engines/mutationofjb/widgets/buttonwidget.h b/engines/mutationofjb/widgets/buttonwidget.h
index de8cb63..d148fa8 100644
--- a/engines/mutationofjb/widgets/buttonwidget.h
+++ b/engines/mutationofjb/widgets/buttonwidget.h
@@ -44,7 +44,7 @@ public:
 	virtual void handleEvent(const Common::Event &event) override;
 
 protected:
-	virtual void _draw(Graphics::ManagedSurface &) override;
+	virtual void draw(Graphics::ManagedSurface &) override;
 
 private:
 	Graphics::Surface _normalSurface;
diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp
index e0bbf66..56e0057 100644
--- a/engines/mutationofjb/widgets/conversationwidget.cpp
+++ b/engines/mutationofjb/widgets/conversationwidget.cpp
@@ -59,7 +59,7 @@ void ConversationWidget::clearChoices() {
 	markDirty();
 }
 
-void ConversationWidget::_draw(Graphics::ManagedSurface &surface) {
+void ConversationWidget::draw(Graphics::ManagedSurface &surface) {
 	surface.blitFrom(_surface, Common::Point(_area.left, _area.top));
 
 	for (int i = 0; i < CONVERSATION_MAX_CHOICES; ++i) {
@@ -94,4 +94,3 @@ void ConversationWidget::handleEvent(const Common::Event &event) {
 }
 
 }
-
diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h
index abde702..34ea215 100644
--- a/engines/mutationofjb/widgets/conversationwidget.h
+++ b/engines/mutationofjb/widgets/conversationwidget.h
@@ -51,7 +51,7 @@ public:
 	virtual void handleEvent(const Common::Event &event) override;
 
 protected:
-	virtual void _draw(Graphics::ManagedSurface &surface) override;
+	virtual void draw(Graphics::ManagedSurface &surface) override;
 
 private:
 	Graphics::Surface _surface;
diff --git a/engines/mutationofjb/widgets/imagewidget.cpp b/engines/mutationofjb/widgets/imagewidget.cpp
index 3395da8..8eeb7ed 100644
--- a/engines/mutationofjb/widgets/imagewidget.cpp
+++ b/engines/mutationofjb/widgets/imagewidget.cpp
@@ -30,7 +30,7 @@ ImageWidget::ImageWidget(Gui &gui, const Common::Rect &area, const Graphics::Sur
 	_image(image) {}
 
 
-void ImageWidget::_draw(Graphics::ManagedSurface &surface) {
+void ImageWidget::draw(Graphics::ManagedSurface &surface) {
 	surface.blitFrom(_image, Common::Point(_area.left, _area.top));
 }
 
diff --git a/engines/mutationofjb/widgets/imagewidget.h b/engines/mutationofjb/widgets/imagewidget.h
index 4585a72..da3dd36 100644
--- a/engines/mutationofjb/widgets/imagewidget.h
+++ b/engines/mutationofjb/widgets/imagewidget.h
@@ -33,7 +33,7 @@ public:
 	ImageWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &image);
 
 protected:
-	virtual void _draw(Graphics::ManagedSurface &surface) override;
+	virtual void draw(Graphics::ManagedSurface &surface) override;
 
 private:
 	Graphics::Surface _image;
diff --git a/engines/mutationofjb/widgets/inventorywidget.cpp b/engines/mutationofjb/widgets/inventorywidget.cpp
index 96f35e4..a26bab6 100644
--- a/engines/mutationofjb/widgets/inventorywidget.cpp
+++ b/engines/mutationofjb/widgets/inventorywidget.cpp
@@ -63,7 +63,7 @@ void InventoryWidget::drawInventoryItem(Graphics::ManagedSurface &surface, const
 	surface.blitFrom(_surfaces[surfaceNo], sourceRect, destStartPos);
 }
 
-void InventoryWidget::_draw(Graphics::ManagedSurface &surface) {
+void InventoryWidget::draw(Graphics::ManagedSurface &surface) {
 	Inventory &inventory = _gui.getGame().getGameData().getInventory();
 	const Inventory::Items &items = inventory.getItems();
 	surface.fillRect(_area, 0x00);
diff --git a/engines/mutationofjb/widgets/inventorywidget.h b/engines/mutationofjb/widgets/inventorywidget.h
index 8d6205e..f1841e8 100644
--- a/engines/mutationofjb/widgets/inventorywidget.h
+++ b/engines/mutationofjb/widgets/inventorywidget.h
@@ -35,7 +35,7 @@ namespace MutationOfJB {
 class InventoryWidget : public Widget {
 public:
 	InventoryWidget(Gui &gui, Gui::InventoryMap &inventoryMap, const Common::Array<Graphics::Surface> &inventorySurfaces);
-	virtual void _draw(Graphics::ManagedSurface &) override;
+	virtual void draw(Graphics::ManagedSurface &) override;
 
 private:
 	void drawInventoryItem(Graphics::ManagedSurface &surface, const Common::String &item, int pos);
diff --git a/engines/mutationofjb/widgets/widget.cpp b/engines/mutationofjb/widgets/widget.cpp
index 5503f62..b030211 100644
--- a/engines/mutationofjb/widgets/widget.cpp
+++ b/engines/mutationofjb/widgets/widget.cpp
@@ -54,7 +54,7 @@ bool Widget::isDirty() const {
 void Widget::update(Graphics::ManagedSurface &surface) {
 	if (_dirty) {
 		if (_visible) {
-			_draw(surface);
+			draw(surface);
 		}
 		_dirty = false;
 	}
diff --git a/engines/mutationofjb/widgets/widget.h b/engines/mutationofjb/widgets/widget.h
index a88f628..e16b1e4 100644
--- a/engines/mutationofjb/widgets/widget.h
+++ b/engines/mutationofjb/widgets/widget.h
@@ -55,7 +55,7 @@ public:
 
 	virtual void handleEvent(const Common::Event &) {}
 protected:
-	virtual void _draw(Graphics::ManagedSurface &) = 0;
+	virtual void draw(Graphics::ManagedSurface &) = 0;
 
 	Gui &_gui;
 	Common::Rect _area;
@@ -67,4 +67,3 @@ protected:
 }
 
 #endif
-


Commit: 959f37dfe4f0d46a28217f85832af07b73e190c0
    https://github.com/scummvm/scummvm/commit/959f37dfe4f0d46a28217f85832af07b73e190c0
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Don't mark internal strings as translatable.

Changed paths:
    engines/mutationofjb/animationdecoder.cpp
    engines/mutationofjb/commands/callmacrocommand.cpp
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/conditionalcommand.cpp
    engines/mutationofjb/commands/endblockcommand.cpp
    engines/mutationofjb/commands/ifcommand.cpp
    engines/mutationofjb/commands/ifitemcommand.cpp
    engines/mutationofjb/commands/ifpiggycommand.cpp
    engines/mutationofjb/commands/randomcommand.cpp
    engines/mutationofjb/commands/seqcommand.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/game.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/room.cpp
    engines/mutationofjb/script.cpp
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/tasks/taskmanager.cpp


diff --git a/engines/mutationofjb/animationdecoder.cpp b/engines/mutationofjb/animationdecoder.cpp
index 712cb42..43590a9 100644
--- a/engines/mutationofjb/animationdecoder.cpp
+++ b/engines/mutationofjb/animationdecoder.cpp
@@ -24,7 +24,6 @@
 #include "mutationofjb/encryptedfile.h"
 #include "mutationofjb/util.h"
 #include "common/debug.h"
-#include "common/translation.h"
 
 namespace MutationOfJB {
 
@@ -88,7 +87,7 @@ bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
 							callback->onFrame(frameNo, _surface);
 						}
 					} else {
-						debug(_("Unsupported record type %02X."), type);
+						debug("Unsupported record type %02X.", type);
 						file.seek(subLength - 6, SEEK_CUR);
 					}
 
diff --git a/engines/mutationofjb/commands/callmacrocommand.cpp b/engines/mutationofjb/commands/callmacrocommand.cpp
index 01470f2..c2ed50f 100644
--- a/engines/mutationofjb/commands/callmacrocommand.cpp
+++ b/engines/mutationofjb/commands/callmacrocommand.cpp
@@ -23,7 +23,6 @@
 #include "mutationofjb/commands/callmacrocommand.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/game.h"
-#include "common/translation.h"
 
 /** @file
  * "_" <name>
@@ -45,7 +44,7 @@ bool CallMacroCommandParser::parse(const Common::String &line, ScriptParseContex
 
 void CallMacroCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
-		warning(_("Unexpected empty command in transition"));
+		warning("Unexpected empty command in transition");
 		return;
 	}
 
diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index e3c8a99..73c5357 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -23,7 +23,6 @@
 #include "mutationofjb/commands/changecommand.h"
 #include "mutationofjb/script.h"
 #include "mutationofjb/gamedata.h"
-#include "common/translation.h"
 
 /** @file
  * "CHANGE" <entity> " " <register> " " <sceneId> " " <entityId> " " <value>
diff --git a/engines/mutationofjb/commands/conditionalcommand.cpp b/engines/mutationofjb/commands/conditionalcommand.cpp
index 2a6b36a..56cc2bf 100644
--- a/engines/mutationofjb/commands/conditionalcommand.cpp
+++ b/engines/mutationofjb/commands/conditionalcommand.cpp
@@ -23,13 +23,12 @@
 #include "mutationofjb/commands/conditionalcommand.h"
 #include "mutationofjb/script.h"
 #include "common/scummsys.h"
-#include "common/translation.h"
 
 namespace MutationOfJB {
 
 void ConditionalCommandParser::transition(ScriptParseContext &parseContext, Command *oldCommand, Command *newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
-		warning(_("Unexpected empty command in transition"));
+		warning("Unexpected empty command in transition");
 		return;
 	}
 
diff --git a/engines/mutationofjb/commands/endblockcommand.cpp b/engines/mutationofjb/commands/endblockcommand.cpp
index 8ad11f8..ae8ad91 100644
--- a/engines/mutationofjb/commands/endblockcommand.cpp
+++ b/engines/mutationofjb/commands/endblockcommand.cpp
@@ -25,7 +25,6 @@
 #include "mutationofjb/commands/conditionalcommand.h"
 #include "common/str.h"
 #include "common/debug.h"
-#include "common/translation.h"
 
 /** @file
  * <look> | <walk> | <talk> | <pickup> | <use> | <else> | <macro> | <extra> | <endRandom>
@@ -171,7 +170,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *ol
 				if (!parseCtx._macros.contains(it->_name)) {
 					parseCtx._macros[it->_name] = newCommand;
 				} else {
-					warning(_("Macro '%s' already exists"), it->_name.c_str());
+					warning("Macro '%s' already exists", it->_name.c_str());
 				}
 				it = _foundMacros.erase(it);
 			}
@@ -188,7 +187,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *ol
 				if (!parseCtx._startups.contains(it->_id)) {
 					parseCtx._startups[it->_id] = newCommand;
 				} else {
-					warning(_("Startup %u already exists"), (unsigned int) it->_id);
+					warning("Startup %u already exists", (unsigned int) it->_id);
 				}
 				it = _foundStartups.erase(it);
 			}
@@ -205,7 +204,7 @@ void EndBlockCommandParser::transition(ScriptParseContext &parseCtx, Command *ol
 				if (!parseCtx._extras.contains(it->_name)) {
 					parseCtx._extras[it->_name] = newCommand;
 				} else {
-					warning(_("Extra '%s' already exists"), it->_name.c_str());
+					warning("Extra '%s' already exists", it->_name.c_str());
 				}
 				it = _foundExtras.erase(it);
 			}
diff --git a/engines/mutationofjb/commands/ifcommand.cpp b/engines/mutationofjb/commands/ifcommand.cpp
index 9c43588..47e06f3 100644
--- a/engines/mutationofjb/commands/ifcommand.cpp
+++ b/engines/mutationofjb/commands/ifcommand.cpp
@@ -24,7 +24,6 @@
 #include "mutationofjb/gamedata.h"
 #include "mutationofjb/script.h"
 #include "common/str.h"
-#include "common/translation.h"
 
 /** @file
  * "IF" <tag> <sceneId> <objectId> <value> ["!"]
diff --git a/engines/mutationofjb/commands/ifitemcommand.cpp b/engines/mutationofjb/commands/ifitemcommand.cpp
index ccbf09e..9695172 100644
--- a/engines/mutationofjb/commands/ifitemcommand.cpp
+++ b/engines/mutationofjb/commands/ifitemcommand.cpp
@@ -25,7 +25,6 @@
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
-#include "common/translation.h"
 
 /** @file
  * "IFITEM " <item> [ "!" ]
diff --git a/engines/mutationofjb/commands/ifpiggycommand.cpp b/engines/mutationofjb/commands/ifpiggycommand.cpp
index d500b4a..4df1deb 100644
--- a/engines/mutationofjb/commands/ifpiggycommand.cpp
+++ b/engines/mutationofjb/commands/ifpiggycommand.cpp
@@ -25,7 +25,6 @@
 #include "mutationofjb/script.h"
 #include "mutationofjb/util.h"
 #include "common/str.h"
-#include "common/translation.h"
 
 /** @file
  * "IFPIGGY"
diff --git a/engines/mutationofjb/commands/randomcommand.cpp b/engines/mutationofjb/commands/randomcommand.cpp
index 260fd3a..ab0f304 100644
--- a/engines/mutationofjb/commands/randomcommand.cpp
+++ b/engines/mutationofjb/commands/randomcommand.cpp
@@ -26,7 +26,6 @@
 #include "mutationofjb/script.h"
 #include "common/debug.h"
 #include "common/random.h"
-#include "common/translation.h"
 
 /** @file
  * "RANDOM " <numChoices>
@@ -49,13 +48,13 @@ bool RandomCommandParser::parse(const Common::String &line, ScriptParseContext &
 	int numChoices = atoi(line.c_str() + 7);
 	if (parseCtx._pendingRandomCommand) {
 		// Nested RANDOM commands are unused and not properly supported by the original game.
-		warning(_("Ignoring nested RANDOM command."));
+		warning("Ignoring nested RANDOM command.");
 	} else if (numChoices >= 1) {
 		RandomCommand *randomCommand = new RandomCommand(static_cast<uint>(numChoices));
 		parseCtx._pendingRandomCommand = randomCommand;
 		command = randomCommand;
 	} else {
-		warning(_("Ignoring malformed RANDOM command with %d choices."), numChoices);
+		warning("Ignoring malformed RANDOM command with %d choices.", numChoices);
 	}
 
 	return true;
@@ -68,7 +67,7 @@ bool RandomBlockStartParser::parse(const Common::String &line, ScriptParseContex
 	}
 
 	if (!parseCtx._pendingRandomCommand) {
-		warning(_("Unexpected start of RANDOM block"));
+		warning("Unexpected start of RANDOM block");
 	}
 
 	return true;
diff --git a/engines/mutationofjb/commands/seqcommand.cpp b/engines/mutationofjb/commands/seqcommand.cpp
index 4836d03..6356986 100644
--- a/engines/mutationofjb/commands/seqcommand.cpp
+++ b/engines/mutationofjb/commands/seqcommand.cpp
@@ -21,13 +21,14 @@
  */
 
 #include "mutationofjb/commands/seqcommand.h"
-#include "common/translation.h"
+
+#include "common/textconsole.h"
 
 namespace MutationOfJB {
 
 void SeqCommandParser::transition(ScriptParseContext &, Command *oldCommand, Command *newCommand, CommandParser *) {
 	if (!oldCommand || !newCommand) {
-		warning(_("Unexpected empty command in transition"));
+		warning("Unexpected empty command in transition");
 		return;
 	}
 
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index f7b074a..b5b49cd 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -32,7 +32,6 @@
 #include "mutationofjb/commands/callmacrocommand.h"
 #include "mutationofjb/commands/randomcommand.h"
 #include "common/debug-channels.h"
-#include "common/translation.h"
 #include "common/scummsys.h"
 
 namespace MutationOfJB {
@@ -84,7 +83,7 @@ bool Console::cmd_showallcommands(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("showallcommands <G|L>\n"));
+		debugPrintf("showallcommands <G|L>\n");
 	}
 
 	return true;
@@ -98,21 +97,21 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 			const char *word = nullptr;
 			if (strcmp(argv[2], "L") == 0) {
 				action = ActionInfo::Look;
-				word = _("Look");
+				word = "Look";
 			} else if (strcmp(argv[2], "W") == 0) {
 				action = ActionInfo::Walk;
-				word = _("Walk");
+				word = "Walk";
 			} else if (strcmp(argv[2], "T") == 0) {
 				action = ActionInfo::Talk;
-				word = _("Talk");
+				word = "Talk";
 			} else if (strcmp(argv[2], "U") == 0) {
 				action = ActionInfo::Use;
-				word = _("Use");
+				word = "Use";
 			} else if (strcmp(argv[2], "P") == 0) {
 				action = ActionInfo::PickUp;
-				word = _("Pick up");
+				word = "Pick up";
 			} else {
-				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n"));
+				debugPrintf("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n");
 			}
 			if (word) {
 				const ActionInfos &actionInfos = script->getActionInfos(action);
@@ -127,7 +126,7 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("listsections <G|L> <L|W|T|U|P>\n"));
+		debugPrintf("listsections <G|L> <L|W|T|U|P>\n");
 	}
 	return true;
 }
@@ -187,7 +186,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 			} else if (strcmp(argv[2], "P") == 0) {
 				action = ActionInfo::PickUp;
 			} else {
-				debugPrintf(_("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n"));
+				debugPrintf("Choose 'L' (look), 'W' (walk), 'T' (talk), 'U' (use) or 'P' (pick up).\n");
 				correctAction = false;
 			}
 
@@ -212,7 +211,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("showsection <G|L> <L|W|T|U|P> <sectionname>\n"));
+		debugPrintf("showsection <G|L> <L|W|T|U|P> <sectionname>\n");
 	}
 
 	return true;
@@ -228,7 +227,7 @@ bool Console::cmd_listmacros(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("listmacros <G|L>\n"));
+		debugPrintf("listmacros <G|L>\n");
 	}
 
 	return true;
@@ -243,7 +242,7 @@ bool Console::cmd_showmacro(int argc, const char **argv) {
 			script = _vm->getGame().getLocalScript();
 		}
 		if (!script) {
-			debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+			debugPrintf("Choose 'G' (global) or 'L' (local) script.\n");
 		} else {
 			const Macros &macros = script->getMacros();
 			Macros::const_iterator itMacro = macros.find(argv[2]);
@@ -256,7 +255,7 @@ bool Console::cmd_showmacro(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("showmacro <G|L> <macroname>\n"));
+		debugPrintf("showmacro <G|L> <macroname>\n");
 	}
 
 	return true;
@@ -272,7 +271,7 @@ bool Console::cmd_liststartups(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("liststartups <G|L>\n"));
+		debugPrintf("liststartups <G|L>\n");
 	}
 
 	return true;
@@ -293,7 +292,7 @@ bool Console::cmd_showstartup(int argc, const char **argv) {
 			}
 		}
 	} else {
-		debugPrintf(_("showstartup <G|L> <startupid>\n"));
+		debugPrintf("showstartup <G|L> <startupid>\n");
 	}
 
 	return true;
@@ -306,7 +305,7 @@ bool Console::cmd_changescene(int argc, const char **argv) {
 
 		_vm->getGame().changeScene(sceneId, partB);
 	} else {
-		debugPrintf(_("changescene <scenename>\n"));
+		debugPrintf("changescene <scenename>\n");
 	}
 
 	return true;
@@ -327,10 +326,10 @@ bool Console::cmd_dumpsceneinfo(int argc, const char **argv) {
 			debugPrintf("PalRotLast: %u\n", (unsigned int) scene->_palRotLast);
 			debugPrintf("PalRotDelay: %u\n", (unsigned int) scene->_palRotDelay);
 		} else {
-			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+			debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
 		}
 	} else {
-		debugPrintf(_("dumpsceneinfo <sceneid>\n"));
+		debugPrintf("dumpsceneinfo <sceneid>\n");
 	}
 
 	return true;
@@ -357,13 +356,13 @@ bool Console::cmd_dumpdoorinfo(int argc, const char **argv) {
 				debugPrintf("WalkToY: %u\n", (unsigned int) door->_walkToY);
 				debugPrintf("SP: %u\n", (unsigned int) door->_SP);
 			} else {
-				debugPrintf(_("Door %u not found.\n"), (unsigned int) doorId);
+				debugPrintf("Door %u not found.\n", (unsigned int) doorId);
 			}
 		} else {
-			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+			debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
 		}
 	} else {
-		debugPrintf(_("dumpdoorinfo <sceneid> <doorid>\n"));
+		debugPrintf("dumpdoorinfo <sceneid> <doorid>\n");
 	}
 
 	return true;
@@ -392,13 +391,13 @@ bool Console::cmd_dumpobjectinfo(int argc, const char **argv) {
 				debugPrintf("WY: %u\n", (unsigned int) object->_roomFrameMSB);
 				debugPrintf("SP: %u\n", (unsigned int) object->_SP);
 			} else {
-				debugPrintf(_("Object %u not found.\n"), (unsigned int) objectId);
+				debugPrintf("Object %u not found.\n", (unsigned int) objectId);
 			}
 		} else {
-			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+			debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
 		}
 	} else {
-		debugPrintf(_("dumpobjectinfo <sceneid> <objectid>\n"));
+		debugPrintf("dumpobjectinfo <sceneid> <objectid>\n");
 	}
 
 	return true;
@@ -423,13 +422,13 @@ bool Console::cmd_dumpstaticinfo(int argc, const char **argv) {
 				debugPrintf("WalkToY: %u\n", (unsigned int) stat->_walkToX);
 				debugPrintf("WalkToFrame: %u\n", (unsigned int) stat->_walkToFrame);
 			} else {
-				debugPrintf(_("Static %u not found.\n"), (unsigned int) staticId);
+				debugPrintf("Static %u not found.\n", (unsigned int) staticId);
 			}
 		} else {
-			debugPrintf(_("Scene %u not found.\n"), (unsigned int) sceneId);
+			debugPrintf("Scene %u not found.\n", (unsigned int) sceneId);
 		}
 	} else {
-		debugPrintf(_("dumpstaticinfo <sceneid> <staticid>\n"));
+		debugPrintf("dumpstaticinfo <sceneid> <staticid>\n");
 	}
 
 	return true;
@@ -443,7 +442,7 @@ Script *Console::getScriptFromArg(const char *arg) {
 		script = _vm->getGame().getLocalScript();
 	}
 	if (!script) {
-		debugPrintf(_("Choose 'G' (global) or 'L' (local) script.\n"));
+		debugPrintf("Choose 'G' (global) or 'L' (local) script.\n");
 	}
 
 	return script;
diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp
index 2843ad9..27def7d 100644
--- a/engines/mutationofjb/game.cpp
+++ b/engines/mutationofjb/game.cpp
@@ -32,7 +32,6 @@
 #include "mutationofjb/util.h"
 
 #include "common/str.h"
-#include "common/translation.h"
 #include "common/util.h"
 
 namespace MutationOfJB {
@@ -232,7 +231,7 @@ uint8 Game::colorFromString(const char *colorStr) {
 		return static_cast<uint8>(atoi(colorStr + 1));
 	}
 
-	warning(_("Color not found"));
+	warning("Color not found");
 	return 0x00;
 }
 
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index d91c0fb..fc006dc 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -23,7 +23,6 @@
 #include "mutationofjb/gamedata.h"
 #include "common/stream.h"
 #include "common/util.h"
-#include "common/translation.h"
 
 namespace MutationOfJB {
 
@@ -147,7 +146,7 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 
 Door *Scene::getDoor(uint8 doorId) {
 	if (doorId == 0 || doorId > _noDoors) {
-		warning(_("Door %d does not exist"), doorId);
+		warning("Door %d does not exist", doorId);
 		return nullptr;
 	}
 
@@ -156,7 +155,7 @@ Door *Scene::getDoor(uint8 doorId) {
 
 Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
 	if (objectId == 0 || objectId > getNoObjects(ignoreNo))  {
-		warning(_("Object %d does not exist"), objectId);
+		warning("Object %d does not exist", objectId);
 		return nullptr;
 	}
 
@@ -165,7 +164,7 @@ Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
 
 Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
 	if (staticId == 0 || staticId > (!ignoreNo ? MIN(_noStatics, (uint8) ARRAYSIZE(_statics)) : ARRAYSIZE(_statics))) {
-		warning(_("Static %d does not exist"), staticId);
+		warning("Static %d does not exist", staticId);
 		return nullptr;
 	}
 
@@ -252,7 +251,7 @@ GameData::GameData()
 
 Scene *GameData::getScene(uint8 sceneId) {
 	if (sceneId == 0 || sceneId > ARRAYSIZE(_scenes)) {
-		warning(_("Scene %d does not exist"), sceneId);
+		warning("Scene %d does not exist", sceneId);
 		return nullptr;
 	}
 
diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp
index a396a64..783edae 100644
--- a/engines/mutationofjb/room.cpp
+++ b/engines/mutationofjb/room.cpp
@@ -30,7 +30,6 @@
 
 #include "common/rect.h"
 #include "common/str.h"
-#include "common/translation.h"
 
 #include "graphics/screen.h"
 
diff --git a/engines/mutationofjb/script.cpp b/engines/mutationofjb/script.cpp
index 01aef2c..7538017 100644
--- a/engines/mutationofjb/script.cpp
+++ b/engines/mutationofjb/script.cpp
@@ -26,7 +26,6 @@
 #include "common/hash-str.h"
 #include "common/stream.h"
 #include "common/debug.h"
-#include "common/translation.h"
 #include "mutationofjb/commands/command.h"
 #include "mutationofjb/commands/ifcommand.h"
 #include "mutationofjb/commands/ifitemcommand.h"
@@ -152,7 +151,7 @@ Command::ExecuteResult ScriptExecutionContext::runActiveCommand() {
 
 Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
 	if (_activeCommand) {
-		warning(_("Trying to start command while another one is running."));
+		warning("Trying to start command while another one is running.");
 		return Command::Finished;
 	}
 	getGameData()._color = WHITE; // The original game resets the color to WHITE beforing running script sections.
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 3b1dcc5..6e5d020 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -33,8 +33,6 @@
 #include "mutationofjb/util.h"
 #include "mutationofjb/widgets/conversationwidget.h"
 
-#include "common/translation.h"
-
 namespace MutationOfJB {
 
 void ConversationTask::start() {
@@ -238,7 +236,7 @@ void ConversationTask::startExtra() {
 				_innerExecCtx = nullptr;
 			}
 		} else {
-			warning(_("Extra '%s' not found"), line->_extra.c_str());
+			warning("Extra '%s' not found", line->_extra.c_str());
 			delete _innerExecCtx;
 			_innerExecCtx = nullptr;
 		}
diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp
index a6d4dc1..6d860d4 100644
--- a/engines/mutationofjb/tasks/taskmanager.cpp
+++ b/engines/mutationofjb/tasks/taskmanager.cpp
@@ -24,8 +24,6 @@
 
 #include "mutationofjb/tasks/task.h"
 
-#include "common/translation.h"
-
 namespace MutationOfJB {
 
 void TaskManager::startTask(const TaskPtr &task) {
@@ -37,7 +35,7 @@ void TaskManager::startTask(const TaskPtr &task) {
 void TaskManager::stopTask(const TaskPtr &task) {
 	TaskPtrs::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
 	if (it == _tasks.end()) {
-		warning(_("Task is not registered in TaskManager."));
+		warning("Task is not registered in TaskManager");
 		return;
 	}
 


Commit: cf878d87776f779f2a13669689361903fbe438cb
    https://github.com/scummvm/scummvm/commit/cf878d87776f779f2a13669689361903fbe438cb
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Change old-style C casts to static_cast.

Changed paths:
    engines/mutationofjb/commands/changecommand.cpp
    engines/mutationofjb/commands/talkcommand.cpp
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/gamedata.cpp
    engines/mutationofjb/tasks/conversationtask.cpp
    engines/mutationofjb/widgets/inventorywidget.cpp


diff --git a/engines/mutationofjb/commands/changecommand.cpp b/engines/mutationofjb/commands/changecommand.cpp
index 73c5357..9051e8e 100644
--- a/engines/mutationofjb/commands/changecommand.cpp
+++ b/engines/mutationofjb/commands/changecommand.cpp
@@ -317,13 +317,13 @@ Common::String ChangeCommand::getValueAsString() const {
 	case PF:
 	case PL:
 	case PD:
-		return Common::String::format("%d", (int)_value._byteVal);
+		return Common::String::format("%d", static_cast<int>(_value._byteVal));
 	case SX:
 	case SY:
 	case XX:
 	case XL:
 	case WX:
-		return Common::String::format("%d", (int)_value._wordVal);
+		return Common::String::format("%d", static_cast<int>(_value._wordVal));
 	default:
 		return "(unknown)";
 	}
diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp
index 9857f38..aa10e38 100644
--- a/engines/mutationofjb/commands/talkcommand.cpp
+++ b/engines/mutationofjb/commands/talkcommand.cpp
@@ -82,7 +82,7 @@ Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx
 
 Common::String TalkCommand::debugString() const {
 	const char *modes[] = {"NORMAL", "RAY_AND_BUTTLEG", "CARNIVAL_TICKET_SELLER"};
-	return Common::String::format("TALK %s", modes[(int) _mode]);
+	return Common::String::format("TALK %s", modes[static_cast<int>(_mode)]);
 }
 
 }
diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index b5b49cd..69804fd 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -282,7 +282,7 @@ bool Console::cmd_showstartup(int argc, const char **argv) {
 		Script *const script = getScriptFromArg(argv[1]);
 		if (script) {
 			const Startups &startups = script->getStartups();
-			Startups::const_iterator itMacro = startups.find((uint8) atoi(argv[2]));
+			Startups::const_iterator itMacro = startups.find(static_cast<uint8>(atoi(argv[2])));
 			if (itMacro != startups.end()) {
 				if (itMacro->_value) {
 					showCommands(itMacro->_value);
diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp
index fc006dc..2026c3e 100644
--- a/engines/mutationofjb/gamedata.cpp
+++ b/engines/mutationofjb/gamedata.cpp
@@ -33,7 +33,7 @@ static bool readString(Common::ReadStream &stream, char *str) {
 	uint8 len = stream.readByte();
 	stream.read(buf, MAX_ENTITY_NAME_LENGTH);
 
-	len = MIN(len, (uint8) MAX_ENTITY_NAME_LENGTH);
+	len = MIN(len, static_cast<uint8>(MAX_ENTITY_NAME_LENGTH));
 	memcpy(str, buf, len);
 
 	return true;
@@ -110,19 +110,19 @@ bool Scene::loadFromStream(Common::ReadStream &stream) {
 	_delay = stream.readByte();
 
 	_noDoors = stream.readByte();
-	_noDoors = MIN(_noDoors, (uint8) ARRAYSIZE(_doors));
+	_noDoors = MIN(_noDoors, static_cast<uint8>(ARRAYSIZE(_doors)));
 	for (i = 0; i < ARRAYSIZE(_doors); ++i) {
 		_doors[i].loadFromStream(stream);
 	}
 
 	_noObjects = stream.readByte();
-	_noObjects = MIN(_noObjects, (uint8) ARRAYSIZE(_objects));
+	_noObjects = MIN(_noObjects, static_cast<uint8>(ARRAYSIZE(_objects)));
 	for (i = 0; i < ARRAYSIZE(_objects); ++i) {
 		_objects[i].loadFromStream(stream);
 	}
 
 	_noStatics = stream.readByte();
-	_noStatics = MIN(_noStatics, (uint8) ARRAYSIZE(_statics));
+	_noStatics = MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics)));
 	for (i = 0; i < ARRAYSIZE(_statics); ++i) {
 		_statics[i].loadFromStream(stream);
 	}
@@ -163,7 +163,7 @@ Object *Scene::getObject(uint8 objectId, bool ignoreNo) {
 }
 
 Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
-	if (staticId == 0 || staticId > (!ignoreNo ? MIN(_noStatics, (uint8) ARRAYSIZE(_statics)) : ARRAYSIZE(_statics))) {
+	if (staticId == 0 || staticId > (!ignoreNo ? MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics))) : ARRAYSIZE(_statics))) {
 		warning("Static %d does not exist", staticId);
 		return nullptr;
 	}
@@ -172,15 +172,15 @@ Static *Scene::getStatic(uint8 staticId, bool ignoreNo) {
 }
 
 uint8 Scene::getNoDoors(bool ignoreNo) const {
-	return (!ignoreNo ? MIN(_noDoors, (uint8) ARRAYSIZE(_doors)) : ARRAYSIZE(_doors));
+	return (!ignoreNo ? MIN(_noDoors, static_cast<uint8>(ARRAYSIZE(_doors))) : ARRAYSIZE(_doors));
 }
 
 uint8 Scene::getNoObjects(bool ignoreNo) const {
-	return (!ignoreNo ? MIN(_noObjects, (uint8) ARRAYSIZE(_objects)) : ARRAYSIZE(_objects));
+	return (!ignoreNo ? MIN(_noObjects, static_cast<uint8>(ARRAYSIZE(_objects))) : ARRAYSIZE(_objects));
 }
 
 uint8 Scene::getNoStatics(bool ignoreNo) const {
-	return (!ignoreNo ? MIN(_noStatics, (uint8) ARRAYSIZE(_statics)) : ARRAYSIZE(_statics));
+	return (!ignoreNo ? MIN(_noStatics, static_cast<uint8>(ARRAYSIZE(_statics))) : ARRAYSIZE(_statics));
 }
 
 Door *Scene::findDoor(int16 x, int16 y, int *index) {
diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp
index 6e5d020..5b0f0f4 100644
--- a/engines/mutationofjb/tasks/conversationtask.cpp
+++ b/engines/mutationofjb/tasks/conversationtask.cpp
@@ -132,7 +132,7 @@ void ConversationTask::showChoicesOrPick() {
 	for (ConversationInfo::ItemGroup::size_type i = 0; i < currentGroup.size(); ++i) {
 		const ConversationInfo::Item &item = currentGroup[i];
 
-		if (scene->isConvItemExhausted(_convInfo._context, (uint8) i + 1, (uint8) _currentGroupIndex + 1)) {
+		if (scene->isConvItemExhausted(_convInfo._context, static_cast<uint8>(i + 1), static_cast<uint8>(_currentGroupIndex + 1))) {
 			continue;
 		}
 		const uint8 toSay = item._question;
@@ -160,7 +160,7 @@ void ConversationTask::showChoicesOrPick() {
 			const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions[i]];
 			const ConversationLineList::Line *const line = toSayList.getLine(item._question);
 			const Common::String widgetText = toUpperCP895(line->_speeches[0]._text);
-			widget.setChoice((int) i, widgetText, itemsWithValidQuestions[i]);
+			widget.setChoice(static_cast<int>(i), widgetText, itemsWithValidQuestions[i]);
 		}
 		_substate = IDLE;
 		_currentItem = nullptr;
diff --git a/engines/mutationofjb/widgets/inventorywidget.cpp b/engines/mutationofjb/widgets/inventorywidget.cpp
index a26bab6..46bfab9 100644
--- a/engines/mutationofjb/widgets/inventorywidget.cpp
+++ b/engines/mutationofjb/widgets/inventorywidget.cpp
@@ -67,7 +67,7 @@ void InventoryWidget::draw(Graphics::ManagedSurface &surface) {
 	Inventory &inventory = _gui.getGame().getGameData().getInventory();
 	const Inventory::Items &items = inventory.getItems();
 	surface.fillRect(_area, 0x00);
-	for (int i = 0; i < MIN((int) items.size(), (int) Inventory::VISIBLE_ITEMS); ++i) {
+	for (Inventory::Items::size_type i = 0; i < MIN<Inventory::Items::size_type>(items.size(), Inventory::VISIBLE_ITEMS); ++i) {
 		drawInventoryItem(surface, items[i], i);
 	}
 }


Commit: 696b61c14626495fd01ffbacc309a2f8f5db4069
    https://github.com/scummvm/scummvm/commit/696b61c14626495fd01ffbacc309a2f8f5db4069
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Move method comments to headers.

Changed paths:
    engines/mutationofjb/mutationofjb.cpp
    engines/mutationofjb/mutationofjb.h
    engines/mutationofjb/tasks/objectanimationtask.cpp
    engines/mutationofjb/tasks/objectanimationtask.h


diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 1d88269..8841a30 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -156,14 +156,6 @@ void MutationOfJBEngine::handleNormalScene(const Common::Event &event) {
 	_game->getGui().handleEvent(event);
 }
 
-/*
-	Special handling for map scenes.
-
-	Bitmaps define mouse clickable areas.
-	Statics are used to start actions.
-	Objects are used for showing labels.
-
-*/
 void MutationOfJBEngine::handleMapScene(const Common::Event &event) {
 	Scene *const scene = _game->getGameData().getCurrentScene();
 
diff --git a/engines/mutationofjb/mutationofjb.h b/engines/mutationofjb/mutationofjb.h
index 21f8094..3803254 100644
--- a/engines/mutationofjb/mutationofjb.h
+++ b/engines/mutationofjb/mutationofjb.h
@@ -61,7 +61,27 @@ private:
 	void setupCursor();
 	void updateCursorHitTest(int16 x, int16 y);
 	void updateCursorPalette();
+
+	/**
+	 * Handling for normal (non-map) scenes.
+	 *
+	 * Statics and doors define mouse clickable areas.
+	 * Statics are used to start actions.
+	 * Doors are used to transition between scenes.
+	 *
+	 * @param event ScummVM event.
+	 */
 	void handleNormalScene(const Common::Event &event);
+
+	/**
+	 * Special handling for map scenes.
+	 *
+	 * Bitmaps define mouse clickable areas.
+	 * Statics are used to start actions.
+	 * Objects are used for showing labels.
+	 *
+	 * @param event ScummVM event.
+	 */
 	void handleMapScene(const Common::Event &event);
 
 	Console *_console;
diff --git a/engines/mutationofjb/tasks/objectanimationtask.cpp b/engines/mutationofjb/tasks/objectanimationtask.cpp
index 2b4bf80..eab3d75 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.cpp
+++ b/engines/mutationofjb/tasks/objectanimationtask.cpp
@@ -48,17 +48,6 @@ void ObjectAnimationTask::update() {
 	}
 }
 
-/**
- * Advances every object animation in the current scene to the next frame.
- *
- * Normally the animation restarts after the last object frame. However, some animations have random
- * elements to them. If _randomFrame is set, the animation restarts when _randomFrame is reached.
- * Additionally, there is a chance with each frame until _randomFrame that the animation may jump
- * straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
- *
- * Randomness is used to introduce variety - e.g. in the starting scene a perched bird occasionally
- * spreads its wings.
- */
 void ObjectAnimationTask::updateObjects() {
 	Scene *const scene = getTaskManager()->getGame().getGameData().getCurrentScene();
 	if (!scene) {
@@ -102,14 +91,6 @@ void ObjectAnimationTask::updateObjects() {
 	}
 }
 
-/**
- * Nasty, hacky stuff the original game does to make some complex animations
- * in the Carnival and Tavern Earthquake scenes possible.
- *
- * @param object Object to process.
- * @return Whether to draw the object. It's important to respect this, otherwise
- * some of the hardcoded animations would suffer from graphical glitches.
- */
 bool ObjectAnimationTask::handleHardcodedAnimation(Object *const object) {
 	GameData &gameData = getTaskManager()->getGame().getGameData();
 	Scene *const scene = gameData.getCurrentScene();
diff --git a/engines/mutationofjb/tasks/objectanimationtask.h b/engines/mutationofjb/tasks/objectanimationtask.h
index 320868f..9e09139 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.h
+++ b/engines/mutationofjb/tasks/objectanimationtask.h
@@ -38,7 +38,27 @@ public:
 	virtual void start() override;
 	virtual void update() override;
 
+	/**
+	 * Advances every object animation in the current scene to the next frame.
+	 *
+	 * Normally the animation restarts after the last object frame. However, some animations have random
+	 * elements to them. If _randomFrame is set, the animation restarts when _randomFrame is reached.
+	 * Additionally, there is a chance with each frame until _randomFrame that the animation may jump
+	 * straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
+	 *
+	 * Randomness is used to introduce variety - e.g. in the starting scene a perched bird occasionally
+	 * spreads its wings.
+	 */
 	void updateObjects();
+
+	/**
+	 * Nasty, hacky stuff the original game does to make some complex animations
+	 * in the Carnival and Tavern Earthquake scenes possible.
+	 *
+	 * @param object Object to process.
+	 * @return Whether to draw the object. It's important to respect this, otherwise
+	 * some of the hardcoded animations would suffer from graphical glitches.
+	 */
 	bool handleHardcodedAnimation(Object *const object);
 
 private:


Commit: 0e90d6eae39363687a447e39d3ba0a4994f1800f
    https://github.com/scummvm/scummvm/commit/0e90d6eae39363687a447e39d3ba0a4994f1800f
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Use advanced detector.

Changed paths:
    common/language.cpp
    common/language.h
    engines/mutationofjb/detection.cpp
    engines/mutationofjb/mutationofjb.cpp


diff --git a/common/language.cpp b/common/language.cpp
index 2112182..0d4fbee 100644
--- a/common/language.cpp
+++ b/common/language.cpp
@@ -51,6 +51,7 @@ const LanguageDescription g_languages[] = {
 	{    "pl", "pl_PL", "Polish", PL_POL },
 	{    "br", "pt_BR", "Portuguese", PT_BRA },
 	{    "ru", "ru_RU", "Russian", RU_RUS },
+	{    "sk", "sk_SK", "Slovak", SK_SVK },
 	{    "es", "es_ES", "Spanish", ES_ESP },
 	{    "se", "sv_SE", "Swedish", SE_SWE },
 	{    "uk", "uk_UA", "Ukrainian", UA_UKR },
diff --git a/common/language.h b/common/language.h
index 752914b..3d4ba3a 100644
--- a/common/language.h
+++ b/common/language.h
@@ -56,6 +56,7 @@ enum Language {
 	PL_POL,
 	PT_BRA,
 	RU_RUS,
+	SK_SVK,
 	ES_ESP,
 	SE_SWE,
 	UA_UKR,
diff --git a/engines/mutationofjb/detection.cpp b/engines/mutationofjb/detection.cpp
index 5e80760..33664c1 100644
--- a/engines/mutationofjb/detection.cpp
+++ b/engines/mutationofjb/detection.cpp
@@ -24,15 +24,71 @@
 
 #include "common/config-manager.h"
 
-#include "engines/metaengine.h"
+#include "engines/advancedDetector.h"
 
-static const PlainGameDescriptor mutationofjb_setting[] = {
+static const PlainGameDescriptor mutationofjbGames[] = {
 	{"mutationofjb", "Mutation of J.B."},
-	{0, 0}
+	{nullptr, nullptr}
 };
 
-class MutationOfJBMetaEngine : public MetaEngine {
+static const ADGameDescription mutationofjbDescriptions[] = {
+	{
+		"mutationofjb",
+		"",
+		{
+			{"jb.ex_", 0, "934164b09c72fa7167811f448ee0a426", 150048},
+			{"startup.dat", 0, nullptr, -1},
+			{"startupb.dat", 0, nullptr, -1},
+			{"global.atn", 0, nullptr, -1},
+			{"piggy.apk", 0, nullptr, -1},
+			{"foogl.apk", 0, nullptr, -1},
+			{"tosay.ger", 0, nullptr, -1},
+			{"response.ger", 0, nullptr, -1},
+			{"font1.aft", 0, nullptr, -1},
+			{"sysfnt.aft", 0, nullptr, -1},
+			{nullptr, 0, nullptr, 0}
+		},
+		Common::SK_SVK,
+		Common::kPlatformDOS,
+		ADGF_CD,
+		GUIO0()
+	},
+	{
+		"mutationofjb",
+		"",
+		{
+			{"jb.ex_", 0, "8833f22f1763d05eeb909e8626cdec7b", 150800},
+			{"startup.dat", 0, nullptr, -1},
+			{"startupb.dat", 0, nullptr, -1},
+			{"global.atn", 0, nullptr, -1},
+			{"piggy.apk", 0, nullptr, -1},
+			{"foogl.apk", 0, nullptr, -1},
+			{"tosay.ger", 0, nullptr, -1},
+			{"response.ger", 0, nullptr, -1},
+			{"font1.aft", 0, nullptr, -1},
+			{"sysfnt.aft", 0, nullptr, -1},
+			{nullptr, 0, nullptr, 0}
+		},
+		Common::DE_DEU,
+		Common::kPlatformDOS,
+		ADGF_CD,
+		GUIO0()
+	},
+	AD_TABLE_END_MARKER
+};
+
+static const char *const mutationofjbDirectoryGlobs[] = {
+	"data",
+	nullptr
+};
+
+class MutationOfJBMetaEngine : public AdvancedMetaEngine {
 public:
+	MutationOfJBMetaEngine() : AdvancedMetaEngine(mutationofjbDescriptions, sizeof(ADGameDescription), mutationofjbGames) {
+		_maxScanDepth = 2;
+		_directoryGlobs = mutationofjbDirectoryGlobs;
+	}
+
 	virtual const char *getName() const {
 		return "Mutation of J.B.";
 	}
@@ -41,67 +97,11 @@ public:
 		return "Mutation of J.B. (C) 1996 RIKI Computer Games";
 	}
 
-	virtual PlainGameList getSupportedGames() const {
-		PlainGameList games;
-		const PlainGameDescriptor *g = mutationofjb_setting;
-		while (g->gameId) {
-			games.push_back(*g);
-			g++;
+	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+		if (desc) {
+			*engine = new MutationOfJB::MutationOfJBEngine(syst);
 		}
-
-		return games;
-	}
-
-	virtual PlainGameDescriptor findGame(const char *gameid) const {
-		const PlainGameDescriptor *g = mutationofjb_setting;
-		while (g->gameId) {
-			if (0 == scumm_stricmp(gameid, g->gameId))
-				return *g;
-			g++;
-		}
-		return PlainGameDescriptor::empty();
-	}
-
-	virtual DetectedGames detectGames(const Common::FSList &fslist) const {
-		DetectedGames detectedGames;
-
-		for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
-			if (!file->isDirectory()) {
-				const char *gameName = file->getName().c_str();
-
-				if (0 == scumm_stricmp("startup.dat", gameName)) {
-					detectedGames.push_back(DetectedGame(mutationofjb_setting[0]));
-					break;
-				}
-			}
-		}
-		return detectedGames;
-	}
-
-	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const {
-		assert(syst);
-		assert(engine);
-
-		Common::FSList fslist;
-		Common::FSNode dir(ConfMan.get("path"));
-		if (!dir.getChildren(fslist, Common::FSNode::kListAll)) {
-			return Common::kNoGameDataFoundError;
-		}
-
-		// Invoke the detector
-		Common::String gameid = ConfMan.get("gameid");
-		DetectedGames detectedGames = detectGames(fslist);
-
-		for (uint i = 0; i < detectedGames.size(); i++) {
-			if (detectedGames[i].gameId == gameid) {
-				// At this point you may want to perform additional sanity checks.
-				*engine = new MutationOfJB::MutationOfJBEngine(syst);
-				return Common::kNoError;
-			}
-		}
-
-		// Failed to find any game data
-		return Common::kNoGameDataFoundError;
+		return desc != nullptr;
 	}
 };
 
diff --git a/engines/mutationofjb/mutationofjb.cpp b/engines/mutationofjb/mutationofjb.cpp
index 8841a30..376c024 100644
--- a/engines/mutationofjb/mutationofjb.cpp
+++ b/engines/mutationofjb/mutationofjb.cpp
@@ -22,10 +22,12 @@
 
 #include "common/scummsys.h"
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/error.h"
 #include "common/system.h"
 #include "common/events.h"
+#include "common/fs.h"
 #include "graphics/screen.h"
 #include "graphics/cursorman.h"
 
@@ -45,13 +47,13 @@ MutationOfJBEngine::MutationOfJBEngine(OSystem *syst)
 	  _screen(nullptr),
 	  _mapObjectId(0),
 	  _cursorState(CURSOR_IDLE) {
-	debug("MutationOfJBEngine::MutationOfJBEngine");
-}
 
-MutationOfJBEngine::~MutationOfJBEngine() {
-	debug("MutationOfJBEngine::~MutationOfJBEngine");
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "data");
 }
 
+MutationOfJBEngine::~MutationOfJBEngine() {}
+
 void MutationOfJBEngine::setupCursor() {
 	const uint8 cursor[] = {
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -237,8 +239,6 @@ void MutationOfJBEngine::updateCursorHitTest(int16 x, int16 y) {
 }
 
 Common::Error MutationOfJBEngine::run() {
-	debug("MutationOfJBEngine::run");
-
 	initGraphics(320, 200);
 
 	_console = new Console(this);


Commit: b00395b0b976b869cb83c1181481f1ad7ba16b1e
    https://github.com/scummvm/scummvm/commit/b00395b0b976b869cb83c1181481f1ad7ba16b1e
Author: Ľubomír Remák (lubomirr at lubomirr.eu)
Date: 2018-08-25T23:12:01+02:00

Commit Message:
MUTATIONOFJB: Fix MSVC warnings.

Changed paths:
    engines/mutationofjb/debug.cpp
    engines/mutationofjb/tasks/objectanimationtask.h


diff --git a/engines/mutationofjb/debug.cpp b/engines/mutationofjb/debug.cpp
index 69804fd..2a06cce 100644
--- a/engines/mutationofjb/debug.cpp
+++ b/engines/mutationofjb/debug.cpp
@@ -93,7 +93,7 @@ bool Console::cmd_listsections(int argc, const char **argv) {
 	if (argc == 3) {
 		Script *const script = getScriptFromArg(argv[1]);
 		if (script) {
-			ActionInfo::Action action;
+			ActionInfo::Action action = ActionInfo::Look;
 			const char *word = nullptr;
 			if (strcmp(argv[2], "L") == 0) {
 				action = ActionInfo::Look;
@@ -171,7 +171,7 @@ bool Console::cmd_showsection(int argc, const char **argv) {
 		Script *const script = getScriptFromArg(argv[1]);
 		if (script) {
 			Command *command = nullptr;
-			ActionInfo::Action action;
+			ActionInfo::Action action = ActionInfo::Look;
 			bool correctAction = true;
 			bool found = false;
 
diff --git a/engines/mutationofjb/tasks/objectanimationtask.h b/engines/mutationofjb/tasks/objectanimationtask.h
index 9e09139..45e83b3 100644
--- a/engines/mutationofjb/tasks/objectanimationtask.h
+++ b/engines/mutationofjb/tasks/objectanimationtask.h
@@ -29,7 +29,7 @@
 
 namespace MutationOfJB {
 
-class Object;
+struct Object;
 
 class ObjectAnimationTask : public Task {
 public:





More information about the Scummvm-git-logs mailing list