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

dreammaster paulfgilbert at gmail.com
Mon Apr 13 01:54:42 UTC 2020


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

Summary:
6f4f4e828f ULTIMA4: Hooking up movement as keybinding actions
6b637be1bd ULTIMA4: Making attack & board keybinder actions
e5894a132e ULTIMA4: Better handling for keybind actions ending turns
dba370697f ULTIMA4: Changed enter to a keybinder action
b8c3775644 ULTIMA4: Shifting more actions to keybindings


Commit: 6f4f4e828fc5196070e6adb18fe7b21e422d5ce7
    https://github.com/scummvm/scummvm/commit/6f4f4e828fc5196070e6adb18fe7b21e422d5ce7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2020-04-12T18:54:30-07:00

Commit Message:
ULTIMA4: Hooking up movement as keybinding actions

Changed paths:
    engines/ultima/shared/engine/debugger.cpp
    engines/ultima/shared/engine/debugger.h
    engines/ultima/ultima4/core/debugger.cpp
    engines/ultima/ultima4/core/debugger.h
    engines/ultima/ultima4/events/event_scummvm.cpp
    engines/ultima/ultima4/game/game.cpp
    engines/ultima/ultima4/meta_engine.cpp
    engines/ultima/ultima4/meta_engine.h


diff --git a/engines/ultima/shared/engine/debugger.cpp b/engines/ultima/shared/engine/debugger.cpp
index 1c80fc4b5f..a81ba8d319 100644
--- a/engines/ultima/shared/engine/debugger.cpp
+++ b/engines/ultima/shared/engine/debugger.cpp
@@ -47,8 +47,80 @@ int Debugger::strToInt(const char *s) {
 	return (int)tmp;
 }
 
+void Debugger::splitString(const Common::String &str,
+		Common::StringArray &argv) {
+	// Clear the vector
+	argv.clear();
+
+	bool quoted = false;
+	Common::String::const_iterator it;
+	int ch;
+	Common::String arg;
+
+	for (it = str.begin(); it != str.end(); ++it) {
+		ch = *it;
+
+		// Toggle quoted string handling
+		if (ch == '\"') {
+			quoted = !quoted;
+			continue;
+		}
+
+		// Handle \\, \", \', \n, \r, \t
+		if (ch == '\\') {
+			Common::String::const_iterator next = it + 1;
+			if (next != str.end()) {
+				if (*next == '\\' || *next == '\"' || *next == '\'') {
+					ch = *next;
+					++it;
+				} else if (*next == 'n') {
+					ch = '\n';
+					++it;
+				} else if (*next == 'r') {
+					ch = '\r';
+					++it;
+				} else if (*next == 't') {
+					ch = '\t';
+					++it;
+				} else if (*next == ' ') {
+					ch = ' ';
+					++it;
+				}
+			}
+		}
+
+		// A space, a tab, line feed, carriage return
+		if (!quoted && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
+			// If we are not empty then we are at the end of the arg
+			// otherwise we will ignore the extra chars
+			if (!arg.empty()) {
+				argv.push_back(arg);
+				arg.clear();
+			}
+
+			continue;
+		}
+
+		// Add the charater to the string
+		arg += ch;
+	}
+
+	// Push any arg if it's left
+	if (!arg.empty())
+		argv.push_back(arg);
+}
+
 void Debugger::executeCommand(const Common::String &cmd) {
+	// Split up the command, and form a const char * array
+	Common::StringArray args;
+	splitString(cmd, args);
+
+	Common::Array<const char *> argv;
+	for (uint idx = 0; idx < args.size(); ++idx)
+		argv.push_back(args[idx].c_str());
 
+	// Execute the command
+	executeCommand(argv.size(), &argv[0]);
 }
 
 void Debugger::executeCommand(int argc, const char **argv) {
diff --git a/engines/ultima/shared/engine/debugger.h b/engines/ultima/shared/engine/debugger.h
index a93872f29b..12767e387a 100644
--- a/engines/ultima/shared/engine/debugger.h
+++ b/engines/ultima/shared/engine/debugger.h
@@ -40,6 +40,11 @@ protected:
 	 * Converts a string to an integer
 	 */
 	int strToInt(const char *s);
+
+	/**
+	 * Split up a command string into arg values
+	 */
+	void splitString(const Common::String &str, Common::StringArray &argv);
 public:
 	Debugger();
     ~Debugger() override {}
diff --git a/engines/ultima/ultima4/core/debugger.cpp b/engines/ultima/ultima4/core/debugger.cpp
index 84348a2348..a6dfc30900 100644
--- a/engines/ultima/ultima4/core/debugger.cpp
+++ b/engines/ultima/ultima4/core/debugger.cpp
@@ -42,6 +42,8 @@ Debugger::Debugger() : Shared::Debugger() {
 	g_debugger = this;
 	_collisionOverride = false;
 
+	registerCmd("move", WRAP_METHOD(Debugger, cmdMove));
+
 	registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
 	registerCmd("collisions", WRAP_METHOD(Debugger, cmdCollisions));
 	registerCmd("companions", WRAP_METHOD(Debugger, cmdCompanions));
@@ -156,6 +158,37 @@ bool Debugger::destroyAt(const Coords &coords) {
 }
 
 
+bool Debugger::cmdMove(int argc, const char **argv) {
+	Direction dir;
+
+	if (argc == 2) {
+		dir = directionFromName(argv[1]);
+	} else {
+		print("move <direction>");
+		return isActive();
+	}
+
+	Common::String priorMap = g_context->_location->_map->_fname;
+	MoveResult retval = g_context->_location->move(dir, true);
+
+	// horse doubles speed (make sure we're on the same map as the previous move first)
+	if (retval & (MOVE_SUCCEEDED | MOVE_SLOWED) &&
+		(g_context->_transportContext == TRANSPORT_HORSE) && g_context->_horseSpeed) {
+		// to give it a smooth look of movement
+		gameUpdateScreen();
+		if (priorMap == g_context->_location->_map->_fname)
+			g_context->_location->move(dir, false);
+	}
+
+	// Let the movement handler decide to end the turn
+	bool endTurn = (retval & MOVE_END_TURN);
+	if (endTurn)
+		g_game->finishTurn();
+
+	return false;
+}
+
+
 bool Debugger::cmd3d(int argc, const char **argv) {
 	if (g_context->_location->_context == CTX_DUNGEON) {
 		print("3-D view %s\n", DungeonViewer.toggle3DDungeonView() ? "on" : "off");
diff --git a/engines/ultima/ultima4/core/debugger.h b/engines/ultima/ultima4/core/debugger.h
index d46cf010a2..ebf1704dd4 100644
--- a/engines/ultima/ultima4/core/debugger.h
+++ b/engines/ultima/ultima4/core/debugger.h
@@ -59,6 +59,12 @@ private:
 	 * Returns a direction from a given string
 	 */
 	Direction directionFromName(const Common::String &dirStr);
+private:
+	/**
+	 * Move the avatar in a given direction
+	 */
+	bool cmdMove(int argc, const char **argv);
+
 private:
 	/**
 	 * Collision detection on/off
diff --git a/engines/ultima/ultima4/events/event_scummvm.cpp b/engines/ultima/ultima4/events/event_scummvm.cpp
index 26f55e4b45..6ff8260715 100644
--- a/engines/ultima/ultima4/events/event_scummvm.cpp
+++ b/engines/ultima/ultima4/events/event_scummvm.cpp
@@ -27,6 +27,7 @@
 #include "ultima/ultima4/core/utils.h"
 #include "ultima/ultima4/gfx/screen.h"
 #include "ultima/ultima4/core/settings.h"
+#include "ultima/ultima4/meta_engine.h"
 #include "common/debug.h"
 #include "common/system.h"
 
@@ -243,6 +244,10 @@ void EventHandler::run() {
 			handleMouseMotionEvent(event);
 			continue;
 
+		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+			MetaEngine::executeAction((KeybindingAction)event.customType);
+			break;
+
 		case Common::EVENT_QUIT:
 			_ended = true;
 			return;
diff --git a/engines/ultima/ultima4/game/game.cpp b/engines/ultima/ultima4/game/game.cpp
index 405a933bf5..9e7113894a 100644
--- a/engines/ultima/ultima4/game/game.cpp
+++ b/engines/ultima/ultima4/game/game.cpp
@@ -628,27 +628,6 @@ bool GameController::keyPressed(int key) {
 		screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
 	else
 		switch (key) {
-
-		case U4_UP:
-		case U4_DOWN:
-		case U4_LEFT:
-		case U4_RIGHT: {
-			/* move the avatar */
-			Common::String previous_map = g_context->_location->_map->_fname;
-			MoveResult retval = g_context->_location->move(keyToDirection(key), true);
-
-			/* horse doubles speed (make sure we're on the same map as the previous move first) */
-			if (retval & (MOVE_SUCCEEDED | MOVE_SLOWED) &&
-			        (g_context->_transportContext == TRANSPORT_HORSE) && g_context->_horseSpeed) {
-				gameUpdateScreen(); /* to give it a smooth look of movement */
-				if (previous_map == g_context->_location->_map->_fname)
-					g_context->_location->move(keyToDirection(key), false);
-			}
-
-			endTurn = (retval & MOVE_END_TURN); /* let the movement handler decide to end the turn */
-		}
-		break;
-
 		case ' ':
 			screenMessage("Pass\n");
 			break;
diff --git a/engines/ultima/ultima4/meta_engine.cpp b/engines/ultima/ultima4/meta_engine.cpp
index 0e8168db24..ac25c80069 100644
--- a/engines/ultima/ultima4/meta_engine.cpp
+++ b/engines/ultima/ultima4/meta_engine.cpp
@@ -39,14 +39,18 @@ struct KeybindingRecord {
 };
 
 static const KeybindingRecord KEYS[] = {
-	{ ACTION_NORTH, "NORTH", "North", "walk north", "up", nullptr },
-	{ ACTION_SOUTH, "SOUTH", "South", "walk south", "down", nullptr },
-	{ ACTION_EAST, "EAST", "East", "walk east", "right", nullptr },
-	{ ACTION_WEST, "WEST", "West", "walk west", "left", nullptr },
+	{ ACTION_NORTH, "UP", "Up", "move up", "UP", nullptr },
+	{ ACTION_SOUTH, "DOWN", "Down", "move down", "DOWN", nullptr },
+	{ ACTION_WEST, "LEFT", "Left", "move left", "LEFT", nullptr },
+	{ ACTION_EAST, "RIGHT", "Right", "move right", "RIGHT", nullptr },
 
 	{ ACTION_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
 };
 
+static const KeybindingRecord CHEAT_KEYS[] = {
+	{ ACTION_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
+};
+
 
 Common::KeymapArray MetaEngine::initKeymaps() {
 	Common::KeymapArray keymapArray;
@@ -86,16 +90,20 @@ void MetaEngine::setKeybindingsActive(bool isActive) {
 }
 
 
-void MetaEngine::pressAction(KeybindingAction keyAction) {
+void MetaEngine::executeAction(KeybindingAction keyAction) {
 	Common::String methodName = getMethod(keyAction);
 	if (!methodName.empty())
 		g_debugger->executeCommand(methodName);
 }
 
 Common::String MetaEngine::getMethod(KeybindingAction keyAction) {
-	for (const KeybindingRecord *r = KEYS; r->_id; ++r) {
-		if (r->_action == keyAction)
-			return r->_method;
+	const KeybindingRecord *KEY_ARRAYS[] = { KEYS, CHEAT_KEYS, nullptr };
+
+	for (const KeybindingRecord **arr = KEY_ARRAYS; *arr; ++arr) {
+		for (const KeybindingRecord *r = *arr; r->_id; ++r) {
+			if (r->_action == keyAction)
+				return r->_method;
+		}
 	}
 
 	return Common::String();
diff --git a/engines/ultima/ultima4/meta_engine.h b/engines/ultima/ultima4/meta_engine.h
index 446f1ca016..659d6fbb8c 100644
--- a/engines/ultima/ultima4/meta_engine.h
+++ b/engines/ultima/ultima4/meta_engine.h
@@ -47,9 +47,9 @@ public:
 	static Common::KeymapArray initKeymaps();
 
 	/**
-	 * Execute an engine keymap press action
+	 * Execute an engine keymap action
 	 */
-	static void pressAction(KeybindingAction keyAction);
+	static void executeAction(KeybindingAction keyAction);
 
 	/**
 	 * Enables/disables the keymaps when not waiting for an in-game action


Commit: 6b637be1bd6a62747c9a0d563135284a8ad192e7
    https://github.com/scummvm/scummvm/commit/6b637be1bd6a62747c9a0d563135284a8ad192e7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2020-04-12T18:54:30-07:00

Commit Message:
ULTIMA4: Making attack & board keybinder actions

Changed paths:
  A engines/ultima/ultima4/core/debugger_actions.cpp
  A engines/ultima/ultima4/core/debugger_actions.h
    engines/ultima/module.mk
    engines/ultima/ultima4/core/debugger.cpp
    engines/ultima/ultima4/core/debugger.h
    engines/ultima/ultima4/events/controller.h
    engines/ultima/ultima4/events/event.cpp
    engines/ultima/ultima4/events/event.h
    engines/ultima/ultima4/events/event_scummvm.cpp
    engines/ultima/ultima4/game/game.cpp
    engines/ultima/ultima4/game/game.h
    engines/ultima/ultima4/meta_engine.cpp
    engines/ultima/ultima4/meta_engine.h


diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 38f2d8ca4e..a3834e9d8b 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -144,6 +144,7 @@ MODULE_OBJS := \
 	ultima4/core/lzw/u4decode.o \
 	ultima4/core/config.o \
 	ultima4/core/debugger.o \
+	ultima4/core/debugger_actions.o \
 	ultima4/core/error.o \
 	ultima4/core/settings.o \
 	ultima4/core/utils.o \
diff --git a/engines/ultima/ultima4/core/debugger.cpp b/engines/ultima/ultima4/core/debugger.cpp
index a6dfc30900..f38eb896c1 100644
--- a/engines/ultima/ultima4/core/debugger.cpp
+++ b/engines/ultima/ultima4/core/debugger.cpp
@@ -43,6 +43,10 @@ Debugger::Debugger() : Shared::Debugger() {
 	_collisionOverride = false;
 
 	registerCmd("move", WRAP_METHOD(Debugger, cmdMove));
+	registerCmd("attack", WRAP_METHOD(Debugger, cmdAttack));
+	registerCmd("board", WRAP_METHOD(Debugger, cmdBoard));
+	registerCmd("cast", WRAP_METHOD(Debugger, cmdCastSpell));
+	registerCmd("pass", WRAP_METHOD(Debugger, cmdPass));
 
 	registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
 	registerCmd("collisions", WRAP_METHOD(Debugger, cmdCollisions));
@@ -83,78 +87,29 @@ void Debugger::print(const char *fmt, ...) {
 	Common::String str = Common::String::vformat(fmt, va);
 	va_end(va);
 
-	if (isActive()) {
-		debugPrintf("%s\n", str.c_str());
-	} else {
-		screenMessage("%s\n", str.c_str());
-	}
-}
-
-void Debugger::summonCreature(const Common::String &name) {
-	const Creature *m = NULL;
-	Common::String creatureName = name;
-
-	creatureName.trim();
-	if (creatureName.empty()) {
-		print("\n");
-		return;
-	}
-
-	/* find the creature by its id and spawn it */
-	unsigned int id = atoi(creatureName.c_str());
-	if (id > 0)
-		m = creatureMgr->getById(id);
-
-	if (!m)
-		m = creatureMgr->getByName(creatureName);
-
-	if (m) {
-		if (gameSpawnCreature(m))
-			print("\n%s summoned!\n", m->getName().c_str());
-		else
-			print("\n\nNo place to put %s!\n\n", m->getName().c_str());
-
-		return;
-	}
-
-	print("\n%s not found\n", creatureName.c_str());
-}
-
-Direction Debugger::directionFromName(const Common::String &dirStr) {
-	Common::String dir = dirStr;
-	dir.toLowercase();
-
-	if (dir == "up" || dir == "north")
-		return DIR_NORTH;
-	else if (dir == "down" || dir == "south")
-		return DIR_SOUTH;
-	else if (dir == "right" || dir == "east")
-		return DIR_EAST;
-	else if (dir == "left" || dir == "west")
-		return DIR_WEST;
-
-	return DIR_NONE;
+	printN("%s\n", str.c_str());
 }
 
-bool Debugger::destroyAt(const Coords &coords) {
-	Object *obj = g_context->_location->_map->objectAt(coords);
+void Debugger::printN(const char *fmt, ...) {
+	// Format the string
+	va_list va;
+	va_start(va, fmt);
+	Common::String str = Common::String::vformat(fmt, va);
+	va_end(va);
 
-	if (obj) {
-		if (isCreature(obj)) {
-			Creature *c = dynamic_cast<Creature *>(obj);
-			screenMessage("%s Destroyed!\n", c->getName().c_str());
-		} else {
-			Tile *t = g_context->_location->_map->_tileset->get(obj->getTile()._id);
-			screenMessage("%s Destroyed!\n", t->getName().c_str());
+	if (isDebuggerActive()) {
+		// Strip off any color special characters that aren't
+		// relevant for showing the text in the debugger
+		Common::String s;
+		for (Common::String::iterator it = str.begin(); it != str.end(); ++it) {
+			if (*it <= ' ' && *it != '\n')
+				s += *it;
 		}
 
-		g_context->_location->_map->removeObject(obj);
-		screenPrompt();
-
-		return true;
+		debugPrintf("%s", s.c_str());
+	} else {
+		screenMessage("%s", str.c_str());
 	}
-
-	return false;
 }
 
 
@@ -165,7 +120,7 @@ bool Debugger::cmdMove(int argc, const char **argv) {
 		dir = directionFromName(argv[1]);
 	} else {
 		print("move <direction>");
-		return isActive();
+		return isDebuggerActive();
 	}
 
 	Common::String priorMap = g_context->_location->_map->_fname;
@@ -188,6 +143,87 @@ bool Debugger::cmdMove(int argc, const char **argv) {
 	return false;
 }
 
+bool Debugger::cmdAttack(int argc, const char **argv) {
+	Direction dir;
+
+	if (argc != 2 && isDebuggerActive()) {
+		print("attack <direction>");
+		return true;
+	}
+
+	printN("Attack: ");
+	if (g_context->_party->isFlying()) {
+		screenMessage("\n%cDrift only!%c\n", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	if (argc == 2) {
+		dir = directionFromName(argv[1]);
+	} else {
+		dir = gameGetDirection();
+	}
+
+	if (dir == DIR_NONE) {
+		if (isDebuggerActive())
+			print("");
+		return isDebuggerActive();
+	}
+
+	Std::vector<Coords> path = gameGetDirectionalActionPath(
+		MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
+		1, 1, NULL, true);
+	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
+		if (attackAt(*i))
+			return isDebuggerActive();
+	}
+
+	print("%cNothing to Attack!%c", FG_GREY, FG_WHITE);
+	g_game->finishTurn();
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdBoard(int argc, const char **argv) {
+	if (g_context->_transportContext != TRANSPORT_FOOT) {
+		print("Board: %cCan't!%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	Object *obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
+	if (!obj) {
+		print("%cBoard What?%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	const Tile *tile = obj->getTile().getTileType();
+	if (tile->isShip()) {
+		print("Board Frigate!");
+		if (g_context->_lastShip != obj)
+			g_context->_party->setShipHull(50);
+	} else if (tile->isHorse())
+		print("Mount Horse!");
+	else if (tile->isBalloon())
+		print("Board Balloon!");
+	else {
+		print("%cBoard What?%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	g_context->_party->setTransport(obj->getTile());
+	g_context->_location->_map->removeObject(obj);
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdCastSpell(int argc, const char **argv) {
+	// TODO
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdPass(int argc, const char **argv) {
+	print("Pass");
+	g_game->finishTurn();
+	return isDebuggerActive();
+}
+
 
 bool Debugger::cmd3d(int argc, const char **argv) {
 	if (g_context->_location->_context == CTX_DUNGEON) {
@@ -196,7 +232,7 @@ bool Debugger::cmd3d(int argc, const char **argv) {
 		print("Not here");
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdCollisions(int argc, const char **argv) {
@@ -204,7 +240,7 @@ bool Debugger::cmdCollisions(int argc, const char **argv) {
 	print("Collision detection %s",
 		_collisionOverride ? "off" : "on");
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdCompanions(int argc, const char **argv) {
@@ -218,7 +254,7 @@ bool Debugger::cmdCompanions(int argc, const char **argv) {
 
 	g_context->_stats->update();
 	print("Joined by companions");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdDestroy(int argc, const char **argv) {
@@ -226,16 +262,16 @@ bool Debugger::cmdDestroy(int argc, const char **argv) {
 
 	if (argc == 2) {
 		dir = directionFromName(argv[1]);
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		print("destroy <direction>");
-		return isActive();
+		return isDebuggerActive();
 	} else {
 		screenMessage("Destroy Object\nDir: ");
 		dir = gameGetDirection();
 	}
 
 	if (dir == DIR_NONE)
-		return isActive();
+		return isDebuggerActive();
 
 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir),
 		MASK_DIR_ALL, g_context->_location->_coords, 1, 1, NULL, true);
@@ -247,7 +283,7 @@ bool Debugger::cmdDestroy(int argc, const char **argv) {
 	}
 
 	print("%cNothing there!%c\n", FG_GREY, FG_WHITE);
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdDungeon(int argc, const char **argv) {
@@ -272,7 +308,7 @@ bool Debugger::cmdDungeon(int argc, const char **argv) {
 				g_ultima->_saveGame->_orientation = DIR_SOUTH;
 			} else {
 				print("Invalid dungeon");
-				return isActive();
+				return isDebuggerActive();
 			}
 
 			g_game->finishTurn();
@@ -284,7 +320,7 @@ bool Debugger::cmdDungeon(int argc, const char **argv) {
 		print("Not here");
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdEquipment(int argc, const char **argv) {
@@ -302,7 +338,7 @@ bool Debugger::cmdEquipment(int argc, const char **argv) {
 	}
 
 	print("All equipment given");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdExit(int argc, const char **argv) {
@@ -314,7 +350,7 @@ bool Debugger::cmdExit(int argc, const char **argv) {
 		print("Exited");
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdGate(int argc, const char **argv) {
@@ -323,7 +359,7 @@ bool Debugger::cmdGate(int argc, const char **argv) {
 	if (!g_context || !g_game || gateNum < 1 || gateNum > 8) {
 		print("Gate <1 to 8>");
 	} else {
-		if (!isActive())
+		if (!isDebuggerActive())
 			print("Gate %d!", gateNum);
 
 		if (g_context->_location->_map->isWorldMap()) {
@@ -338,7 +374,7 @@ bool Debugger::cmdGate(int argc, const char **argv) {
 		}
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdGoto(int argc, const char **argv) {
@@ -346,7 +382,7 @@ bool Debugger::cmdGoto(int argc, const char **argv) {
 
 	if (argc == 2) {
 		dest = argv[1];
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		print("teleport <destination name>");
 		return true;
 	} else {
@@ -384,17 +420,17 @@ bool Debugger::cmdGoto(int argc, const char **argv) {
 		g_game->finishTurn();
 		return false;
 	} else {
-		if (isActive())
+		if (isDebuggerActive())
 			print("Can't find %s", dest.c_str());
 		else
 			print("Can't find\n%s", dest.c_str());
 
-		return isActive();
+		return isDebuggerActive();
 	}
 }
 
 bool Debugger::cmdHelp(int argc, const char **argv) {
-	if (!isActive()) {
+	if (!isDebuggerActive()) {
 		screenMessage("Help!\n");
 		screenPrompt();
 	}
@@ -423,7 +459,7 @@ bool Debugger::cmdItems(int argc, const char **argv) {
 
 	g_context->_stats->update();
 	print("All items given");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdKarma(int argc, const char **argv) {
@@ -442,13 +478,13 @@ bool Debugger::cmdKarma(int argc, const char **argv) {
 		print("%s", line.c_str());
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdLocation(int argc, const char **argv) {
 	const MapCoords &pos = g_context->_location->_coords;
 
-	if (isActive()) {
+	if (isDebuggerActive()) {
 		if (g_context->_location->_map->isWorldMap())
 			print("Location: %s x: %d, y: %d",
 				"World Map", pos.x, pos.y);
@@ -464,7 +500,7 @@ bool Debugger::cmdLocation(int argc, const char **argv) {
 				g_context->_location->_map->getName().c_str(), pos.x, pos.y, pos.z);
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdMixtures(int argc, const char **argv) {
@@ -472,7 +508,7 @@ bool Debugger::cmdMixtures(int argc, const char **argv) {
 		g_ultima->_saveGame->_mixtures[i] = 99;
 
 	screenMessage("All mixtures given");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdMoon(int argc, const char **argv) {
@@ -493,13 +529,13 @@ bool Debugger::cmdMoon(int argc, const char **argv) {
 	g_game->finishTurn();
 
 	print("Moons advanced");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdOpacity(int argc, const char **argv) {
 	g_context->_opacity = !g_context->_opacity;
 	screenMessage("Opacity is %s", g_context->_opacity ? "on" : "off");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdPeer(int argc, const char **argv) {
@@ -511,7 +547,7 @@ bool Debugger::cmdPeer(int argc, const char **argv) {
 		g_context->_location->_viewMode = VIEW_NORMAL;
 
 	print("Toggle view");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdReagents(int argc, const char **argv) {
@@ -519,7 +555,7 @@ bool Debugger::cmdReagents(int argc, const char **argv) {
 		g_ultima->_saveGame->_reagents[i] = 99;
 
 	print("Reagents given");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdStats(int argc, const char **argv) {
@@ -536,7 +572,7 @@ bool Debugger::cmdStats(int argc, const char **argv) {
 	}
 
 	print("Full Stats given");
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdSummon(int argc, const char **argv) {
@@ -544,7 +580,7 @@ bool Debugger::cmdSummon(int argc, const char **argv) {
 
 	if (argc == 2) {
 		creature = argv[1];
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		print("summon <creature name>");
 		return true;
 	} else {
@@ -554,21 +590,21 @@ bool Debugger::cmdSummon(int argc, const char **argv) {
 	}
 
 	summonCreature(creature);
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdTorch(int argc, const char **argv) {
 	print("Torch: %d\n", g_context->_party->getTorchDuration());
-	if (!isActive())
+	if (!isDebuggerActive())
 		screenPrompt();
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdTransport(int argc, const char **argv) {
 	if (!g_context->_location->_map->isWorldMap()) {
 		print("Not here!");
-		return isActive();
+		return isDebuggerActive();
 	}
 
 	_horse = g_context->_location->_map->_tileset->getByName("horse")->getId();
@@ -583,9 +619,9 @@ bool Debugger::cmdTransport(int argc, const char **argv) {
 	char transport;
 	if (argc >= 2) {
 		transport = argv[1][0];
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		print("transport <transport name>");
-		return isActive();
+		return isDebuggerActive();
 	} else {
 		transport = ReadChoiceController::get("shb \033\015");
 	}
@@ -602,7 +638,7 @@ bool Debugger::cmdTransport(int argc, const char **argv) {
 		break;
 	default:
 		print("Unknown transport");
-		return isActive();
+		return isDebuggerActive();
 	}
 
 	tile = g_context->_location->_map->_tileset->get(choice->getId());
@@ -610,7 +646,7 @@ bool Debugger::cmdTransport(int argc, const char **argv) {
 
 	if (argc == 3) {
 		dir = directionFromName(argv[2]);
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		dir = DIR_NONE;
 	} else {
 		screenMessage("%s\n", tile->getName().c_str());
@@ -653,7 +689,7 @@ bool Debugger::cmdTransport(int argc, const char **argv) {
 		}
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdUp(int argc, const char **argv) {
@@ -667,7 +703,7 @@ bool Debugger::cmdUp(int argc, const char **argv) {
 		g_game->exitToParentMap();
 		g_music->play();
 
-		return isActive();
+		return isDebuggerActive();
 	}
 }
 
@@ -677,7 +713,7 @@ bool Debugger::cmdDown(int argc, const char **argv) {
 		return false;
 	} else {
 		print("Not here");
-		return isActive();
+		return isDebuggerActive();
 	}
 }
 
@@ -706,7 +742,7 @@ bool Debugger::cmdVirtue(int argc, const char **argv) {
 		}
 	}
 
-	return isActive();
+	return isDebuggerActive();
 }
 
 bool Debugger::cmdWind(int argc, const char **argv) {
@@ -714,7 +750,7 @@ bool Debugger::cmdWind(int argc, const char **argv) {
 
 	if (argc == 2) {
 		windDir = argv[1];
-	} else if (isActive()) {
+	} else if (isDebuggerActive()) {
 		print("wind <direction or 'lock'>");
 		return true;
 	} else {
@@ -732,7 +768,7 @@ bool Debugger::cmdWind(int argc, const char **argv) {
 
 		if (dir == DIR_NONE) {
 			print("Unknown direction");
-			return isActive();
+			return isDebuggerActive();
 		} else {
 			g_context->_windDirection = dir;
 		}
diff --git a/engines/ultima/ultima4/core/debugger.h b/engines/ultima/ultima4/core/debugger.h
index ebf1704dd4..b1e4f2641b 100644
--- a/engines/ultima/ultima4/core/debugger.h
+++ b/engines/ultima/ultima4/core/debugger.h
@@ -25,6 +25,7 @@
 
 #include "ultima/ultima4/core/coords.h"
 #include "ultima/ultima4/core/types.h"
+#include "ultima/ultima4/core/debugger_actions.h"
 #include "ultima/shared/engine/debugger.h"
 
 namespace Ultima {
@@ -33,38 +34,59 @@ namespace Ultima4 {
 /**
  * Debugger base class
  */
-class Debugger : public Shared::Debugger {
+class Debugger : public Shared::Debugger, public DebuggerActions {
 private:
 	MapTile _horse, _ship, _balloon;
-private:
+protected:
 	/**
-	 * Prints a message to the console if it's active, or to the
-	 * game screen if not
+	 * Returns true if the debugger is active
 	 */
-	void print(const char *fmt, ...);
+	bool isDebuggerActive() const override {
+		return isActive();
+	}
 
 	/**
-	 * Summons a creature given by 'creatureName'. This can either be given
-	 * as the creature's name, or the creature's id.  Once it finds the
-	 * creature to be summoned, it calls gameSpawnCreature() to spawn it.
+	 * Prints a message to the console if it's active, or to the
+	 * game screen if not
 	 */
-	void summonCreature(const Common::String &name);
+	virtual void print(const char *fmt, ...);
 
 	/**
-	 * Destroy object at a given co-ordinate
+	 * Prints a message to the console if it's active, or to the
+	 * game screen if not, with no newline
 	 */
-	bool destroyAt(const Coords &coords);
+	virtual void printN(const char *fmt, ...);
 
 	/**
-	 * Returns a direction from a given string
+	 * Gets the direction for an action
 	 */
-	Direction directionFromName(const Common::String &dirStr);
+	Direction getDirection(int argc, const char **argv);
 private:
 	/**
 	 * Move the avatar in a given direction
 	 */
 	bool cmdMove(int argc, const char **argv);
 
+	/**
+	 * Attack
+	 */
+	bool cmdAttack(int argc, const char **argv);
+
+	/**
+	 * Board transport
+	 */
+	bool cmdBoard(int argc, const char **argv);
+
+	/**
+	 * Cast spell
+	 */
+	bool cmdCastSpell(int argc, const char **argv);
+
+	/**
+	 * Pass turn
+	 */
+	bool cmdPass(int argc, const char **argv);
+
 private:
 	/**
 	 * Collision detection on/off
diff --git a/engines/ultima/ultima4/core/debugger_actions.cpp b/engines/ultima/ultima4/core/debugger_actions.cpp
new file mode 100644
index 0000000000..ae000b64d5
--- /dev/null
+++ b/engines/ultima/ultima4/core/debugger_actions.cpp
@@ -0,0 +1,138 @@
+/* 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 "ultima/ultima4/core/debugger_actions.h"
+#include "ultima/ultima4/game/context.h"
+#include "ultima/ultima4/game/player.h"
+#include "ultima/ultima4/gfx/screen.h"
+#include "ultima/ultima4/gfx/textcolor.h"
+#include "ultima/ultima4/map/combat.h"
+
+namespace Ultima {
+namespace Ultima4 {
+
+void DebuggerActions::summonCreature(const Common::String &name) {
+	const Creature *m = NULL;
+	Common::String creatureName = name;
+
+	creatureName.trim();
+	if (creatureName.empty()) {
+		print("\n");
+		return;
+	}
+
+	/* find the creature by its id and spawn it */
+	unsigned int id = atoi(creatureName.c_str());
+	if (id > 0)
+		m = creatureMgr->getById(id);
+
+	if (!m)
+		m = creatureMgr->getByName(creatureName);
+
+	if (m) {
+		if (gameSpawnCreature(m))
+			print("\n%s summoned!\n", m->getName().c_str());
+		else
+			print("\n\nNo place to put %s!\n\n", m->getName().c_str());
+
+		return;
+	}
+
+	print("\n%s not found\n", creatureName.c_str());
+}
+
+Direction DebuggerActions::directionFromName(const Common::String &dirStr) {
+	Common::String dir = dirStr;
+	dir.toLowercase();
+
+	if (dir == "up" || dir == "north")
+		return DIR_NORTH;
+	else if (dir == "down" || dir == "south")
+		return DIR_SOUTH;
+	else if (dir == "right" || dir == "east")
+		return DIR_EAST;
+	else if (dir == "left" || dir == "west")
+		return DIR_WEST;
+
+	return DIR_NONE;
+}
+
+bool DebuggerActions::destroyAt(const Coords &coords) {
+	Object *obj = g_context->_location->_map->objectAt(coords);
+
+	if (obj) {
+		if (isCreature(obj)) {
+			Creature *c = dynamic_cast<Creature *>(obj);
+			screenMessage("%s Destroyed!\n", c->getName().c_str());
+		} else {
+			Tile *t = g_context->_location->_map->_tileset->get(obj->getTile()._id);
+			screenMessage("%s Destroyed!\n", t->getName().c_str());
+		}
+
+		g_context->_location->_map->removeObject(obj);
+		screenPrompt();
+
+		return true;
+	}
+
+	return false;
+}
+
+bool DebuggerActions::attackAt(const Coords &coords) {
+	Object *under;
+	const Tile *ground;
+	Creature *m;
+
+	m = dynamic_cast<Creature *>(g_context->_location->_map->objectAt(coords));
+	/* nothing attackable: move on to next tile */
+	if (m == NULL || !m->isAttackable())
+		return false;
+
+	/* attack successful */
+	/// TODO: CHEST: Make a user option to not make chests change battlefield
+	/// map (1 of 2)
+	ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
+	if (!ground->isChest()) {
+		ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITHOUT_OBJECTS);
+		if ((under = g_context->_location->_map->objectAt(g_context->_location->_coords)) &&
+			under->getTile().getTileType()->isShip())
+			ground = under->getTile().getTileType();
+	}
+
+	/* You're attacking a townsperson!  Alert the guards! */
+	if ((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR))
+		g_context->_location->_map->alertGuards();
+
+	/* not good karma to be killing the innocent.  Bad avatar! */
+	if (m->isGood() || /* attacking a good creature */
+			/* attacking a docile (although possibly evil) person in town */
+		((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR)))
+		g_context->_party->adjustKarma(KA_ATTACKED_GOOD);
+
+	CombatController *cc = new CombatController(CombatMap::mapForTile(ground, g_context->_party->getTransport().getTileType(), m));
+	cc->init(m);
+	cc->begin();
+	return false;
+}
+
+} // End of namespace Ultima4
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima4/core/debugger_actions.h b/engines/ultima/ultima4/core/debugger_actions.h
new file mode 100644
index 0000000000..c696fdc1e1
--- /dev/null
+++ b/engines/ultima/ultima4/core/debugger_actions.h
@@ -0,0 +1,84 @@
+/* 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 ULTIMA4_CORE_DEBUGGER_ACTIONS_H
+#define ULTIMA4_CORE_DEBUGGER_ACTIONS_H
+
+#include "ultima/ultima4/core/coords.h"
+#include "ultima/ultima4/core/types.h"
+#include "ultima/shared/engine/debugger.h"
+
+namespace Ultima {
+namespace Ultima4 {
+
+/**
+ * This is a secondary class inherited by the Debugger class
+ * that contains various support methods for implementing the
+ * different actions players can take in the game
+ */
+class DebuggerActions {
+protected:
+	/**
+	 * Returns true if the debugger is active
+	 */
+	virtual bool isDebuggerActive() const = 0;
+
+	/**
+	 * Prints a message to the console if it's active, or to the
+	 * game screen if not
+	 */
+	virtual void print(const char *fmt, ...) = 0;
+
+	/**
+	 * Prints a message to the console if it's active, or to the
+	 * game screen if not
+	 */
+	virtual void printN(const char *fmt, ...) = 0;
+public:
+	/**
+	 * Summons a creature given by 'creatureName'. This can either be given
+	 * as the creature's name, or the creature's id.  Once it finds the
+	 * creature to be summoned, it calls gameSpawnCreature() to spawn it.
+	 */
+	void summonCreature(const Common::String &name);
+
+	/**
+	 * Destroy object at a given co-ordinate
+	 */
+	bool destroyAt(const Coords &coords);
+
+	/**
+	 * Returns a direction from a given string
+	 */
+	Direction directionFromName(const Common::String &dirStr);
+
+	/**
+	 * Attempts to attack a creature at map coordinates x,y.  If no
+	 * creature is present at that point, zero is returned.
+	 */
+	bool attackAt(const Coords &coords);
+};
+
+} // End of namespace Ultima4
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima4/events/controller.h b/engines/ultima/ultima4/events/controller.h
index 5c4e882202..726cc7d683 100644
--- a/engines/ultima/ultima4/events/controller.h
+++ b/engines/ultima/ultima4/events/controller.h
@@ -23,6 +23,8 @@
 #ifndef ULTIMA4_CONTROLLER_H
 #define ULTIMA4_CONTROLLER_H
 
+#include "ultima/ultima4/meta_engine.h"
+
 namespace Ultima {
 namespace Ultima4 {
 
@@ -56,9 +58,14 @@ public:
 	 */
 	static void timerCallback(void *data);
 
-	/* control methods subclasses may want to override */
+	/** control methods subclasses may want to override */
 	virtual bool keyPressed(int key) = 0;
 
+	/**
+	 * Handles keybinder actions
+	 */
+	virtual void keybinder(KeybindingAction action) {}
+
 	/**
 	 * The default timerFired handler for a controller.  By default,
 	 * timers are ignored, but subclasses can override this method and it
diff --git a/engines/ultima/ultima4/events/event.cpp b/engines/ultima/ultima4/events/event.cpp
index 94cbcee84c..7e365902a7 100644
--- a/engines/ultima/ultima4/events/event.cpp
+++ b/engines/ultima/ultima4/events/event.cpp
@@ -264,10 +264,32 @@ ReadDirController::ReadDirController() {
 	_value = DIR_NONE;
 }
 
-bool ReadDirController::keyPressed(int key) {
-	Direction d = keyToDirection(key);
-	bool valid = (d != DIR_NONE);
+void ReadDirController::keybinder(KeybindingAction action) {
+	switch (action) {
+	case KEYBIND_UP:
+		_value = DIR_NORTH;
+		break;
+	case KEYBIND_DOWN:
+		_value = DIR_SOUTH;
+		break;
+	case KEYBIND_LEFT:
+		_value = DIR_WEST;
+		break;
+	case KEYBIND_RIGHT:
+		_value = DIR_EAST;
+		break;
+	case KEYBIND_PASS:
+		_value = DIR_NONE;
+		doneWaiting();
+		break;
+	default:
+		return;
+	}
+
+	doneWaiting();
+}
 
+bool ReadDirController::keyPressed(int key) {
 	switch (key) {
 	case Common::KEYCODE_ESCAPE:
 	case Common::KEYCODE_SPACE:
@@ -277,11 +299,6 @@ bool ReadDirController::keyPressed(int key) {
 		return true;
 
 	default:
-		if (valid) {
-			_value = d;
-			doneWaiting();
-			return true;
-		}
 		break;
 	}
 
diff --git a/engines/ultima/ultima4/events/event.h b/engines/ultima/ultima4/events/event.h
index fd44a2aca0..50df579b29 100644
--- a/engines/ultima/ultima4/events/event.h
+++ b/engines/ultima/ultima4/events/event.h
@@ -212,7 +212,16 @@ protected:
 class ReadDirController : public WaitableController<Direction> {
 public:
 	ReadDirController();
+
+	/**
+	 * Key was pressed
+	 */
 	bool keyPressed(int key) override;
+
+	/**
+	 * Handles keybinder actions
+	 */
+	void keybinder(KeybindingAction action) override;
 };
 
 /**
diff --git a/engines/ultima/ultima4/events/event_scummvm.cpp b/engines/ultima/ultima4/events/event_scummvm.cpp
index 6ff8260715..8f37bfc7cc 100644
--- a/engines/ultima/ultima4/events/event_scummvm.cpp
+++ b/engines/ultima/ultima4/events/event_scummvm.cpp
@@ -245,7 +245,7 @@ void EventHandler::run() {
 			continue;
 
 		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
-			MetaEngine::executeAction((KeybindingAction)event.customType);
+			getController()->keybinder((KeybindingAction)event.customType);
 			break;
 
 		case Common::EVENT_QUIT:
diff --git a/engines/ultima/ultima4/game/game.cpp b/engines/ultima/ultima4/game/game.cpp
index 9e7113894a..198fb5d1fb 100644
--- a/engines/ultima/ultima4/game/game.cpp
+++ b/engines/ultima/ultima4/game/game.cpp
@@ -63,6 +63,7 @@
 #include "ultima/ultima4/map/dungeonview.h"
 #include "ultima/ultima4/sound/music.h"
 #include "ultima/ultima4/sound/sound.h"
+#include "ultima/ultima4/meta_engine.h"
 #include "common/savefile.h"
 #include "common/system.h"
 
@@ -549,6 +550,10 @@ void gameCastSpell(unsigned int spell, int caster, int param) {
 	}
 }
 
+void GameController::keybinder(KeybindingAction action) {
+	MetaEngine::executeAction(action);
+}
+
 bool GameController::keyPressed(int key) {
 	bool valid = true;
 	int endTurn = 1;
@@ -685,14 +690,6 @@ bool GameController::keyPressed(int key) {
 			endTurn = false;
 			break;
 
-		case 'a':
-			attack();
-			break;
-
-		case 'b':
-			board();
-			break;
-
 		case 'c':
 			castSpell();
 			break;
@@ -1211,101 +1208,6 @@ bool ZtatsController::keyPressed(int key) {
 	}
 }
 
-void attack() {
-	screenMessage("Attack: ");
-
-	if (g_context->_party->isFlying()) {
-		screenMessage("\n%cDrift only!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	Direction dir = gameGetDirection();
-
-	if (dir == DIR_NONE)
-		return;
-
-	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
-	                           1, 1, NULL, true);
-	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
-		if (attackAt(*i))
-			return;
-	}
-
-	screenMessage("%cNothing to Attack!%c\n", FG_GREY, FG_WHITE);
-}
-
-/**
- * Attempts to attack a creature at map coordinates x,y.  If no
- * creature is present at that point, zero is returned.
- */
-bool attackAt(const Coords &coords) {
-	Object *under;
-	const Tile *ground;
-	Creature *m;
-
-	m = dynamic_cast<Creature *>(g_context->_location->_map->objectAt(coords));
-	/* nothing attackable: move on to next tile */
-	if (m == NULL || !m->isAttackable())
-		return false;
-
-	/* attack successful */
-	/// TODO: CHEST: Make a user option to not make chests change battlefield
-	/// map (1 of 2)
-	ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
-	if (!ground->isChest()) {
-		ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITHOUT_OBJECTS);
-		if ((under = g_context->_location->_map->objectAt(g_context->_location->_coords)) &&
-		        under->getTile().getTileType()->isShip())
-			ground = under->getTile().getTileType();
-	}
-
-	/* You're attacking a townsperson!  Alert the guards! */
-	if ((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR))
-		g_context->_location->_map->alertGuards();
-
-	/* not good karma to be killing the innocent.  Bad avatar! */
-	if (m->isGood() || /* attacking a good creature */
-	        /* attacking a docile (although possibly evil) person in town */
-	        ((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR)))
-		g_context->_party->adjustKarma(KA_ATTACKED_GOOD);
-
-	CombatController *cc = new CombatController(CombatMap::mapForTile(ground, g_context->_party->getTransport().getTileType(), m));
-	cc->init(m);
-	cc->begin();
-	return true;
-}
-
-void board() {
-	if (g_context->_transportContext != TRANSPORT_FOOT) {
-		screenMessage("Board: %cCan't!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	Object *obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
-	if (!obj) {
-		screenMessage("%cBoard What?%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	const Tile *tile = obj->getTile().getTileType();
-	if (tile->isShip()) {
-		screenMessage("Board Frigate!\n");
-		if (g_context->_lastShip != obj)
-			g_context->_party->setShipHull(50);
-	} else if (tile->isHorse())
-		screenMessage("Mount Horse!\n");
-	else if (tile->isBalloon())
-		screenMessage("Board Balloon!\n");
-	else {
-		screenMessage("%cBoard What?%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	g_context->_party->setTransport(obj->getTile());
-	g_context->_location->_map->removeObject(obj);
-}
-
-
 void castSpell(int player) {
 	if (player == -1) {
 		screenMessage("Cast Spell!\nPlayer: ");
@@ -2545,7 +2447,7 @@ void GameController::timerFired() {
 			gameTimeSinceLastCommand() > 20) {
 
 			/* pass the turn, and redraw the text area so the prompt is shown */
-			controller->keyPressed(U4_SPACE);
+			MetaEngine::executeAction(KEYBIND_PASS);
 			screenRedrawTextArea(TEXT_AREA_X, TEXT_AREA_Y, TEXT_AREA_W, TEXT_AREA_H);
 		}
 	}
diff --git a/engines/ultima/ultima4/game/game.h b/engines/ultima/ultima4/game/game.h
index 1842e4cf99..80eb69c131 100644
--- a/engines/ultima/ultima4/game/game.h
+++ b/engines/ultima/ultima4/game/game.h
@@ -110,6 +110,12 @@ public:
 	GameController();
 
 	/* controller functions */
+
+	/**
+	 * Keybinder actions
+	 */
+	void keybinder(KeybindingAction action) override;
+
 	/**
 	 * The main key handler for the game.  Interpretes each key as a
 	 * command - 'a' for attack, 't' for talk, etc.
@@ -233,9 +239,6 @@ void castSpell(int player = -1);
 void gameSpellEffect(int spell, int player, Sound sound);
 
 /* action functions */
-void destroy();
-void attack();
-void board();
 void fire();
 void getChest(int player = -1);
 void holeUp();
diff --git a/engines/ultima/ultima4/meta_engine.cpp b/engines/ultima/ultima4/meta_engine.cpp
index ac25c80069..f06a5fe959 100644
--- a/engines/ultima/ultima4/meta_engine.cpp
+++ b/engines/ultima/ultima4/meta_engine.cpp
@@ -39,16 +39,20 @@ struct KeybindingRecord {
 };
 
 static const KeybindingRecord KEYS[] = {
-	{ ACTION_NORTH, "UP", "Up", "move up", "UP", nullptr },
-	{ ACTION_SOUTH, "DOWN", "Down", "move down", "DOWN", nullptr },
-	{ ACTION_WEST, "LEFT", "Left", "move left", "LEFT", nullptr },
-	{ ACTION_EAST, "RIGHT", "Right", "move right", "RIGHT", nullptr },
-
-	{ ACTION_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
+	{ KEYBIND_UP, "UP", "Up", "move up", "UP", nullptr },
+	{ KEYBIND_DOWN, "DOWN", "Down", "move down", "DOWN", nullptr },
+	{ KEYBIND_LEFT, "LEFT", "Left", "move left", "LEFT", nullptr },
+	{ KEYBIND_RIGHT, "RIGHT", "Right", "move right", "RIGHT", nullptr },
+	{ KEYBIND_ATTACK, "ATTACK", "Attack", "attack", "a", nullptr },
+	{ KEYBIND_BOARD, "BOARD", "Board", "board", "b", nullptr },
+	{ KEYBIND_CAST, "CAST", "Cast", "cast", "c", nullptr },
+	{ KEYBIND_PASS, "PASS", "Pass", "pass", "SPACE", nullptr },
+
+	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
 };
 
 static const KeybindingRecord CHEAT_KEYS[] = {
-	{ ACTION_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
+	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
 };
 
 
diff --git a/engines/ultima/ultima4/meta_engine.h b/engines/ultima/ultima4/meta_engine.h
index 659d6fbb8c..0cdcfbaa83 100644
--- a/engines/ultima/ultima4/meta_engine.h
+++ b/engines/ultima/ultima4/meta_engine.h
@@ -29,9 +29,11 @@ namespace Ultima {
 namespace Ultima4 {
 
 enum KeybindingAction {
-	ACTION_NORTH, ACTION_SOUTH, ACTION_EAST, ACTION_WEST,
+	KEYBIND_UP, KEYBIND_DOWN, KEYBIND_LEFT, KEYBIND_RIGHT,
+	KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST,
+	KEYBIND_PASS,
 
-	ACTION_NONE
+	KEYBIND_NONE
 };
 
 class MetaEngine {


Commit: e5894a132e3f2c352ffc78fc199b96c517031943
    https://github.com/scummvm/scummvm/commit/e5894a132e3f2c352ffc78fc199b96c517031943
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2020-04-12T18:54:30-07:00

Commit Message:
ULTIMA4: Better handling for keybind actions ending turns

Changed paths:
    engines/ultima/ultima4/core/debugger.cpp
    engines/ultima/ultima4/core/debugger.h


diff --git a/engines/ultima/ultima4/core/debugger.cpp b/engines/ultima/ultima4/core/debugger.cpp
index f38eb896c1..e815e16b7b 100644
--- a/engines/ultima/ultima4/core/debugger.cpp
+++ b/engines/ultima/ultima4/core/debugger.cpp
@@ -112,6 +112,15 @@ void Debugger::printN(const char *fmt, ...) {
 	}
 }
 
+bool Debugger::handleCommand(int argc, const char **argv, bool &keepRunning) {
+	bool result = Shared::Debugger::handleCommand(argc, argv, keepRunning);
+
+	if (result && !isActive() && argv[0] != "move")
+		g_game->finishTurn();
+
+	return result;
+}
+
 
 bool Debugger::cmdMove(int argc, const char **argv) {
 	Direction dir;
@@ -178,7 +187,6 @@ bool Debugger::cmdAttack(int argc, const char **argv) {
 	}
 
 	print("%cNothing to Attack!%c", FG_GREY, FG_WHITE);
-	g_game->finishTurn();
 	return isDebuggerActive();
 }
 
@@ -220,7 +228,6 @@ bool Debugger::cmdCastSpell(int argc, const char **argv) {
 
 bool Debugger::cmdPass(int argc, const char **argv) {
 	print("Pass");
-	g_game->finishTurn();
 	return isDebuggerActive();
 }
 
@@ -277,7 +284,6 @@ bool Debugger::cmdDestroy(int argc, const char **argv) {
 		MASK_DIR_ALL, g_context->_location->_coords, 1, 1, NULL, true);
 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
 		if (destroyAt(*i)) {
-			g_game->finishTurn();
 			return false;
 		}
 	}
@@ -311,7 +317,6 @@ bool Debugger::cmdDungeon(int argc, const char **argv) {
 				return isDebuggerActive();
 			}
 
-			g_game->finishTurn();
 			return false;
 		} else {
 			print("dungeon <number>");
@@ -345,7 +350,6 @@ bool Debugger::cmdExit(int argc, const char **argv) {
 	if (!g_game->exitToParentMap()) {
 		print("Not Here");
 	} else {
-		g_game->finishTurn();
 		g_music->play();
 		print("Exited");
 	}
@@ -366,7 +370,6 @@ bool Debugger::cmdGate(int argc, const char **argv) {
 			const Coords *moongate = moongateGetGateCoordsForPhase(gateNum - 1);
 			if (moongate) {
 				g_context->_location->_coords = *moongate;
-				g_game->finishTurn();
 				return false;
 			}
 		} else {
@@ -417,7 +420,6 @@ bool Debugger::cmdGoto(int argc, const char **argv) {
 	}
 
 	if (found) {
-		g_game->finishTurn();
 		return false;
 	} else {
 		if (isDebuggerActive())
@@ -440,7 +442,6 @@ bool Debugger::cmdHelp(int argc, const char **argv) {
 	g_context->_location->_coords.x = 19;
 	g_context->_location->_coords.y = 8;
 	g_context->_location->_coords.z = 0;
-	g_game->finishTurn();
 
 	return false;
 }
@@ -526,7 +527,6 @@ bool Debugger::cmdMoon(int argc, const char **argv) {
 
 	while (g_ultima->_saveGame->_trammelPhase != moonNum)
 		g_game->updateMoons(true);
-	g_game->finishTurn();
 
 	print("Moons advanced");
 	return isDebuggerActive();
@@ -695,7 +695,6 @@ bool Debugger::cmdTransport(int argc, const char **argv) {
 bool Debugger::cmdUp(int argc, const char **argv) {
 	if ((g_context->_location->_context & CTX_DUNGEON) && (g_context->_location->_coords.z > 0)) {
 		g_context->_location->_coords.z--;
-		g_game->finishTurn();
 
 		return false;
 	} else {
diff --git a/engines/ultima/ultima4/core/debugger.h b/engines/ultima/ultima4/core/debugger.h
index b1e4f2641b..d2ed92c71b 100644
--- a/engines/ultima/ultima4/core/debugger.h
+++ b/engines/ultima/ultima4/core/debugger.h
@@ -45,17 +45,24 @@ protected:
 		return isActive();
 	}
 
+	/**
+	 * Process the given command line.
+	 * Returns true if and only if argv[0] is a known command and was
+	 * handled, false otherwise.
+	 */
+	bool handleCommand(int argc, const char **argv, bool &keepRunning) override;
+
 	/**
 	 * Prints a message to the console if it's active, or to the
 	 * game screen if not
 	 */
-	virtual void print(const char *fmt, ...);
+	void print(const char *fmt, ...) override;
 
 	/**
 	 * Prints a message to the console if it's active, or to the
 	 * game screen if not, with no newline
 	 */
-	virtual void printN(const char *fmt, ...);
+	void printN(const char *fmt, ...) override;
 
 	/**
 	 * Gets the direction for an action


Commit: dba370697f1ed153b2b7d018ffd309cbc26994cf
    https://github.com/scummvm/scummvm/commit/dba370697f1ed153b2b7d018ffd309cbc26994cf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2020-04-12T18:54:30-07:00

Commit Message:
ULTIMA4: Changed enter to a keybinder action

Changed paths:
    engines/ultima/ultima4/core/debugger.cpp
    engines/ultima/ultima4/core/debugger.h
    engines/ultima/ultima4/game/game.cpp
    engines/ultima/ultima4/meta_engine.cpp
    engines/ultima/ultima4/meta_engine.h


diff --git a/engines/ultima/ultima4/core/debugger.cpp b/engines/ultima/ultima4/core/debugger.cpp
index e815e16b7b..fcc5f69484 100644
--- a/engines/ultima/ultima4/core/debugger.cpp
+++ b/engines/ultima/ultima4/core/debugger.cpp
@@ -41,11 +41,13 @@ Debugger *g_debugger;
 Debugger::Debugger() : Shared::Debugger() {
 	g_debugger = this;
 	_collisionOverride = false;
+	_dontEndTurn = false;
 
 	registerCmd("move", WRAP_METHOD(Debugger, cmdMove));
 	registerCmd("attack", WRAP_METHOD(Debugger, cmdAttack));
 	registerCmd("board", WRAP_METHOD(Debugger, cmdBoard));
 	registerCmd("cast", WRAP_METHOD(Debugger, cmdCastSpell));
+	registerCmd("enter", WRAP_METHOD(Debugger, cmdEnter));
 	registerCmd("pass", WRAP_METHOD(Debugger, cmdPass));
 
 	registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
@@ -115,9 +117,12 @@ void Debugger::printN(const char *fmt, ...) {
 bool Debugger::handleCommand(int argc, const char **argv, bool &keepRunning) {
 	bool result = Shared::Debugger::handleCommand(argc, argv, keepRunning);
 
-	if (result && !isActive() && argv[0] != "move")
-		g_game->finishTurn();
+	if (result && !isActive()) {
+		if (!_dontEndTurn)
+			g_game->finishTurn();
+	}
 
+	_dontEndTurn = false;
 	return result;
 }
 
@@ -146,8 +151,8 @@ bool Debugger::cmdMove(int argc, const char **argv) {
 
 	// Let the movement handler decide to end the turn
 	bool endTurn = (retval & MOVE_END_TURN);
-	if (endTurn)
-		g_game->finishTurn();
+	if (!endTurn)
+		dontEndTurn();
 
 	return false;
 }
@@ -226,6 +231,17 @@ bool Debugger::cmdCastSpell(int argc, const char **argv) {
 	return isDebuggerActive();
 }
 
+bool Debugger::cmdEnter(int argc, const char **argv) {
+	if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_ENTER)) {
+		if (!g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER))
+			print("%cEnter what?%c\n", FG_GREY, FG_WHITE);
+	} else {
+		dontEndTurn();
+	}
+
+	return isDebuggerActive();
+}
+
 bool Debugger::cmdPass(int argc, const char **argv) {
 	print("Pass");
 	return isDebuggerActive();
diff --git a/engines/ultima/ultima4/core/debugger.h b/engines/ultima/ultima4/core/debugger.h
index d2ed92c71b..042e04ff45 100644
--- a/engines/ultima/ultima4/core/debugger.h
+++ b/engines/ultima/ultima4/core/debugger.h
@@ -37,6 +37,7 @@ namespace Ultima4 {
 class Debugger : public Shared::Debugger, public DebuggerActions {
 private:
 	MapTile _horse, _ship, _balloon;
+	bool _dontEndTurn;
 protected:
 	/**
 	 * Returns true if the debugger is active
@@ -68,6 +69,14 @@ protected:
 	 * Gets the direction for an action
 	 */
 	Direction getDirection(int argc, const char **argv);
+
+	/**
+	 * Used by methods so that when they're triggered by a keybinding
+	 * action, stops the turn from being finished when they're done
+	 */
+	void dontEndTurn() {
+		_dontEndTurn = true;
+	}
 private:
 	/**
 	 * Move the avatar in a given direction
@@ -89,6 +98,11 @@ private:
 	 */
 	bool cmdCastSpell(int argc, const char **argv);
 
+	/**
+	 * Enter location
+	 */
+	bool cmdEnter(int argc, const char **argv);
+
 	/**
 	 * Pass turn
 	 */
diff --git a/engines/ultima/ultima4/game/game.cpp b/engines/ultima/ultima4/game/game.cpp
index 198fb5d1fb..0fbbcc6bfa 100644
--- a/engines/ultima/ultima4/game/game.cpp
+++ b/engines/ultima/ultima4/game/game.cpp
@@ -717,13 +717,6 @@ bool GameController::keyPressed(int key) {
 			break;
 		}
 
-		case 'e':
-			if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_ENTER)) {
-				if (!g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER))
-					screenMessage("%cEnter what?%c\n", FG_GREY, FG_WHITE);
-			} else endTurn = 0; /* entering a portal doesn't end the turn */
-			break;
-
 		case 'f':
 			fire();
 			break;
diff --git a/engines/ultima/ultima4/meta_engine.cpp b/engines/ultima/ultima4/meta_engine.cpp
index f06a5fe959..a08ad06a52 100644
--- a/engines/ultima/ultima4/meta_engine.cpp
+++ b/engines/ultima/ultima4/meta_engine.cpp
@@ -46,6 +46,7 @@ static const KeybindingRecord KEYS[] = {
 	{ KEYBIND_ATTACK, "ATTACK", "Attack", "attack", "a", nullptr },
 	{ KEYBIND_BOARD, "BOARD", "Board", "board", "b", nullptr },
 	{ KEYBIND_CAST, "CAST", "Cast", "cast", "c", nullptr },
+	{ KEYBIND_ENTER, "ENTER", "Enter", "enter", "e", nullptr },
 	{ KEYBIND_PASS, "PASS", "Pass", "pass", "SPACE", nullptr },
 
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
diff --git a/engines/ultima/ultima4/meta_engine.h b/engines/ultima/ultima4/meta_engine.h
index 0cdcfbaa83..2875c1b398 100644
--- a/engines/ultima/ultima4/meta_engine.h
+++ b/engines/ultima/ultima4/meta_engine.h
@@ -30,7 +30,7 @@ namespace Ultima4 {
 
 enum KeybindingAction {
 	KEYBIND_UP, KEYBIND_DOWN, KEYBIND_LEFT, KEYBIND_RIGHT,
-	KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST,
+	KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST, KEYBIND_ENTER,
 	KEYBIND_PASS,
 
 	KEYBIND_NONE


Commit: b8c3775644ce8f21366d2fb284deab3939a2d527
    https://github.com/scummvm/scummvm/commit/b8c3775644ce8f21366d2fb284deab3939a2d527
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2020-04-12T18:54:30-07:00

Commit Message:
ULTIMA4: Shifting more actions to keybindings

Changed paths:
    engines/ultima/ultima4/core/debugger.cpp
    engines/ultima/ultima4/core/debugger.h
    engines/ultima/ultima4/core/debugger_actions.cpp
    engines/ultima/ultima4/core/debugger_actions.h
    engines/ultima/ultima4/game/game.cpp
    engines/ultima/ultima4/game/game.h
    engines/ultima/ultima4/game/spell.cpp
    engines/ultima/ultima4/map/combat.cpp
    engines/ultima/ultima4/meta_engine.cpp
    engines/ultima/ultima4/meta_engine.h


diff --git a/engines/ultima/ultima4/core/debugger.cpp b/engines/ultima/ultima4/core/debugger.cpp
index fcc5f69484..b81bbeaf92 100644
--- a/engines/ultima/ultima4/core/debugger.cpp
+++ b/engines/ultima/ultima4/core/debugger.cpp
@@ -29,6 +29,9 @@
 #include "ultima/ultima4/game/stats.h"
 #include "ultima/ultima4/game/weapon.h"
 #include "ultima/ultima4/gfx/screen.h"
+#include "ultima/ultima4/map/annotation.h"
+#include "ultima/ultima4/map/camp.h"
+#include "ultima/ultima4/map/city.h"
 #include "ultima/ultima4/map/dungeonview.h"
 #include "ultima/ultima4/map/mapmgr.h"
 #include "ultima/ultima4/ultima4.h"
@@ -48,6 +51,12 @@ Debugger::Debugger() : Shared::Debugger() {
 	registerCmd("board", WRAP_METHOD(Debugger, cmdBoard));
 	registerCmd("cast", WRAP_METHOD(Debugger, cmdCastSpell));
 	registerCmd("enter", WRAP_METHOD(Debugger, cmdEnter));
+	registerCmd("fire", WRAP_METHOD(Debugger, cmdFire));
+	registerCmd("get", WRAP_METHOD(Debugger, cmdGet));
+	registerCmd("hole", WRAP_METHOD(Debugger, cmdHoleUp));
+	registerCmd("ignite", WRAP_METHOD(Debugger, cmdIgnite));
+	registerCmd("jimmy", WRAP_METHOD(Debugger, cmdJimmy));
+
 	registerCmd("pass", WRAP_METHOD(Debugger, cmdPass));
 
 	registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
@@ -126,6 +135,14 @@ bool Debugger::handleCommand(int argc, const char **argv, bool &keepRunning) {
 	return result;
 }
 
+void Debugger::getChest(int player) {
+	Common::String param = Common::String::format("%d", player);
+	const char *argv[2] = { "get", param.c_str() };
+
+	cmdGet(2, argv);
+}
+
+
 
 bool Debugger::cmdMove(int argc, const char **argv) {
 	Direction dir;
@@ -242,6 +259,144 @@ bool Debugger::cmdEnter(int argc, const char **argv) {
 	return isDebuggerActive();
 }
 
+bool Debugger::cmdFire(int argc, const char **argv) {
+	if (g_context->_transportContext != TRANSPORT_SHIP) {
+		print("%cFire What?%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	printN("Fire Cannon!\nDir: ");
+	Direction dir = gameGetDirection();
+
+	if (dir == DIR_NONE)
+		return isDebuggerActive();
+
+	// can only fire broadsides
+	int broadsidesDirs = dirGetBroadsidesDirs(g_context->_party->getDirection());
+	if (!DIR_IN_MASK(dir, broadsidesDirs)) {
+		print("%cBroadsides Only!%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	// nothing (not even mountains!) can block cannonballs
+	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), broadsidesDirs, g_context->_location->_coords,
+		1, 3, NULL, false);
+	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
+		if (fireAt(*i, true))
+			return isDebuggerActive();
+	}
+
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdGet(int argc, const char **argv) {
+	int player = 1;
+	if (argc == 2)
+		player = strToInt(argv[1]);
+
+	print("Get Chest!");
+
+	if (g_context->_party->isFlying()) {
+		print("%cDrift only!%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	// first check to see if a chest exists at the current location
+	// if one exists, prompt the player for the opener, if necessary
+	MapCoords coords;
+	g_context->_location->getCurrentPosition(&coords);
+	const Tile *tile = g_context->_location->_map->tileTypeAt(coords, WITH_GROUND_OBJECTS);
+
+	/* get the object for the chest, if it is indeed an object */
+	Object *obj = g_context->_location->_map->objectAt(coords);
+	if (obj && !obj->getTile().getTileType()->isChest())
+		obj = NULL;
+
+	if (tile->isChest() || obj) {
+		// if a spell was cast to open this chest,
+		// player will equal -2, otherwise player
+		// will default to -1 or the defult character
+		// number if one was earlier specified
+		if (player == -1) {
+			printN("Who opens? ");
+			player = gameGetPlayer(false, true);
+		}
+		if (player == -1)
+			return isDebuggerActive();
+
+		if (obj)
+			g_context->_location->_map->removeObject(obj);
+		else {
+			TileId newTile = g_context->_location->getReplacementTile(coords, tile);
+			g_context->_location->_map->_annotations->add(coords, newTile, false, true);
+		}
+
+		// see if the chest is trapped and handle it
+		getChestTrapHandler(player);
+
+		print("The Chest Holds: %d Gold", g_context->_party->getChest());
+
+		screenPrompt();
+
+		if (isCity(g_context->_location->_map) && obj == NULL)
+			g_context->_party->adjustKarma(KA_STOLE_CHEST);
+	} else {
+		print("%cNot Here!%c", FG_GREY, FG_WHITE);
+	}
+
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdHoleUp(int argc, const char **argv) {
+	print("Hole up & Camp!");
+
+	if (!(g_context->_location->_context & (CTX_WORLDMAP | CTX_DUNGEON))) {
+		print("%cNot here!%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	if (g_context->_transportContext != TRANSPORT_FOOT) {
+		print("%cOnly on foot!%c", FG_GREY, FG_WHITE);
+		return isDebuggerActive();
+	}
+
+	CombatController *cc = new CampController();
+	cc->init(NULL);
+	cc->begin();
+
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdIgnite(int argc, const char **argv) {
+	print("Ignite torch!");
+	if (g_context->_location->_context == CTX_DUNGEON) {
+		if (!g_context->_party->lightTorch())
+			print("%cNone left!%c", FG_GREY, FG_WHITE);
+	} else {
+		print("%cNot here!%c", FG_GREY, FG_WHITE);
+	}
+
+	return isDebuggerActive();
+}
+
+bool Debugger::cmdJimmy(int argc, const char **argv) {
+	screenMessage("Jimmy: ");
+	Direction dir = gameGetDirection();
+
+	if (dir == DIR_NONE)
+		return isDebuggerActive();
+
+	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
+		1, 1, NULL, true);
+	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
+		if (jimmyAt(*i))
+			return isDebuggerActive();
+	}
+
+	print("%cJimmy what?%c", FG_GREY, FG_WHITE);
+	return isDebuggerActive();
+}
+
 bool Debugger::cmdPass(int argc, const char **argv) {
 	print("Pass");
 	return isDebuggerActive();
diff --git a/engines/ultima/ultima4/core/debugger.h b/engines/ultima/ultima4/core/debugger.h
index 042e04ff45..dc75632ac0 100644
--- a/engines/ultima/ultima4/core/debugger.h
+++ b/engines/ultima/ultima4/core/debugger.h
@@ -103,6 +103,31 @@ private:
 	 */
 	bool cmdEnter(int argc, const char **argv);
 
+	/**
+	 * Fire
+	 */
+	bool cmdFire(int argc, const char **argv);
+
+	/**
+	 * Get chest
+	 */
+	bool cmdGet(int argc, const char **argv);
+
+	/**
+	 * Hole Up
+	 */
+	bool cmdHoleUp(int argc, const char **argv);
+
+	/**
+	 * Ignite Torch
+	 */
+	bool cmdIgnite(int argc, const char **argv);
+
+	/**
+	 * Jimmy lock
+	 */
+	bool cmdJimmy(int argc, const char **argv);
+
 	/**
 	 * Pass turn
 	 */
@@ -244,6 +269,13 @@ public:
 public:
 	Debugger();
 	~Debugger() override;
+
+	/**
+	 * Gets a chest.
+	 * If the default -2 is used, it bypasses prompting for a
+	 * user. Otherwise, a non-negative player number is expected
+	 */
+	void getChest(int player = -2);
 };
 
 extern Debugger *g_debugger;
diff --git a/engines/ultima/ultima4/core/debugger_actions.cpp b/engines/ultima/ultima4/core/debugger_actions.cpp
index ae000b64d5..2f6cd284a9 100644
--- a/engines/ultima/ultima4/core/debugger_actions.cpp
+++ b/engines/ultima/ultima4/core/debugger_actions.cpp
@@ -21,10 +21,12 @@
  */
 
 #include "ultima/ultima4/core/debugger_actions.h"
+#include "ultima/ultima4/core/utils.h"
 #include "ultima/ultima4/game/context.h"
 #include "ultima/ultima4/game/player.h"
 #include "ultima/ultima4/gfx/screen.h"
 #include "ultima/ultima4/gfx/textcolor.h"
+#include "ultima/ultima4/map/annotation.h"
 #include "ultima/ultima4/map/combat.h"
 
 namespace Ultima {
@@ -134,5 +136,81 @@ bool DebuggerActions::attackAt(const Coords &coords) {
 	return false;
 }
 
+bool DebuggerActions::getChestTrapHandler(int player) {
+	TileEffect trapType;
+	int randNum = xu4_random(4);
+
+	/* Do we use u4dos's way of trap-determination, or the original intended way? */
+	int passTest = (settings._enhancements && settings._enhancementsOptions._c64chestTraps) ?
+		(xu4_random(2) == 0) : /* xu4-enhanced */
+		((randNum & 1) == 0); /* u4dos original way (only allows even numbers through, so only acid and poison show) */
+
+/* Chest is trapped! 50/50 chance */
+	if (passTest) {
+		/* Figure out which trap the chest has */
+		switch (randNum & xu4_random(4)) {
+		case 0:
+			trapType = EFFECT_FIRE;
+			break;   /* acid trap (56% chance - 9/16) */
+		case 1:
+			trapType = EFFECT_SLEEP;
+			break;  /* sleep trap (19% chance - 3/16) */
+		case 2:
+			trapType = EFFECT_POISON;
+			break; /* poison trap (19% chance - 3/16) */
+		case 3:
+			trapType = EFFECT_LAVA;
+			break;   /* bomb trap (6% chance - 1/16) */
+		default:
+			trapType = EFFECT_FIRE;
+			break;
+		}
+
+		/* apply the effects from the trap */
+		if (trapType == EFFECT_FIRE)
+			screenMessage("%cAcid%c Trap!\n", FG_RED, FG_WHITE);
+		else if (trapType == EFFECT_POISON)
+			screenMessage("%cPoison%c Trap!\n", FG_GREEN, FG_WHITE);
+		else if (trapType == EFFECT_SLEEP)
+			screenMessage("%cSleep%c Trap!\n", FG_PURPLE, FG_WHITE);
+		else if (trapType == EFFECT_LAVA)
+			screenMessage("%cBomb%c Trap!\n", FG_RED, FG_WHITE);
+
+		// player is < 0 during the 'O'pen spell (immune to traps)
+		//
+		// if the chest was opened by a PC, see if the trap was
+		// evaded by testing the PC's dex
+		//
+		if ((player >= 0) &&
+			(g_ultima->_saveGame->_players[player]._dex + 25 < xu4_random(100))) {
+			if (trapType == EFFECT_LAVA) /* bomb trap */
+				g_context->_party->applyEffect(trapType);
+			else g_context->_party->member(player)->applyEffect(trapType);
+		} else screenMessage("Evaded!\n");
+
+		return true;
+	}
+
+	return false;
+}
+
+bool DebuggerActions::jimmyAt(const Coords &coords) {
+	MapTile *tile = g_context->_location->_map->tileAt(coords, WITH_OBJECTS);
+
+	if (!tile->getTileType()->isLockedDoor())
+		return false;
+
+	if (g_ultima->_saveGame->_keys) {
+		Tile *door = g_context->_location->_map->_tileset->getByName("door");
+		ASSERT(door, "no door tile found in tileset");
+		g_ultima->_saveGame->_keys--;
+		g_context->_location->_map->_annotations->add(coords, door->getId());
+		screenMessage("\nUnlocked!\n");
+	} else
+		screenMessage("%cNo keys left!%c\n", FG_GREY, FG_WHITE);
+
+	return true;
+}
+
 } // End of namespace Ultima4
 } // End of namespace Ultima
diff --git a/engines/ultima/ultima4/core/debugger_actions.h b/engines/ultima/ultima4/core/debugger_actions.h
index c696fdc1e1..7df8ca83da 100644
--- a/engines/ultima/ultima4/core/debugger_actions.h
+++ b/engines/ultima/ultima4/core/debugger_actions.h
@@ -76,6 +76,19 @@ public:
 	 * creature is present at that point, zero is returned.
 	 */
 	bool attackAt(const Coords &coords);
+
+	/**
+	 * Called by getChest() to handle possible traps on chests
+	 **/
+	bool getChestTrapHandler(int player);
+
+	/**
+	 * Attempts to jimmy a locked door at map coordinates x,y.  The locked
+	 * door is replaced by a permanent annotation of an unlocked door
+	 * tile.
+	 */
+	bool jimmyAt(const Coords &coords);
+
 };
 
 } // End of namespace Ultima4
diff --git a/engines/ultima/ultima4/game/game.cpp b/engines/ultima/ultima4/game/game.cpp
index 0fbbcc6bfa..4aebcd0506 100644
--- a/engines/ultima/ultima4/game/game.cpp
+++ b/engines/ultima/ultima4/game/game.cpp
@@ -99,9 +99,6 @@ bool talkAt(const Coords &coords);
 void talkRunConversation(Conversation &conv, Person *talker, bool showPrompt);
 
 /* action functions */
-bool attackAt(const Coords &coords);
-bool getChestTrapHandler(int player);
-bool jimmyAt(const Coords &coords);
 bool openAt(const Coords &coords);
 void wearArmor(int player = -1);
 void ztatsFor(int player = -1);
@@ -717,30 +714,6 @@ bool GameController::keyPressed(int key) {
 			break;
 		}
 
-		case 'f':
-			fire();
-			break;
-
-		case 'g':
-			getChest();
-			break;
-
-		case 'h':
-			holeUp();
-			break;
-
-		case 'i':
-			screenMessage("Ignite torch!\n");
-			if (g_context->_location->_context == CTX_DUNGEON) {
-				if (!g_context->_party->lightTorch())
-					screenMessage("%cNone left!%c\n", FG_GREY, FG_WHITE);
-			} else screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
-			break;
-
-		case 'j':
-			jimmy();
-			break;
-
 		case 'k':
 			if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_KLIMB)) {
 				if (g_context->_transportContext == TRANSPORT_BALLOON) {
@@ -1336,34 +1309,6 @@ void castSpell(int player) {
 	}
 }
 
-void fire() {
-	if (g_context->_transportContext != TRANSPORT_SHIP) {
-		screenMessage("%cFire What?%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	screenMessage("Fire Cannon!\nDir: ");
-	Direction dir = gameGetDirection();
-
-	if (dir == DIR_NONE)
-		return;
-
-	// can only fire broadsides
-	int broadsidesDirs = dirGetBroadsidesDirs(g_context->_party->getDirection());
-	if (!DIR_IN_MASK(dir, broadsidesDirs)) {
-		screenMessage("%cBroadsides Only!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	// nothing (not even mountains!) can block cannonballs
-	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), broadsidesDirs, g_context->_location->_coords,
-	                           1, 3, NULL, false);
-	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
-		if (fireAt(*i, true))
-			return;
-	}
-}
-
 bool fireAt(const Coords &coords, bool originAvatar) {
 	bool validObject = false;
 	bool hitsAvatar = false;
@@ -1422,140 +1367,6 @@ bool fireAt(const Coords &coords, bool originAvatar) {
 	return objectHit;
 }
 
-/**
- * Get the chest at the current x,y of the current context for player 'player'
- */
-void getChest(int player) {
-	screenMessage("Get Chest!\n");
-
-	if (g_context->_party->isFlying()) {
-		screenMessage("%cDrift only!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	// first check to see if a chest exists at the current location
-	// if one exists, prompt the player for the opener, if necessary
-	MapCoords coords;
-	g_context->_location->getCurrentPosition(&coords);
-	const Tile *tile = g_context->_location->_map->tileTypeAt(coords, WITH_GROUND_OBJECTS);
-
-	/* get the object for the chest, if it is indeed an object */
-	Object *obj = g_context->_location->_map->objectAt(coords);
-	if (obj && !obj->getTile().getTileType()->isChest())
-		obj = NULL;
-
-	if (tile->isChest() || obj) {
-		// if a spell was cast to open this chest,
-		// player will equal -2, otherwise player
-		// will default to -1 or the defult character
-		// number if one was earlier specified
-		if (player == -1) {
-			screenMessage("Who opens? ");
-			player = gameGetPlayer(false, true);
-		}
-		if (player == -1)
-			return;
-
-		if (obj)
-			g_context->_location->_map->removeObject(obj);
-		else {
-			TileId newTile = g_context->_location->getReplacementTile(coords, tile);
-			g_context->_location->_map->_annotations->add(coords, newTile, false , true);
-		}
-
-		// see if the chest is trapped and handle it
-		getChestTrapHandler(player);
-
-		screenMessage("The Chest Holds: %d Gold\n", g_context->_party->getChest());
-
-		screenPrompt();
-
-		if (isCity(g_context->_location->_map) && obj == NULL)
-			g_context->_party->adjustKarma(KA_STOLE_CHEST);
-	} else {
-		screenMessage("%cNot Here!%c\n", FG_GREY, FG_WHITE);
-	}
-}
-
-/**
- * Called by getChest() to handle possible traps on chests
- **/
-bool getChestTrapHandler(int player) {
-	TileEffect trapType;
-	int randNum = xu4_random(4);
-
-	/* Do we use u4dos's way of trap-determination, or the original intended way? */
-	int passTest = (settings._enhancements && settings._enhancementsOptions._c64chestTraps) ?
-	               (xu4_random(2) == 0) : /* xu4-enhanced */
-	               ((randNum & 1) == 0); /* u4dos original way (only allows even numbers through, so only acid and poison show) */
-
-	/* Chest is trapped! 50/50 chance */
-	if (passTest) {
-		/* Figure out which trap the chest has */
-		switch (randNum & xu4_random(4)) {
-		case 0:
-			trapType = EFFECT_FIRE;
-			break;   /* acid trap (56% chance - 9/16) */
-		case 1:
-			trapType = EFFECT_SLEEP;
-			break;  /* sleep trap (19% chance - 3/16) */
-		case 2:
-			trapType = EFFECT_POISON;
-			break; /* poison trap (19% chance - 3/16) */
-		case 3:
-			trapType = EFFECT_LAVA;
-			break;   /* bomb trap (6% chance - 1/16) */
-		default:
-			trapType = EFFECT_FIRE;
-			break;
-		}
-
-		/* apply the effects from the trap */
-		if (trapType == EFFECT_FIRE)
-			screenMessage("%cAcid%c Trap!\n", FG_RED, FG_WHITE);
-		else if (trapType == EFFECT_POISON)
-			screenMessage("%cPoison%c Trap!\n", FG_GREEN, FG_WHITE);
-		else if (trapType == EFFECT_SLEEP)
-			screenMessage("%cSleep%c Trap!\n", FG_PURPLE, FG_WHITE);
-		else if (trapType == EFFECT_LAVA)
-			screenMessage("%cBomb%c Trap!\n", FG_RED, FG_WHITE);
-
-		// player is < 0 during the 'O'pen spell (immune to traps)
-		//
-		// if the chest was opened by a PC, see if the trap was
-		// evaded by testing the PC's dex
-		//
-		if ((player >= 0) &&
-		        (g_ultima->_saveGame->_players[player]._dex + 25 < xu4_random(100))) {
-			if (trapType == EFFECT_LAVA) /* bomb trap */
-				g_context->_party->applyEffect(trapType);
-			else g_context->_party->member(player)->applyEffect(trapType);
-		} else screenMessage("Evaded!\n");
-
-		return true;
-	}
-
-	return false;
-}
-
-void holeUp() {
-	screenMessage("Hole up & Camp!\n");
-
-	if (!(g_context->_location->_context & (CTX_WORLDMAP | CTX_DUNGEON))) {
-		screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	if (g_context->_transportContext != TRANSPORT_FOOT) {
-		screenMessage("%cOnly on foot!%c\n", FG_GREY, FG_WHITE);
-		return;
-	}
-
-	CombatController *cc = new CampController();
-	cc->init(NULL);
-	cc->begin();
-}
-
 void GameController::initMoons() {
 	int trammelphase = g_ultima->_saveGame->_trammelPhase,
 	    feluccaphase = g_ultima->_saveGame->_feluccaPhase;
@@ -1690,7 +1501,7 @@ void GameController::avatarMoved(MoveEvent &event) {
 					openAt(new_coords);
 					event._result = (MoveResult)(MOVE_SUCCEEDED | MOVE_END_TURN);
 				} else if (tile->getTileType()->isLockedDoor()) {
-					jimmyAt(new_coords);
+					g_debugger->jimmyAt(new_coords);
 					event._result = (MoveResult)(MOVE_SUCCEEDED | MOVE_END_TURN);
 				} /*else if (mapPersonAt(c->location->map, new_coords) != NULL) {
                     talkAtCoord(newx, newy, 1, NULL);
@@ -1782,46 +1593,6 @@ void GameController::avatarMovedInDungeon(MoveEvent &event) {
 	}
 }
 
-void jimmy() {
-	screenMessage("Jimmy: ");
-	Direction dir = gameGetDirection();
-
-	if (dir == DIR_NONE)
-		return;
-
-	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
-	                           1, 1, NULL, true);
-	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
-		if (jimmyAt(*i))
-			return;
-	}
-
-	screenMessage("%cJimmy what?%c\n", FG_GREY, FG_WHITE);
-}
-
-/**
- * Attempts to jimmy a locked door at map coordinates x,y.  The locked
- * door is replaced by a permanent annotation of an unlocked door
- * tile.
- */
-bool jimmyAt(const Coords &coords) {
-	MapTile *tile = g_context->_location->_map->tileAt(coords, WITH_OBJECTS);
-
-	if (!tile->getTileType()->isLockedDoor())
-		return false;
-
-	if (g_ultima->_saveGame->_keys) {
-		Tile *door = g_context->_location->_map->_tileset->getByName("door");
-		ASSERT(door, "no door tile found in tileset");
-		g_ultima->_saveGame->_keys--;
-		g_context->_location->_map->_annotations->add(coords, door->getId());
-		screenMessage("\nUnlocked!\n");
-	} else
-		screenMessage("%cNo keys left!%c\n", FG_GREY, FG_WHITE);
-
-	return true;
-}
-
 void opendoor() {
 	///  XXX: Pressing "o" should close any open door.
 
diff --git a/engines/ultima/ultima4/game/game.h b/engines/ultima/ultima4/game/game.h
index 80eb69c131..cdb9dcebe6 100644
--- a/engines/ultima/ultima4/game/game.h
+++ b/engines/ultima/ultima4/game/game.h
@@ -239,10 +239,6 @@ void castSpell(int player = -1);
 void gameSpellEffect(int spell, int player, Sound sound);
 
 /* action functions */
-void fire();
-void getChest(int player = -1);
-void holeUp();
-void jimmy();
 void opendoor();
 bool gamePeerCity(int city, void *data);
 void peer(bool useGem = true);
diff --git a/engines/ultima/ultima4/game/spell.cpp b/engines/ultima/ultima4/game/spell.cpp
index 381adaebe2..968cd4c10d 100644
--- a/engines/ultima/ultima4/game/spell.cpp
+++ b/engines/ultima/ultima4/game/spell.cpp
@@ -21,25 +21,26 @@
  */
 
 #include "ultima/ultima4/ultima4.h"
+#include "ultima/ultima4/core/settings.h"
+#include "ultima/ultima4/core/debugger.h"
+#include "ultima/ultima4/core/utils.h"
+#include "ultima/ultima4/events/event.h"
+#include "ultima/ultima4/game/game.h"
 #include "ultima/ultima4/game/spell.h"
+#include "ultima/ultima4/game/context.h"
+#include "ultima/ultima4/game/creature.h"
+#include "ultima/ultima4/game/moongate.h"
+#include "ultima/ultima4/game/player.h"
+#include "ultima/ultima4/gfx/screen.h"
 #include "ultima/ultima4/map/annotation.h"
 #include "ultima/ultima4/map/combat.h"
-#include "ultima/ultima4/game/context.h"
 #include "ultima/ultima4/map/direction.h"
 #include "ultima/ultima4/map/dungeon.h"
-#include "ultima/ultima4/events/event.h"
-#include "ultima/ultima4/game/game.h"
 #include "ultima/ultima4/map/location.h"
 #include "ultima/ultima4/map/map.h"
 #include "ultima/ultima4/map/mapmgr.h"
-#include "ultima/ultima4/game/creature.h"
-#include "ultima/ultima4/game/moongate.h"
-#include "ultima/ultima4/game/player.h"
-#include "ultima/ultima4/gfx/screen.h"
-#include "ultima/ultima4/core/settings.h"
 #include "ultima/ultima4/map/tile.h"
 #include "ultima/ultima4/map/tileset.h"
-#include "ultima/ultima4/core/utils.h"
 
 namespace Ultima {
 namespace Ultima4 {
@@ -658,7 +659,7 @@ static int spellNegate(int unused) {
 }
 
 static int spellOpen(int unused) {
-	getChest(-2);   // HACK: -2 will not prompt for opener
+	g_debugger->getChest();
 	return 1;
 }
 
diff --git a/engines/ultima/ultima4/map/combat.cpp b/engines/ultima/ultima4/map/combat.cpp
index 49d8f4be34..f51a824f1f 100644
--- a/engines/ultima/ultima4/map/combat.cpp
+++ b/engines/ultima/ultima4/map/combat.cpp
@@ -21,29 +21,30 @@
  */
 
 #include "ultima/ultima4/ultima4.h"
-#include "ultima/ultima4/map/combat.h"
-#include "ultima/ultima4/map/annotation.h"
+#include "ultima/ultima4/core/debugger.h"
+#include "ultima/ultima4/core/settings.h"
+#include "ultima/ultima4/core/utils.h"
+#include "ultima/ultima4/events/event.h"
 #include "ultima/ultima4/game/context.h"
 #include "ultima/ultima4/game/creature.h"
 #include "ultima/ultima4/game/death.h"
-#include "ultima/ultima4/map/dungeon.h"
-#include "ultima/ultima4/events/event.h"
 #include "ultima/ultima4/game/game.h"
 #include "ultima/ultima4/game/item.h"
-#include "ultima/ultima4/map/location.h"
-#include "ultima/ultima4/map/mapmgr.h"
-#include "ultima/ultima4/map/movement.h"
 #include "ultima/ultima4/game/names.h"
 #include "ultima/ultima4/game/object.h"
 #include "ultima/ultima4/game/player.h"
 #include "ultima/ultima4/game/portal.h"
-#include "ultima/ultima4/gfx/screen.h"
-#include "ultima/ultima4/core/settings.h"
 #include "ultima/ultima4/game/spell.h"
 #include "ultima/ultima4/game/stats.h"
-#include "ultima/ultima4/map/tileset.h"
-#include "ultima/ultima4/core/utils.h"
 #include "ultima/ultima4/game/weapon.h"
+#include "ultima/ultima4/gfx/screen.h"
+#include "ultima/ultima4/map/combat.h"
+#include "ultima/ultima4/map/annotation.h"
+#include "ultima/ultima4/map/dungeon.h"
+#include "ultima/ultima4/map/location.h"
+#include "ultima/ultima4/map/mapmgr.h"
+#include "ultima/ultima4/map/movement.h"
+#include "ultima/ultima4/map/tileset.h"
 #include "ultima/shared/std/containers.h"
 #include "common/system.h"
 
@@ -923,7 +924,7 @@ bool CombatController::keyPressed(int key) {
 #endif
 	case 'g':
 		screenMessage("Get Chest!\n");
-		getChest(_focus);
+		g_debugger->getChest(_focus);
 		break;
 
 	case 'l':
diff --git a/engines/ultima/ultima4/meta_engine.cpp b/engines/ultima/ultima4/meta_engine.cpp
index a08ad06a52..24cf884e9c 100644
--- a/engines/ultima/ultima4/meta_engine.cpp
+++ b/engines/ultima/ultima4/meta_engine.cpp
@@ -47,6 +47,12 @@ static const KeybindingRecord KEYS[] = {
 	{ KEYBIND_BOARD, "BOARD", "Board", "board", "b", nullptr },
 	{ KEYBIND_CAST, "CAST", "Cast", "cast", "c", nullptr },
 	{ KEYBIND_ENTER, "ENTER", "Enter", "enter", "e", nullptr },
+	{ KEYBIND_FIRE, "FIRE", "Fire", "fire", "f", nullptr },
+	{ KEYBIND_GET, "GET", "Get Chest", "get", "g", nullptr },
+	{ KEYBIND_HOLE_UP, "HOLE-UP", "Hole Up", "hole", "h", nullptr },
+	{ KEYBIND_JIMMY, "JIMMY", "Jimmy", "jimmy", "j", nullptr },
+	{ KEYBIND_IGNITE, "IGNITE", "Ignite", "ignite", "i", nullptr },
+
 	{ KEYBIND_PASS, "PASS", "Pass", "pass", "SPACE", nullptr },
 
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
diff --git a/engines/ultima/ultima4/meta_engine.h b/engines/ultima/ultima4/meta_engine.h
index 2875c1b398..65a015d418 100644
--- a/engines/ultima/ultima4/meta_engine.h
+++ b/engines/ultima/ultima4/meta_engine.h
@@ -31,6 +31,8 @@ namespace Ultima4 {
 enum KeybindingAction {
 	KEYBIND_UP, KEYBIND_DOWN, KEYBIND_LEFT, KEYBIND_RIGHT,
 	KEYBIND_ATTACK, KEYBIND_BOARD, KEYBIND_CAST, KEYBIND_ENTER,
+	KEYBIND_FIRE, KEYBIND_GET, KEYBIND_HOLE_UP, KEYBIND_IGNITE,
+	KEYBIND_JIMMY,
 	KEYBIND_PASS,
 
 	KEYBIND_NONE




More information about the Scummvm-git-logs mailing list