1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/ultima4/core/debugger.h"
24 #include "ultima/ultima4/core/utils.h"
25 #include "ultima/ultima4/controllers/alpha_action_controller.h"
26 #include "ultima/ultima4/controllers/camp_controller.h"
27 #include "ultima/ultima4/controllers/read_choice_controller.h"
28 #include "ultima/ultima4/controllers/read_dir_controller.h"
29 #include "ultima/ultima4/controllers/ztats_controller.h"
30 #include "ultima/ultima4/game/armor.h"
31 #include "ultima/ultima4/game/context.h"
32 #include "ultima/ultima4/game/game.h"
33 #include "ultima/ultima4/game/item.h"
34 #include "ultima/ultima4/game/moongate.h"
35 #include "ultima/ultima4/game/player.h"
36 #include "ultima/ultima4/game/portal.h"
37 #include "ultima/ultima4/game/weapon.h"
38 #include "ultima/ultima4/gfx/screen.h"
39 #include "ultima/ultima4/map/annotation.h"
40 #include "ultima/ultima4/map/city.h"
41 #include "ultima/ultima4/map/mapmgr.h"
42 #include "ultima/ultima4/views/dungeonview.h"
43 #include "ultima/ultima4/views/stats.h"
44 #include "ultima/ultima4/ultima4.h"
45 #include "common/system.h"
46 
47 namespace Ultima {
48 namespace Ultima4 {
49 
50 Debugger *g_debugger;
51 
Debugger()52 Debugger::Debugger() : Shared::Debugger() {
53 	g_debugger = this;
54 	_collisionOverride = false;
55 	_disableCombat = false;
56 	_disableHunger = false;
57 	_dontEndTurn = false;
58 
59 	registerCmd("move", WRAP_METHOD(Debugger, cmdMove));
60 	registerCmd("attack", WRAP_METHOD(Debugger, cmdAttack));
61 	registerCmd("board", WRAP_METHOD(Debugger, cmdBoard));
62 	registerCmd("camp", WRAP_METHOD(Debugger, cmdCamp));
63 	registerCmd("cast", WRAP_METHOD(Debugger, cmdCastSpell));
64 	registerCmd("spell", WRAP_METHOD(Debugger, cmdCastSpell));
65 	registerCmd("climb", WRAP_METHOD(Debugger, cmdClimb));
66 	registerCmd("descend", WRAP_METHOD(Debugger, cmdDescend));
67 	registerCmd("enter", WRAP_METHOD(Debugger, cmdEnter));
68 	registerCmd("exit", WRAP_METHOD(Debugger, cmdExit));
69 	registerCmd("fire", WRAP_METHOD(Debugger, cmdFire));
70 	registerCmd("get", WRAP_METHOD(Debugger, cmdGetChest));
71 	registerCmd("ignite", WRAP_METHOD(Debugger, cmdIgnite));
72 	registerCmd("interact", WRAP_METHOD(Debugger, cmdInteract));
73 	registerCmd("jimmy", WRAP_METHOD(Debugger, cmdJimmy));
74 	registerCmd("locate", WRAP_METHOD(Debugger, cmdLocate));
75 	registerCmd("mix", WRAP_METHOD(Debugger, cmdMixReagents));
76 	registerCmd("open", WRAP_METHOD(Debugger, cmdOpenDoor));
77 	registerCmd("order", WRAP_METHOD(Debugger, cmdNewOrder));
78 	registerCmd("party", WRAP_METHOD(Debugger, cmdParty));
79 	registerCmd("pass", WRAP_METHOD(Debugger, cmdPass));
80 	registerCmd("peer", WRAP_METHOD(Debugger, cmdPeer));
81 	registerCmd("quitAndSave", WRAP_METHOD(Debugger, cmdQuitAndSave));
82 	registerCmd("ready", WRAP_METHOD(Debugger, cmdReadyWeapon));
83 	registerCmd("search", WRAP_METHOD(Debugger, cmdSearch));
84 	registerCmd("stats", WRAP_METHOD(Debugger, cmdStats));
85 	registerCmd("talk", WRAP_METHOD(Debugger, cmdTalk));
86 	registerCmd("use", WRAP_METHOD(Debugger, cmdUse));
87 	registerCmd("wear", WRAP_METHOD(Debugger, cmdWearArmor));
88 	registerCmd("yell", WRAP_METHOD(Debugger, cmdYell));
89 
90 	registerCmd("speed", WRAP_METHOD(Debugger, cmdSpeed));
91 	registerCmd("combat_speed", WRAP_METHOD(Debugger, cmdCombatSpeed));
92 
93 	registerCmd("3d", WRAP_METHOD(Debugger, cmd3d));
94 	registerCmd("abyss", WRAP_METHOD(Debugger, cmdAbyss));
95 	registerCmd("collisions", WRAP_METHOD(Debugger, cmdCollisions));
96 	registerCmd("combat", WRAP_METHOD(Debugger, cmdCombat));
97 	registerCmd("companions", WRAP_METHOD(Debugger, cmdCompanions));
98 	registerCmd("destroy", WRAP_METHOD(Debugger, cmdDestroy));
99 	registerCmd("destroy_creatures", WRAP_METHOD(Debugger, cmdDestroyCreatures));
100 	registerCmd("dungeon", WRAP_METHOD(Debugger, cmdDungeon));
101 	registerCmd("equipment", WRAP_METHOD(Debugger, cmdEquipment));
102 	registerCmd("exit", WRAP_METHOD(Debugger, cmdExit));
103 	registerCmd("flee", WRAP_METHOD(Debugger, cmdFlee));
104 	registerCmd("fullstats", WRAP_METHOD(Debugger, cmdFullStats));
105 	registerCmd("gate", WRAP_METHOD(Debugger, cmdGate));
106 	registerCmd("goto", WRAP_METHOD(Debugger, cmdGoto));
107 	registerCmd("hunger", WRAP_METHOD(Debugger, cmdHunger));
108 	registerCmd("items", WRAP_METHOD(Debugger, cmdItems));
109 	registerCmd("karma", WRAP_METHOD(Debugger, cmdKarma));
110 	registerCmd("leave", WRAP_METHOD(Debugger, cmdLeave));
111 	registerCmd("location", WRAP_METHOD(Debugger, cmdLocation));
112 	registerCmd("lordbritish", WRAP_METHOD(Debugger, cmdLorddBritish));
113 	registerCmd("mixtures", WRAP_METHOD(Debugger, cmdMixtures));
114 	registerCmd("moon", WRAP_METHOD(Debugger, cmdMoon));
115 	registerCmd("opacity", WRAP_METHOD(Debugger, cmdOpacity));
116 	registerCmd("overhead", WRAP_METHOD(Debugger, cmdOverhead));
117 	registerCmd("reagents", WRAP_METHOD(Debugger, cmdReagents));
118 	registerCmd("summon", WRAP_METHOD(Debugger, cmdSummon));
119 	registerCmd("torch", WRAP_METHOD(Debugger, cmdTorch));
120 	registerCmd("transport", WRAP_METHOD(Debugger, cmdTransport));
121 	registerCmd("triggers", WRAP_METHOD(Debugger, cmdListTriggers));
122 	registerCmd("up", WRAP_METHOD(Debugger, cmdUp));
123 	registerCmd("down", WRAP_METHOD(Debugger, cmdDown));
124 	registerCmd("virtue", WRAP_METHOD(Debugger, cmdVirtue));
125 	registerCmd("wind", WRAP_METHOD(Debugger, cmdWind));
126 }
127 
~Debugger()128 Debugger::~Debugger() {
129 	g_debugger = nullptr;
130 }
131 
print(const char * fmt,...)132 void Debugger::print(const char *fmt, ...) {
133 	// Format the string
134 	va_list va;
135 	va_start(va, fmt);
136 	Common::String str = Common::String::vformat(fmt, va);
137 	va_end(va);
138 
139 	printN("%s\n", str.c_str());
140 }
141 
printN(const char * fmt,...)142 void Debugger::printN(const char *fmt, ...) {
143 	// Format the string
144 	va_list va;
145 	va_start(va, fmt);
146 	Common::String str = Common::String::vformat(fmt, va);
147 	va_end(va);
148 
149 	if (isDebuggerActive()) {
150 		// Strip off any color special characters that aren't
151 		// relevant for showing the text in the debugger
152 		Common::String s;
153 		for (Common::String::iterator it = str.begin(); it != str.end(); ++it) {
154 			if (*it >= ' ' || *it == '\n')
155 				s += *it;
156 		}
157 
158 		debugPrintf("%s", s.c_str());
159 	} else {
160 		g_screen->screenMessage("%s", str.c_str());
161 	}
162 }
163 
prompt()164 void Debugger::prompt() {
165 	if (isDebuggerActive())
166 		g_screen->screenPrompt();
167 }
168 
handleCommand(int argc,const char ** argv,bool & keepRunning)169 bool Debugger::handleCommand(int argc, const char **argv, bool &keepRunning) {
170 	static const char *DUNGEON_DISALLOWED[] = {
171 		"attack", "board", "enter", "fire", "jimmy", "locate",
172 		"open", "talk", "exit", "yell", nullptr
173 	};
174 	static const char *COMBAT_DISALLOWED[] = {
175 		"board", "climb", "descend", "enter", "exit", "fire", "hole",
176 		"ignite", "jimmy", "mix", "order", "open", "peer", "quitAndSave",
177 		"search", "wear", "yell", nullptr
178 	};
179 
180 	if (g_context && g_context->_location) {
181 		int ctx = g_context->_location->_context;
182 		if (ctx & (CTX_DUNGEON | CTX_COMBAT)) {
183 			Common::String method = argv[0];
184 			const char *const *mth = (ctx & CTX_COMBAT) ?
185 				COMBAT_DISALLOWED : DUNGEON_DISALLOWED;
186 
187 			for (; *mth; ++mth) {
188 				if (method.equalsIgnoreCase(*mth)) {
189 					print("%cNot here!%c", FG_GREY, FG_WHITE);
190 					g_context->_location->_turnCompleter->finishTurn();
191 					keepRunning = false;
192 					return true;
193 				}
194 			}
195 		}
196 	}
197 
198 	bool result = Shared::Debugger::handleCommand(argc, argv, keepRunning);
199 
200 	if (result) {
201 		Controller *ctl = eventHandler->getController();
202 
203 		if (g_context)
204 			g_context->_lastCommandTime = g_system->getMillis();
205 
206 		if (!isActive() && !_dontEndTurn) {
207 			GameController *gc = dynamic_cast<GameController *>(ctl);
208 			CombatController *cc = dynamic_cast<CombatController *>(ctl);
209 
210 			if (gc)
211 				gc->finishTurn();
212 			else if (cc)
213 				cc->finishTurn();
214 		} else if (_dontEndTurn) {
215 			if (ctl == g_game || ctl == g_combat) {
216 				assert(g_context);
217 				g_context->_location->_turnCompleter->finishTurn();
218 			}
219 		}
220 	}
221 
222 	_dontEndTurn = false;
223 	return result;
224 }
225 
getChest(int player)226 void Debugger::getChest(int player) {
227 	Common::String param = Common::String::format("%d", player);
228 	const char *argv[2] = { "get", param.c_str() };
229 
230 	cmdGetChest(2, argv);
231 }
232 
cmdMove(int argc,const char ** argv)233 bool Debugger::cmdMove(int argc, const char **argv) {
234 	Direction dir;
235 
236 	if (argc == 2) {
237 		dir = directionFromName(argv[1]);
238 	} else {
239 		print("move <direction>");
240 		return isDebuggerActive();
241 	}
242 
243 	Common::String priorMap = g_context->_location->_map->_fname;
244 	MoveResult retval = g_context->_location->move(dir, true);
245 
246 	// horse doubles speed (make sure we're on the same map as the previous move first)
247 	if (retval & (MOVE_SUCCEEDED | MOVE_SLOWED) &&
248 		(g_context->_transportContext == TRANSPORT_HORSE) && g_context->_horseSpeed) {
249 		// to give it a smooth look of movement
250 		gameUpdateScreen();
251 		if (priorMap == g_context->_location->_map->_fname)
252 			g_context->_location->move(dir, false);
253 	}
254 
255 	// Let the movement handler decide to end the turn
256 	bool endTurn = (retval & MOVE_END_TURN);
257 	if (!endTurn)
258 		dontEndTurn();
259 
260 	return false;
261 }
262 
cmdAttack(int argc,const char ** argv)263 bool Debugger::cmdAttack(int argc, const char **argv) {
264 	if (argc < 2 && isDebuggerActive()) {
265 		print("attack <direction> [distance]");
266 		return true;
267 	}
268 
269 	Direction dir = (argc >= 2) ? directionFromName(argv[1]) : DIR_NONE;
270 	int range = (argc >= 3) ? strToInt(argv[2]) : -1;
271 
272 	CombatController *cc = dynamic_cast<CombatController *>(eventHandler->getController());
273 	GameController *gc = dynamic_cast<GameController *>(eventHandler->getController());
274 
275 	if (cc)
276 		cc->attack(dir, range);
277 	else if (gc)
278 		gc->attack(dir);
279 
280 	return isDebuggerActive();
281 }
282 
cmdBoard(int argc,const char ** argv)283 bool Debugger::cmdBoard(int argc, const char **argv) {
284 	if (g_context->_transportContext != TRANSPORT_FOOT) {
285 		print("Board: %cCan't!%c", FG_GREY, FG_WHITE);
286 		return isDebuggerActive();
287 	}
288 
289 	Object *obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
290 	if (!obj) {
291 		print("%cBoard What?%c", FG_GREY, FG_WHITE);
292 		return isDebuggerActive();
293 	}
294 
295 	const Tile *tile = obj->getTile().getTileType();
296 	if (tile->isShip()) {
297 		print("Board Frigate!");
298 		if (g_context->_lastShip != obj)
299 			g_context->_party->setShipHull(50);
300 	} else if (tile->isHorse())
301 		print("Mount Horse!");
302 	else if (tile->isBalloon())
303 		print("Board Balloon!");
304 	else {
305 		print("%cBoard What?%c", FG_GREY, FG_WHITE);
306 		return isDebuggerActive();
307 	}
308 
309 	g_context->_party->setTransport(obj->getTile());
310 	g_context->_location->_map->removeObject(obj);
311 	return isDebuggerActive();
312 }
313 
cmdCastSpell(int argc,const char ** argv)314 bool Debugger::cmdCastSpell(int argc, const char **argv) {
315 	int player = -1;
316 	if (argc >= 2)
317 		player = strToInt(argv[1]);
318 
319 	print("Cast Spell!");
320 	if (isCombat()) {
321 		player = getCombatFocus();
322 	} else if (player == -1) {
323 		printN("Player: ");
324 		player = gameGetPlayer(false, true);
325 	}
326 	if (player == -1)
327 		return isDebuggerActive();
328 
329 	// get the spell to cast
330 	g_context->_stats->setView(STATS_MIXTURES);
331 	printN("Spell: ");
332 #ifdef IOS_ULTIMA4
333 	// ### Put the iPad thing too.
334 	U4IOS::IOSCastSpellHelper castSpellController;
335 #endif
336 	int spell;
337 	if (argc == 3) {
338 		printN("Spell: ");
339 		if (Common::isAlpha(argv[2][0])) {
340 			spell = tolower(argv[2][0]) - 'a';
341 		} else {
342 			spell = -1;
343 		}
344 	} else {
345 		spell = AlphaActionController::get('z', "Spell: ");
346 	}
347 
348 	if (spell == -1) {
349 		print("");
350 		return isDebuggerActive();
351 	}
352 
353 	print("%s!", g_spells->spellGetName(spell)); // Prints spell name at prompt
354 
355 	g_context->_stats->setView(STATS_PARTY_OVERVIEW);
356 
357 	// If we can't really cast this spell, skip the extra parameters
358 	if (g_spells->spellCheckPrerequisites(spell, player) != CASTERR_NOERROR) {
359 		gameCastSpell(spell, player, 0);
360 		return isDebuggerActive();
361 	}
362 
363 	// Get the final parameters for the spell
364 	switch (g_spells->spellGetParamType(spell)) {
365 	case Spell::PARAM_NONE:
366 		gameCastSpell(spell, player, 0);
367 		break;
368 
369 	case Spell::PARAM_PHASE: {
370 		printN("To Phase: ");
371 #ifdef IOS_ULTIMA4
372 		U4IOS::IOSConversationChoiceHelper choiceController;
373 		choiceController.fullSizeChoicePanel();
374 		choiceController.updateGateSpellChoices();
375 #endif
376 		int choice = ReadChoiceController::get("12345678 \033\n");
377 		if (choice < '1' || choice > '8')
378 			print("None");
379 		else {
380 			print("");
381 			gameCastSpell(spell, player, choice - '1');
382 		}
383 		break;
384 	}
385 
386 	case Spell::PARAM_PLAYER: {
387 		printN("Who: ");
388 		int subject = gameGetPlayer(true, false);
389 		if (subject != -1)
390 			gameCastSpell(spell, player, subject);
391 		break;
392 	}
393 
394 	case Spell::PARAM_DIR:
395 		if (g_context->_location->_context == CTX_DUNGEON)
396 			gameCastSpell(spell, player, g_ultima->_saveGame->_orientation);
397 		else {
398 			printN("Dir: ");
399 			Direction dir = gameGetDirection();
400 			if (dir != DIR_NONE)
401 				gameCastSpell(spell, player, (int)dir);
402 		}
403 		break;
404 
405 	case Spell::PARAM_TYPEDIR: {
406 		printN("Energy type? ");
407 #ifdef IOS_ULTIMA4
408 		U4IOS::IOSConversationChoiceHelper choiceController;
409 		choiceController.fullSizeChoicePanel();
410 		choiceController.updateEnergyFieldSpellChoices();
411 #endif
412 		EnergyFieldType fieldType = ENERGYFIELD_NONE;
413 		char key = ReadChoiceController::get("flps \033\n\r");
414 		switch (key) {
415 		case 'f':
416 			fieldType = ENERGYFIELD_FIRE;
417 			break;
418 		case 'l':
419 			fieldType = ENERGYFIELD_LIGHTNING;
420 			break;
421 		case 'p':
422 			fieldType = ENERGYFIELD_POISON;
423 			break;
424 		case 's':
425 			fieldType = ENERGYFIELD_SLEEP;
426 			break;
427 		default:
428 			break;
429 		}
430 
431 		if (fieldType != ENERGYFIELD_NONE) {
432 			print("");
433 
434 			Direction dir;
435 			if (g_context->_location->_context == CTX_DUNGEON)
436 				dir = (Direction)g_ultima->_saveGame->_orientation;
437 			else {
438 				printN("Dir: ");
439 				dir = gameGetDirection();
440 			}
441 
442 			if (dir != DIR_NONE) {
443 
444 				/* Need to pack both dir and fieldType into param */
445 				int param = fieldType << 4;
446 				param |= (int)dir;
447 
448 				gameCastSpell(spell, player, param);
449 			}
450 		} else {
451 			/* Invalid input here = spell failure */
452 			print("Failed!");
453 
454 			/*
455 			 * Confirmed both mixture loss and mp loss in this situation in the
456 			 * original Ultima IV (at least, in the Amiga version.)
457 			 */
458 			 //c->saveGame->_mixtures[castSpell]--;
459 			g_context->_party->member(player)->adjustMp(
460 				-g_spells->spellGetRequiredMP(spell));
461 		}
462 		break;
463 	}
464 
465 	case Spell::PARAM_FROMDIR: {
466 		printN("From Dir: ");
467 		Direction dir = gameGetDirection();
468 		if (dir != DIR_NONE)
469 			gameCastSpell(spell, player, (int)dir);
470 		break;
471 	}
472 	}
473 
474 	return false;
475 }
476 
cmdCamp(int argc,const char ** argv)477 bool Debugger::cmdCamp(int argc, const char **argv) {
478 	print("Hole up & Camp!");
479 
480 	if (!(g_context->_location->_context & (CTX_WORLDMAP | CTX_DUNGEON))) {
481 		print("%cNot here!%c", FG_GREY, FG_WHITE);
482 		return isDebuggerActive();
483 	}
484 
485 	if (g_context->_transportContext != TRANSPORT_FOOT) {
486 		print("%cOnly on foot!%c", FG_GREY, FG_WHITE);
487 		return isDebuggerActive();
488 	}
489 
490 	CombatController *cc = new CampController();
491 	cc->init(nullptr);
492 	cc->begin();
493 
494 	return isDebuggerActive();
495 }
496 
cmdClimb(int argc,const char ** argv)497 bool Debugger::cmdClimb(int argc, const char **argv) {
498 	if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_KLIMB)) {
499 		if (g_context->_transportContext == TRANSPORT_BALLOON) {
500 				g_ultima->_saveGame->_balloonState = 1;
501 				g_context->_opacity = 0;
502 			print("Klimb altitude");
503 		} else
504 			print("%cKlimb what?%c", FG_GREY, FG_WHITE);
505 	}
506 
507 	return isDebuggerActive();
508 }
509 
cmdDescend(int argc,const char ** argv)510 bool Debugger::cmdDescend(int argc, const char **argv) {
511 	// unload the map for the second level of Lord British's Castle. The reason
512 	// why is that Lord British's farewell is dependent on the number of party members.
513 	// Instead of just redoing the dialog, it's a bit severe, but easier to unload the
514 	// whole level.
515 	bool cleanMap = (g_context->_party->size() == 1 && g_context->_location->_map->_id == 100);
516 	if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_DESCEND)) {
517 		if (g_context->_transportContext == TRANSPORT_BALLOON) {
518 			print("Land Balloon");
519 			if (!g_context->_party->isFlying())
520 				print("%cAlready Landed!%c", FG_GREY, FG_WHITE);
521 			else if (g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_OBJECTS)->canLandBalloon()) {
522 				g_ultima->_saveGame->_balloonState = 0;
523 				g_context->_opacity = 1;
524 			} else {
525 				print("%cNot Here!%c", FG_GREY, FG_WHITE);
526 			}
527 		} else {
528 			print("%cDescend what?%c", FG_GREY, FG_WHITE);
529 		}
530 	} else {
531 		if (cleanMap)
532 			mapMgr->unloadMap(100);
533 	}
534 
535 	return isDebuggerActive();
536 }
537 
cmdEnter(int argc,const char ** argv)538 bool Debugger::cmdEnter(int argc, const char **argv) {
539 	if (!usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_ENTER)) {
540 		if (!g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER))
541 			print("%cEnter what?%c", FG_GREY, FG_WHITE);
542 	} else {
543 		dontEndTurn();
544 	}
545 
546 	return isDebuggerActive();
547 }
548 
cmdExit(int argc,const char ** argv)549 bool Debugger::cmdExit(int argc, const char **argv) {
550 	if ((g_context->_transportContext != TRANSPORT_FOOT) && !g_context->_party->isFlying()) {
551 		Object *obj = g_context->_location->_map->addObject(g_context->_party->getTransport(), g_context->_party->getTransport(), g_context->_location->_coords);
552 		if (g_context->_transportContext == TRANSPORT_SHIP)
553 			g_context->_lastShip = obj;
554 
555 		Tile *avatar = g_context->_location->_map->_tileSet->getByName("avatar");
556 		assertMsg(avatar, "no avatar tile found in tileset");
557 
558 		g_context->_party->setTransport(avatar->getId());
559 		g_context->_horseSpeed = 0;
560 		print("X-it");
561 	} else {
562 		print("%cX-it What?%c", FG_GREY, FG_WHITE);
563 	}
564 
565 	return isDebuggerActive();
566 }
567 
cmdFire(int argc,const char ** argv)568 bool Debugger::cmdFire(int argc, const char **argv) {
569 	if (g_context->_transportContext != TRANSPORT_SHIP) {
570 		print("%cFire What?%c", FG_GREY, FG_WHITE);
571 		return isDebuggerActive();
572 	}
573 
574 	printN("Fire Cannon!\nDir: ");
575 	Direction dir = gameGetDirection();
576 
577 	if (dir == DIR_NONE)
578 		return isDebuggerActive();
579 
580 	// can only fire broadsides
581 	int broadsidesDirs = dirGetBroadsidesDirs(g_context->_party->getDirection());
582 	if (!DIR_IN_MASK(dir, broadsidesDirs)) {
583 		print("%cBroadsides Only!%c", FG_GREY, FG_WHITE);
584 		return isDebuggerActive();
585 	}
586 
587 	// nothing (not even mountains!) can block cannonballs
588 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), broadsidesDirs, g_context->_location->_coords,
589 		1, 3, nullptr, false);
590 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
591 		if (fireAt(*i, true))
592 			return isDebuggerActive();
593 	}
594 
595 	return isDebuggerActive();
596 }
597 
cmdGetChest(int argc,const char ** argv)598 bool Debugger::cmdGetChest(int argc, const char **argv) {
599 	int player = -1;
600 	if (argc == 2)
601 		player = strToInt(argv[1]);
602 	else if (isCombat())
603 		player = getCombatFocus();
604 
605 	print("Get Chest!");
606 
607 	if (g_context->_party->isFlying()) {
608 		print("%cDrift only!%c", FG_GREY, FG_WHITE);
609 		return isDebuggerActive();
610 	}
611 
612 	// first check to see if a chest exists at the current location
613 	// if one exists, prompt the player for the opener, if necessary
614 	MapCoords coords;
615 	g_context->_location->getCurrentPosition(&coords);
616 	const Tile *tile = g_context->_location->_map->tileTypeAt(coords, WITH_GROUND_OBJECTS);
617 
618 	/* get the object for the chest, if it is indeed an object */
619 	Object *obj = g_context->_location->_map->objectAt(coords);
620 	if (obj && !obj->getTile().getTileType()->isChest())
621 		obj = nullptr;
622 
623 	if (tile->isChest() || obj) {
624 		// if a spell was cast to open this chest,
625 		// player will equal -2, otherwise player
626 		// will default to -1 or the defult character
627 		// number if one was earlier specified
628 		if (player == -1) {
629 			printN("Who opens? ");
630 			player = gameGetPlayer(false, true);
631 		}
632 		if (player == -1)
633 			return isDebuggerActive();
634 
635 		if (obj)
636 			g_context->_location->_map->removeObject(obj);
637 		else {
638 			TileId newTile = g_context->_location->getReplacementTile(coords, tile);
639 			g_context->_location->_map->_annotations->add(coords, newTile, false, true);
640 		}
641 
642 		// see if the chest is trapped and handle it
643 		getChestTrapHandler(player);
644 
645 		print("The Chest Holds: %d Gold", g_context->_party->getChest());
646 
647 		g_screen->screenPrompt();
648 
649 		if (isCity(g_context->_location->_map) && obj == nullptr)
650 			g_context->_party->adjustKarma(KA_STOLE_CHEST);
651 	} else {
652 		print("%cNot Here!%c", FG_GREY, FG_WHITE);
653 	}
654 
655 	return isDebuggerActive();
656 }
657 
cmdIgnite(int argc,const char ** argv)658 bool Debugger::cmdIgnite(int argc, const char **argv) {
659 	print("Ignite torch!");
660 	if (g_context->_location->_context == CTX_DUNGEON) {
661 		if (!g_context->_party->lightTorch())
662 			print("%cNone left!%c", FG_GREY, FG_WHITE);
663 	} else {
664 		print("%cNot here!%c", FG_GREY, FG_WHITE);
665 	}
666 
667 	return isDebuggerActive();
668 }
669 
cmdInteract(int argc,const char ** argv)670 bool Debugger::cmdInteract(int argc, const char **argv) {
671 	if (!settings._enhancements || !settings._enhancementsOptions._smartEnterKey)
672 		return isDebuggerActive();
673 
674 	// Attempt to guess based on the character's surroundings
675 
676 	if (g_context->_transportContext == TRANSPORT_FOOT) {
677 		// When on foot, check for boarding
678 		Object *obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
679 		if (obj && (obj->getTile().getTileType()->isShip() ||
680 				obj->getTile().getTileType()->isHorse() ||
681 				obj->getTile().getTileType()->isBalloon()))
682 			return cmdBoard(argc, argv);
683 	} else if (g_context->_transportContext == TRANSPORT_BALLOON) {
684 		// Climb/Descend Balloon
685 		if (g_context->_party->isFlying()) {
686 			return cmdDescend(argc, argv);
687 		} else {
688 #ifdef IOS_ULTIMA4
689 			U4IOS::IOSSuperButtonHelper superHelper;
690 			key = ReadChoiceController::get("xk \033\n");
691 #else
692 			return cmdClimb(argc, argv);
693 #endif
694 		}
695 	} else {
696 		// For all other transports, exit the transport
697 		return cmdExit(argc, argv);
698 	}
699 
700 	if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_KLIMB) != nullptr))
701 		// Climb
702 		return cmdClimb(argc, argv);
703 	else if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_DESCEND) != nullptr))
704 		// Descend
705 		return cmdDescend(argc, argv);
706 
707 	if (g_context->_location->_context == CTX_DUNGEON) {
708 		Dungeon *dungeon = static_cast<Dungeon *>(g_context->_location->_map);
709 		bool up = dungeon->ladderUpAt(g_context->_location->_coords);
710 		bool down = dungeon->ladderDownAt(g_context->_location->_coords);
711 		if (up && down) {
712 #ifdef IOS_ULTIMA4
713 			U4IOS::IOSClimbHelper climbHelper;
714 			key = ReadChoiceController::get("kd \033\n");
715 #else
716 			return cmdClimb(argc, argv);
717 #endif
718 		} else if (up) {
719 			return cmdClimb(argc, argv);
720 		} else {
721 			return cmdDescend(argc, argv);
722 		}
723 	}
724 
725 	if (g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER) != nullptr)
726 		// Enter?
727 		return cmdEnter(argc, argv);
728 
729 	if (!g_context->_party->isFlying()) {
730 		// Get Chest?
731 		MapTile *tile = g_context->_location->_map->tileAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
732 
733 		if (tile->getTileType()->isChest())
734 			return cmdGetChest(argc, argv);
735 	}
736 
737 	// Otherwise default to search
738 	return cmdSearch(argc, argv);
739 }
740 
cmdJimmy(int argc,const char ** argv)741 bool Debugger::cmdJimmy(int argc, const char **argv) {
742 	printN("Jimmy: ");
743 	Direction dir = gameGetDirection();
744 
745 	if (dir == DIR_NONE)
746 		return isDebuggerActive();
747 
748 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
749 		1, 1, nullptr, true);
750 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
751 		if (jimmyAt(*i))
752 			return isDebuggerActive();
753 	}
754 
755 	print("%cJimmy what?%c", FG_GREY, FG_WHITE);
756 	return isDebuggerActive();
757 }
758 
cmdLocate(int argc,const char ** argv)759 bool Debugger::cmdLocate(int argc, const char **argv) {
760 	// Normally Locate isn't allowed in combat, but allow for a special
761 	// debug display if this command is explicitly run in the debugger
762 	if (isCombat() && isDebuggerActive()) {
763 		CombatController *cc = static_cast<CombatController *>(eventHandler->getController());
764 		Coords coords = cc->getCurrentPlayer()->getCoords();
765 		print("Location: x:%d, y:%d, z:%d", coords.x, coords.y, coords.z);
766 		dontEndTurn();
767 	}
768 	// Otherwise can't use sextant in dungeon or in combat
769 	else if (g_context->_location->_context & ~(CTX_DUNGEON | CTX_COMBAT)) {
770 		if (g_ultima->_saveGame->_sextants >= 1)
771 			print("Locate position\nwith sextant\n Latitude: %c'%c\"\nLongitude: %c'%c\"",
772 				g_context->_location->_coords.y / 16 + 'A', g_context->_location->_coords.y % 16 + 'A',
773 				g_context->_location->_coords.x / 16 + 'A', g_context->_location->_coords.x % 16 + 'A');
774 		else
775 			print("%cLocate position with what?%c", FG_GREY, FG_WHITE);
776 	} else {
777 		print("%cNot here!%c", FG_GREY, FG_WHITE);
778 	}
779 
780 	return isDebuggerActive();
781 }
782 
cmdMixReagents(int argc,const char ** argv)783 bool Debugger::cmdMixReagents(int argc, const char **argv) {
784 	/*  uncomment this line to activate new spell mixing code */
785 	//   return mixReagentsSuper();
786 	bool done = false;
787 
788 	while (!done) {
789 		print("Mix reagents");
790 #ifdef IOS_ULTIMA4
791 		U4IOS::beginMixSpellController();
792 		return isDebuggerActive(); // Just return, the dialog takes control from here.
793 #endif
794 
795 		// Verify that there are reagents remaining in the inventory
796 		bool found = false;
797 		for (int i = 0; i < 8; i++) {
798 			if (g_ultima->_saveGame->_reagents[i] > 0) {
799 				found = true;
800 				break;
801 			}
802 		}
803 		if (!found) {
804 			printN("%cNone Left!%c", FG_GREY, FG_WHITE);
805 			done = true;
806 		} else {
807 			printN("For Spell: ");
808 			g_context->_stats->setView(STATS_MIXTURES);
809 
810 			int choice = ReadChoiceController::get("abcdefghijklmnopqrstuvwxyz \033\n\r");
811 			if (choice == -1 || choice == ' ' || choice == '\033' || choice == '\n' || choice == '\r')
812 				break;
813 
814 			int spell = choice - 'a';
815 			print("\n%s", g_spells->spellGetName(spell));
816 
817 			// ensure the mixtures for the spell isn't already maxed out
818 			if (g_ultima->_saveGame->_mixtures[spell] == 99) {
819 				print("\n%cYou cannot mix any more of that spell!%c", FG_GREY, FG_WHITE);
820 				break;
821 			}
822 
823 			// Reset the reagent spell mix menu by removing
824 			// the menu highlight from the current item, and
825 			// hiding reagents that you don't have
826 			g_context->_stats->resetReagentsMenu();
827 
828 			g_context->_stats->setView(MIX_REAGENTS);
829 			if (settings._enhancements && settings._enhancementsOptions._u5SpellMixing)
830 				done = mixReagentsForSpellU5(spell);
831 			else
832 				done = mixReagentsForSpellU4(spell);
833 		}
834 	}
835 
836 	g_context->_stats->setView(STATS_PARTY_OVERVIEW);
837 	print("");
838 
839 	return isDebuggerActive();
840 }
841 
cmdNewOrder(int argc,const char ** argv)842 bool Debugger::cmdNewOrder(int argc, const char **argv) {
843 	printN("New Order!\nExchange # ");
844 
845 	int player1 = gameGetPlayer(true, false);
846 
847 	if (player1 == -1)
848 		return isDebuggerActive();
849 
850 	if (player1 == 0) {
851 		print("%s, You must lead!", g_context->_party->member(0)->getName().c_str());
852 		return isDebuggerActive();
853 	}
854 
855 	printN("    with # ");
856 
857 	int player2 = gameGetPlayer(true, false);
858 
859 	if (player2 == -1)
860 		return isDebuggerActive();
861 
862 	if (player2 == 0) {
863 		print("%s, You must lead!", g_context->_party->member(0)->getName().c_str());
864 		return isDebuggerActive();
865 	}
866 
867 	if (player1 == player2) {
868 		print("%cWhat?%c", FG_GREY, FG_WHITE);
869 		return isDebuggerActive();
870 	}
871 
872 	g_context->_party->swapPlayers(player1, player2);
873 	return isDebuggerActive();
874 }
875 
cmdOpenDoor(int argc,const char ** argv)876 bool Debugger::cmdOpenDoor(int argc, const char **argv) {
877 	///  XXX: Pressing "o" should close any open door.
878 
879 	printN("Open: ");
880 
881 	if (g_context->_party->isFlying()) {
882 		print("%cNot Here!%c", FG_GREY, FG_WHITE);
883 		return isDebuggerActive();
884 	}
885 
886 	Direction dir = gameGetDirection();
887 
888 	if (dir == DIR_NONE)
889 		return isDebuggerActive();
890 
891 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
892 		1, 1, nullptr, true);
893 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
894 		if (openAt(*i))
895 			return isDebuggerActive();
896 	}
897 
898 	print("%cNot Here!%c", FG_GREY, FG_WHITE);
899 	return isDebuggerActive();
900 }
901 
cmdParty(int argc,const char ** argv)902 bool Debugger::cmdParty(int argc, const char **argv) {
903 	if (settings._enhancements && settings._enhancementsOptions._activePlayer) {
904 		int player = (argc == 2) ? strToInt(argv[1]) - 1 : -1;
905 		gameSetActivePlayer(player);
906 	} else {
907 		print("%cBad command!%c", FG_GREY, FG_WHITE);
908 	}
909 
910 	dontEndTurn();
911 	return isDebuggerActive();
912 }
913 
cmdPass(int argc,const char ** argv)914 bool Debugger::cmdPass(int argc, const char **argv) {
915 	print("Pass");
916 	return isDebuggerActive();
917 }
918 
cmdPeer(int argc,const char ** argv)919 bool Debugger::cmdPeer(int argc, const char **argv) {
920 	bool useGem = (argc != 2) ? true : strToBool(argv[1]);
921 	peer(useGem);
922 
923 	return isDebuggerActive();
924 }
925 
cmdQuitAndSave(int argc,const char ** argv)926 bool Debugger::cmdQuitAndSave(int argc, const char **argv) {
927 	print("Quit & Save...\n%d moves", g_ultima->_saveGame->_moves);
928 	if (g_context->_location->_context & CTX_CAN_SAVE_GAME) {
929 		(void)g_ultima->saveGameDialog();
930 		g_ultima->quitGame();
931 
932 		return false;
933 	} else {
934 		print("%cNot here!%c", FG_GREY, FG_WHITE);
935 		return isDebuggerActive();
936 	}
937 }
938 
cmdReadyWeapon(int argc,const char ** argv)939 bool Debugger::cmdReadyWeapon(int argc, const char **argv) {
940 	int player = -1;
941 	if (argc == 2)
942 		player = strToInt(argv[1]);
943 	else if (isCombat())
944 		player = getCombatFocus();
945 
946 	// get the player if not provided
947 	if (player == -1) {
948 		printN("Ready a weapon for: ");
949 		player = gameGetPlayer(true, false);
950 		if (player == -1)
951 			return isDebuggerActive();
952 	}
953 
954 	// get the weapon to use
955 	g_context->_stats->setView(STATS_WEAPONS);
956 	printN("Weapon: ");
957 	int weapon = AlphaActionController::get(WEAP_MAX + 'a' - 1, "Weapon: ");
958 	g_context->_stats->setView(STATS_PARTY_OVERVIEW);
959 	if (weapon == -1)
960 		return isDebuggerActive();
961 
962 	PartyMember *p = g_context->_party->member(player);
963 	const Weapon *w = g_weapons->get((WeaponType)weapon);
964 
965 	if (!w) {
966 		print("");
967 		return isDebuggerActive();
968 	}
969 
970 	switch (p->setWeapon(w)) {
971 	case EQUIP_SUCCEEDED:
972 		print("%s", w->getName().c_str());
973 		break;
974 	case EQUIP_NONE_LEFT:
975 		print("%cNone left!%c", FG_GREY, FG_WHITE);
976 		break;
977 	case EQUIP_CLASS_RESTRICTED:
978 	{
979 		Common::String indef_article;
980 
981 		switch (tolower(w->getName()[0])) {
982 		case 'a':
983 		case 'e':
984 		case 'i':
985 		case 'o':
986 		case 'u':
987 		case 'y':
988 			indef_article = "an";
989 			break;
990 		default:
991 			indef_article = "a";
992 			break;
993 		}
994 
995 		print("\n%cA %s may NOT use %s %s%c", FG_GREY, getClassName(p->getClass()),
996 			indef_article.c_str(), w->getName().c_str(), FG_WHITE);
997 		break;
998 	}
999 	}
1000 
1001 	return isDebuggerActive();
1002 }
1003 
cmdSearch(int argc,const char ** argv)1004 bool Debugger::cmdSearch(int argc, const char **argv) {
1005 	if (g_context->_location->_context == CTX_DUNGEON) {
1006 		dungeonSearch();
1007 	} else if (g_context->_party->isFlying()) {
1008 		print("Searching...\n%cDrift only!%c", FG_GREY, FG_WHITE);
1009 	} else if (g_context->_location->_map->_id == MAP_SCUMMVM &&
1010 			g_context->_location->_coords == Coords(52, 5, 0)) {
1011 		// Special hack for the ScummVM easter egg map. Searching on
1012 		// the given tile triggers the cheat to allow teleporting
1013 		print("Searching...\nFound teleport point!");
1014 		g_game->exitToParentMap();
1015 		g_music->playMapMusic();
1016 
1017 		return cmdGoto(argc, argv);
1018 	} else {
1019 		print("Searching...");
1020 
1021 		const ItemLocation *item = g_items->itemAtLocation(g_context->_location->_map, g_context->_location->_coords);
1022 		if (item) {
1023 			if (item->_isItemInInventory && (g_items->*(item->_isItemInInventory))(item->_data)) {
1024 				print("%cNothing Here!%c", FG_GREY, FG_WHITE);
1025 			} else {
1026 				if (item->_name)
1027 					print("You find...\n%s!", item->_name);
1028 				(g_items->*(item->_putItemInInventory))(item->_data);
1029 			}
1030 		} else if (usePortalAt(g_context->_location, g_context->_location->_coords, ACTION_ENTER)) {
1031 			print("");
1032 		} else {
1033 			print("%cNothing Here!%c", FG_GREY, FG_WHITE);
1034 		}
1035 	}
1036 
1037 	return isDebuggerActive();
1038 }
1039 
cmdSpeed(int argc,const char ** argv)1040 bool Debugger::cmdSpeed(int argc, const char **argv) {
1041 	Common::String action = argv[1];
1042 	int oldCycles = settings._gameCyclesPerSecond;
1043 
1044 	if (action == "up") {
1045 		if (++settings._gameCyclesPerSecond > MAX_CYCLES_PER_SECOND)
1046 			settings._gameCyclesPerSecond = MAX_CYCLES_PER_SECOND;
1047 	} else if (action == "down") {
1048 		if (--settings._gameCyclesPerSecond == 0)
1049 			settings._gameCyclesPerSecond = 1;
1050 	} else if (action == "normal") {
1051 		settings._gameCyclesPerSecond = DEFAULT_CYCLES_PER_SECOND;
1052 	}
1053 
1054 	if (oldCycles != settings._gameCyclesPerSecond) {
1055 		settings._eventTimerGranularity = (1000 / settings._gameCyclesPerSecond);
1056 		eventHandler->getTimer()->reset(settings._eventTimerGranularity);
1057 
1058 		if (settings._gameCyclesPerSecond == DEFAULT_CYCLES_PER_SECOND)
1059 			print("Speed: Normal");
1060 		else if (action == "up")
1061 			print("Speed Up (%d)", settings._gameCyclesPerSecond);
1062 		else
1063 			print("Speed Down (%d)", settings._gameCyclesPerSecond);
1064 	} else if (settings._gameCyclesPerSecond == DEFAULT_CYCLES_PER_SECOND) {
1065 		print("Speed: Normal");
1066 	}
1067 
1068 	dontEndTurn();
1069 	return isDebuggerActive();
1070 }
1071 
cmdCombatSpeed(int argc,const char ** argv)1072 bool Debugger::cmdCombatSpeed(int argc, const char **argv) {
1073 	Common::String action = argv[1];
1074 	int oldSpeed = settings._battleSpeed;
1075 
1076 	if (action == "up" && ++settings._battleSpeed > MAX_BATTLE_SPEED)
1077 		settings._battleSpeed = MAX_BATTLE_SPEED;
1078 	else if (action == "down" && --settings._battleSpeed == 0)
1079 		settings._battleSpeed = 1;
1080 	else if (action == "normal")
1081 		settings._battleSpeed = DEFAULT_BATTLE_SPEED;
1082 
1083 	if (oldSpeed != settings._battleSpeed) {
1084 		if (settings._battleSpeed == DEFAULT_BATTLE_SPEED) {
1085 			print("Battle Speed:\nNormal");
1086 		} else if (action == "up") {
1087 			print("Battle Speed:\nUp (%d)", settings._battleSpeed);
1088 		} else {
1089 			print("Battle Speed:\nDown (%d)", settings._battleSpeed);
1090 		}
1091 	} else if (settings._battleSpeed == DEFAULT_BATTLE_SPEED) {
1092 		print("Battle Speed:\nNormal");
1093 	}
1094 
1095 	dontEndTurn();
1096 	return isDebuggerActive();
1097 }
1098 
cmdStats(int argc,const char ** argv)1099 bool Debugger::cmdStats(int argc, const char **argv) {
1100 	int player = -1;
1101 	if (argc == 2)
1102 		player = strToInt(argv[1]);
1103 	else if (isCombat())
1104 		player = getCombatFocus();
1105 
1106 	// get the player if not provided
1107 	if (player == -1) {
1108 		printN("Ztats for: ");
1109 		player = gameGetPlayer(true, false);
1110 		if (player == -1)
1111 			return isDebuggerActive();
1112 	} else {
1113 		print("Ztats");
1114 	}
1115 
1116 	// Reset the reagent spell mix menu by removing
1117 	// the menu highlight from the current item, and
1118 	// hiding reagents that you don't have
1119 	g_context->_stats->resetReagentsMenu();
1120 
1121 	g_context->_stats->setView(StatsView(STATS_CHAR1 + player));
1122 #ifdef IOS_ULTIMA4
1123 	U4IOS::IOSHideActionKeysHelper hideExtraControls;
1124 #endif
1125 	ZtatsController ctrl;
1126 	eventHandler->pushController(&ctrl);
1127 	ctrl.waitFor();
1128 
1129 	return isDebuggerActive();
1130 }
1131 
cmdTalk(int argc,const char ** argv)1132 bool Debugger::cmdTalk(int argc, const char **argv) {
1133 	printN("Talk: ");
1134 
1135 	if (g_context->_party->isFlying()) {
1136 		print("%cDrift only!%c", FG_GREY, FG_WHITE);
1137 		return isDebuggerActive();
1138 	}
1139 
1140 	Direction dir = gameGetDirection();
1141 
1142 	if (dir == DIR_NONE)
1143 		return isDebuggerActive();
1144 
1145 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
1146 		1, 2, &Tile::canTalkOverTile, true);
1147 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
1148 		if (talkAt(*i))
1149 			return isDebuggerActive();
1150 	}
1151 
1152 	print("Funny, no response!");
1153 	return isDebuggerActive();
1154 }
1155 
cmdUse(int argc,const char ** argv)1156 bool Debugger::cmdUse(int argc, const char **argv) {
1157 	print("Use which item:");
1158 
1159 	if (settings._enhancements) {
1160 		// a little xu4 enhancement: show items in inventory when prompted for an item to use
1161 		g_context->_stats->setView(STATS_ITEMS);
1162 	}
1163 #ifdef IOS_ULTIMA4
1164 	U4IOS::IOSConversationHelper::setIntroString("Use which item?");
1165 #endif
1166 	g_items->itemUse(gameGetInput().c_str());
1167 	return isDebuggerActive();
1168 }
1169 
cmdWearArmor(int argc,const char ** argv)1170 bool Debugger::cmdWearArmor(int argc, const char **argv) {
1171 	int player = -1;
1172 	if (argc == 2)
1173 		player = strToInt(argv[1]);
1174 
1175 	// get the player if not provided
1176 	if (player == -1) {
1177 		printN("Wear Armour\nfor: ");
1178 		player = gameGetPlayer(true, false);
1179 		if (player == -1)
1180 			return isDebuggerActive();
1181 	}
1182 
1183 	g_context->_stats->setView(STATS_ARMOR);
1184 	printN("Armour: ");
1185 	int armor = AlphaActionController::get(ARMR_MAX + 'a' - 1, "Armour: ");
1186 	g_context->_stats->setView(STATS_PARTY_OVERVIEW);
1187 	if (armor == -1)
1188 		return isDebuggerActive();
1189 
1190 	const Armor *a = g_armors->get((ArmorType)armor);
1191 	PartyMember *p = g_context->_party->member(player);
1192 
1193 	if (!a) {
1194 		print("");
1195 		return isDebuggerActive();
1196 	}
1197 
1198 	switch (p->setArmor(a)) {
1199 	case EQUIP_SUCCEEDED:
1200 		print("%s", a->getName().c_str());
1201 		break;
1202 	case EQUIP_NONE_LEFT:
1203 		print("%cNone left!%c", FG_GREY, FG_WHITE);
1204 		break;
1205 	case EQUIP_CLASS_RESTRICTED:
1206 		print("\n%cA %s may NOT use %s%c", FG_GREY, getClassName(p->getClass()), a->getName().c_str(), FG_WHITE);
1207 		break;
1208 	}
1209 
1210 	return isDebuggerActive();
1211 }
1212 
cmdYell(int argc,const char ** argv)1213 bool Debugger::cmdYell(int argc, const char **argv) {
1214 	printN("Yell ");
1215 	if (g_context->_transportContext == TRANSPORT_HORSE) {
1216 		if (g_context->_horseSpeed == 0) {
1217 			print("Giddyup!");
1218 			g_context->_horseSpeed = 1;
1219 		} else {
1220 			print("Whoa!");
1221 			g_context->_horseSpeed = 0;
1222 		}
1223 	} else {
1224 		print("%cWhat?%c", FG_GREY, FG_WHITE);
1225 	}
1226 
1227 	return isDebuggerActive();
1228 }
1229 
1230 
cmd3d(int argc,const char ** argv)1231 bool Debugger::cmd3d(int argc, const char **argv) {
1232 	if (g_context->_location->_context == CTX_DUNGEON) {
1233 		print("3-D view %s", DungeonViewer.toggle3DDungeonView() ? "on" : "off");
1234 	} else {
1235 		print("Not here");
1236 	}
1237 
1238 	return isDebuggerActive();
1239 }
1240 
cmdAbyss(int argc,const char ** argv)1241 bool Debugger::cmdAbyss(int argc, const char **argv) {
1242 	// first teleport to the abyss
1243 	g_context->_location->_coords.x = 0xe9;
1244 	g_context->_location->_coords.y = 0xe9;
1245 	g_game->setMap(mapMgr->get(MAP_ABYSS), 1, nullptr);
1246 
1247 	// then to the final altar
1248 	g_context->_location->_coords.x = 7;
1249 	g_context->_location->_coords.y = 7;
1250 	g_context->_location->_coords.z = 7;
1251 	g_ultima->_saveGame->_orientation = DIR_NORTH;
1252 	g_context->_party->lightTorch(100, false);
1253 
1254 	cmdIgnite(0, nullptr);
1255 	return isDebuggerActive();
1256 }
1257 
cmdCollisions(int argc,const char ** argv)1258 bool Debugger::cmdCollisions(int argc, const char **argv) {
1259 	_collisionOverride = !_collisionOverride;
1260 	print("Collision detection %s",
1261 		_collisionOverride ? "off" : "on");
1262 
1263 	return isDebuggerActive();
1264 }
1265 
cmdCompanions(int argc,const char ** argv)1266 bool Debugger::cmdCompanions(int argc, const char **argv) {
1267 	for (int m = g_ultima->_saveGame->_members; m < 8; m++) {
1268 		if (g_context->_party->canPersonJoin(g_ultima->_saveGame->_players[m]._name, nullptr)) {
1269 			g_context->_party->join(g_ultima->_saveGame->_players[m]._name);
1270 		}
1271 	}
1272 
1273 	g_context->_stats->update();
1274 	print("Joined by companions");
1275 	return isDebuggerActive();
1276 }
1277 
cmdCombat(int argc,const char ** argv)1278 bool Debugger::cmdCombat(int argc, const char **argv) {
1279 	_disableCombat = !_disableCombat;
1280 	print("Combat encounters %s",
1281 		_disableCombat ? "off" : "on");
1282 
1283 	return isDebuggerActive();
1284 }
1285 
cmdDestroy(int argc,const char ** argv)1286 bool Debugger::cmdDestroy(int argc, const char **argv) {
1287 	Direction dir;
1288 
1289 	if (argc == 2) {
1290 		dir = directionFromName(argv[1]);
1291 	} else if (isDebuggerActive()) {
1292 		print("destroy <direction>");
1293 		return isDebuggerActive();
1294 	} else {
1295 		printN("Destroy Object\nDir: ");
1296 		dir = gameGetDirection();
1297 	}
1298 
1299 	if (dir == DIR_NONE)
1300 		return isDebuggerActive();
1301 
1302 	Std::vector<Coords> path = gameGetDirectionalActionPath(MASK_DIR(dir),
1303 		MASK_DIR_ALL, g_context->_location->_coords, 1, 1, nullptr, true);
1304 	for (Std::vector<Coords>::iterator i = path.begin(); i != path.end(); i++) {
1305 		if (destroyAt(*i)) {
1306 			return false;
1307 		}
1308 	}
1309 
1310 	print("%cNothing there!%c", FG_GREY, FG_WHITE);
1311 	return isDebuggerActive();
1312 }
1313 
cmdDestroyCreatures(int argc,const char ** argv)1314 bool Debugger::cmdDestroyCreatures(int argc, const char **argv) {
1315 	gameDestroyAllCreatures();
1316 	dontEndTurn();
1317 
1318 	return isDebuggerActive();
1319 }
1320 
cmdDungeon(int argc,const char ** argv)1321 bool Debugger::cmdDungeon(int argc, const char **argv) {
1322 	if (g_context->_location->_context & CTX_WORLDMAP) {
1323 		if (argc == 2) {
1324 			int dungNum = strToInt(argv[1]);
1325 
1326 			if (dungNum >= 1 && dungNum <= 8) {
1327 				g_context->_location->_coords = g_context->_location->_map->_portals[dungNum + 15]->_coords;
1328 				return false;
1329 			} else if (dungNum == 9) {
1330 				g_game->setMap(mapMgr->get(MAP_DECEIT), 1, nullptr);
1331 				g_context->_location->_coords = MapCoords(1, 0, 7);
1332 				g_ultima->_saveGame->_orientation = DIR_SOUTH;
1333 			} else if (dungNum == 10) {
1334 				g_game->setMap(mapMgr->get(MAP_DESPISE), 1, nullptr);
1335 				g_context->_location->_coords = MapCoords(3, 2, 7);
1336 				g_ultima->_saveGame->_orientation = DIR_SOUTH;
1337 			} else if (dungNum == 11) {
1338 				g_game->setMap(mapMgr->get(MAP_DESTARD), 1, nullptr);
1339 				g_context->_location->_coords = MapCoords(7, 6, 7);
1340 				g_ultima->_saveGame->_orientation = DIR_SOUTH;
1341 			} else {
1342 				print("Invalid dungeon");
1343 				return isDebuggerActive();
1344 			}
1345 
1346 			return false;
1347 		} else {
1348 			print("dungeon <number>");
1349 		}
1350 	} else {
1351 		print("Not here");
1352 	}
1353 
1354 	return isDebuggerActive();
1355 }
1356 
cmdFlee(int argc,const char ** argv)1357 bool Debugger::cmdFlee(int argc, const char **argv) {
1358 	if (eventHandler->getController() == g_combat) {
1359 		// End the combat without losing karma
1360 		g_combat->end(false);
1361 	} else {
1362 		print("Bad command");
1363 	}
1364 
1365 	return isDebuggerActive();
1366 }
1367 
cmdEquipment(int argc,const char ** argv)1368 bool Debugger::cmdEquipment(int argc, const char **argv) {
1369 	int i;
1370 
1371 	for (i = ARMR_NONE + 1; i < ARMR_MAX; ++i)
1372 		g_ultima->_saveGame->_armor[i] = 8;
1373 
1374 	for (i = WEAP_HANDS + 1; i < WEAP_MAX; ++i) {
1375 		const Weapon *weapon = g_weapons->get(static_cast<WeaponType>(i));
1376 		if (weapon->loseWhenUsed() || weapon->loseWhenRanged())
1377 			g_ultima->_saveGame->_weapons[i] = 99;
1378 		else
1379 			g_ultima->_saveGame->_weapons[i] = 8;
1380 	}
1381 
1382 	print("All equipment given");
1383 	return isDebuggerActive();
1384 }
1385 
cmdGate(int argc,const char ** argv)1386 bool Debugger::cmdGate(int argc, const char **argv) {
1387 	int gateNum = (argc == 2) ? strToInt(argv[1]) : -1;
1388 
1389 	if (!g_context || !g_game || gateNum < 1 || gateNum > 8) {
1390 		print("Gate <1 to 8>");
1391 	} else {
1392 		if (!isDebuggerActive())
1393 			print("Gate %d!", gateNum);
1394 
1395 		if (g_context->_location->_map->isWorldMap()) {
1396 			const Coords *moongate = g_moongates->getGateCoordsForPhase(gateNum - 1);
1397 			if (moongate) {
1398 				g_context->_location->_coords = *moongate;
1399 				return false;
1400 			}
1401 		} else {
1402 			print("Not here!");
1403 		}
1404 	}
1405 
1406 	return isDebuggerActive();
1407 }
1408 
cmdGoto(int argc,const char ** argv)1409 bool Debugger::cmdGoto(int argc, const char **argv) {
1410 	Common::String dest;
1411 	PortalList &portals = g_context->_location->_map->_portals;
1412 	uint p;
1413 
1414 	if (argc == 2) {
1415 		dest = argv[1];
1416 	} else if (isDebuggerActive()) {
1417 		print("teleport <destination name>");
1418 		return true;
1419 	} else {
1420 		printN("Goto: ");
1421 		dest = gameGetInput(32);
1422 		print("");
1423 	}
1424 
1425 	dest.toLowercase();
1426 	if (dest == "britain")
1427 		dest = "britannia";
1428 
1429 	bool found = false;
1430 	p = strToInt(dest.c_str());
1431 
1432 	if (p > 0 && p <= portals.size()) {
1433 		g_context->_location->_coords = portals[p - 1]->_coords;
1434 		found = true;
1435 	}
1436 
1437 	for (p = 0; p < portals.size() && !found; p++) {
1438 		MapId destid = portals[p]->_destid;
1439 		Common::String destNameLower = mapMgr->get(destid)->getName();
1440 		destNameLower.toLowercase();
1441 
1442 		if (destNameLower.find(dest) != Common::String::npos) {
1443 			print("\n%s", mapMgr->get(destid)->getName().c_str());
1444 			g_context->_location->_coords = portals[p]->_coords;
1445 			found = true;
1446 			break;
1447 		}
1448 	}
1449 
1450 	if (!found) {
1451 		MapCoords coords = g_context->_location->_map->getLabel(dest);
1452 		if (coords != MapCoords::nowhere()) {
1453 			print("%s", dest.c_str());
1454 			g_context->_location->_coords = coords;
1455 			found = true;
1456 		}
1457 	}
1458 
1459 	if (found) {
1460 		return false;
1461 	} else {
1462 		if (isDebuggerActive())
1463 			print("Can't find %s", dest.c_str());
1464 		else
1465 			print("Can't find\n%s", dest.c_str());
1466 
1467 		return isDebuggerActive();
1468 	}
1469 }
1470 
cmdLorddBritish(int argc,const char ** argv)1471 bool Debugger::cmdLorddBritish(int argc, const char **argv) {
1472 	if (!isDebuggerActive()) {
1473 		print("Help me LB!");
1474 		g_screen->screenPrompt();
1475 	}
1476 
1477 	// Help! send me to Lord British
1478 	g_game->setMap(mapMgr->get(100), 1, nullptr);
1479 	g_context->_location->_coords.x = 19;
1480 	g_context->_location->_coords.y = 8;
1481 	g_context->_location->_coords.z = 0;
1482 
1483 	return false;
1484 }
1485 
cmdItems(int argc,const char ** argv)1486 bool Debugger::cmdItems(int argc, const char **argv) {
1487 	SaveGame &sg = *g_ultima->_saveGame;
1488 	sg._torches = 99;
1489 	sg._gems = 99;
1490 	sg._keys = 99;
1491 	sg._sextants = 1;
1492 	sg._items = ITEM_SKULL | ITEM_CANDLE | ITEM_BOOK | ITEM_BELL | ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T | ITEM_HORN | ITEM_WHEEL;
1493 	sg._stones = 0xff;
1494 	sg._runes = 0xff;
1495 	sg._food = 999900;
1496 	sg._gold = 9999;
1497 
1498 	g_context->_stats->update();
1499 	print("All items given");
1500 	return isDebuggerActive();
1501 }
1502 
cmdKarma(int argc,const char ** argv)1503 bool Debugger::cmdKarma(int argc, const char **argv) {
1504 	print("Karma!");
1505 
1506 	for (int i = 0; i < 8; ++i) {
1507 		Common::String line = Common::String::format("%s:",
1508 			getVirtueName(static_cast<Virtue>(i)));
1509 		while (line.size() < 13)
1510 			line += ' ';
1511 
1512 		if (g_ultima->_saveGame->_karma[i] > 0)
1513 			line += Common::String::format("%.2d", g_ultima->_saveGame->_karma[i]);
1514 		else
1515 			line += "--";
1516 		print("%s", line.c_str());
1517 	}
1518 
1519 	return isDebuggerActive();
1520 }
1521 
cmdLeave(int argc,const char ** argv)1522 bool Debugger::cmdLeave(int argc, const char **argv) {
1523 	if (!g_game->exitToParentMap()) {
1524 		print("Not Here");
1525 	} else {
1526 		g_music->playMapMusic();
1527 		print("Exited");
1528 	}
1529 
1530 	return isDebuggerActive();
1531 }
1532 
cmdLocation(int argc,const char ** argv)1533 bool Debugger::cmdLocation(int argc, const char **argv) {
1534 	const MapCoords &pos = g_context->_location->_coords;
1535 
1536 	if (argc == 3) {
1537 		Coords newPos;
1538 
1539 		if (strlen(argv[1]) == 2 && strlen(argv[2]) == 2
1540 				&& Common::isAlpha(argv[1][0]) && Common::isAlpha(argv[1][1])
1541 			&& Common::isAlpha(argv[2][0]) && Common::isAlpha(argv[2][1])
1542 		) {
1543 			newPos.y = (toupper(argv[1][0]) - 'A') * 16 + (toupper(argv[1][1]) - 'A');
1544 			newPos.x = (toupper(argv[2][0]) - 'A') * 16 + (toupper(argv[2][1]) - 'A');
1545 		} else {
1546 			newPos.x = strToInt(argv[1]);
1547 			newPos.y = strToInt(argv[2]);
1548 		}
1549 
1550 		if (newPos.x >= 0 && newPos.y >= 0
1551 				&& newPos.x < (int)g_context->_location->_map->_width
1552 				&& newPos.y < (int)g_context->_location->_map->_height) {
1553 			g_context->_location->_coords = newPos;
1554 			return false;
1555 		} else {
1556 			print("Invalid location!");
1557 		}
1558 	} else if (isDebuggerActive()) {
1559 		if (g_context->_location->_map->isWorldMap())
1560 			print("Location: %s x: %d, y: %d",
1561 				"World Map", pos.x, pos.y);
1562 		else
1563 			print("Location: %s x: %d, y: %d, z: %d",
1564 				g_context->_location->_map->getName().c_str(), pos.x, pos.y, pos.z);
1565 	} else {
1566 		if (g_context->_location->_map->isWorldMap())
1567 			print("\nLocation:\n%s\nx: %d\ny: %d", "World Map",
1568 				pos.x, pos.y);
1569 		else
1570 			print("\nLocation:\n%s\nx: %d\ny: %d\nz: %d",
1571 				g_context->_location->_map->getName().c_str(), pos.x, pos.y, pos.z);
1572 	}
1573 
1574 	return isDebuggerActive();
1575 }
1576 
cmdMixtures(int argc,const char ** argv)1577 bool Debugger::cmdMixtures(int argc, const char **argv) {
1578 	for (int i = 0; i < SPELL_MAX; i++)
1579 		g_ultima->_saveGame->_mixtures[i] = 99;
1580 
1581 	print("All mixtures given");
1582 	return isDebuggerActive();
1583 }
1584 
cmdOverhead(int argc,const char ** argv)1585 bool Debugger::cmdOverhead(int argc, const char **argv) {
1586 	if ((g_context->_location->_viewMode == VIEW_NORMAL) || (g_context->_location->_viewMode == VIEW_DUNGEON))
1587 		g_context->_location->_viewMode = VIEW_GEM;
1588 	else if (g_context->_location->_context == CTX_DUNGEON)
1589 		g_context->_location->_viewMode = VIEW_DUNGEON;
1590 	else
1591 		g_context->_location->_viewMode = VIEW_NORMAL;
1592 
1593 	print("Toggle view");
1594 	return isDebuggerActive();
1595 }
1596 
cmdMoon(int argc,const char ** argv)1597 bool Debugger::cmdMoon(int argc, const char **argv) {
1598 	int moonNum;
1599 
1600 	if (argc == 2) {
1601 		moonNum = strToInt(argv[1]);
1602 		if (moonNum < 0 || moonNum > 7) {
1603 			print("Invalid moon");
1604 			return true;
1605 		}
1606 	} else {
1607 		moonNum = (g_ultima->_saveGame->_trammelPhase + 1) & 7;
1608 	}
1609 
1610 	while (g_ultima->_saveGame->_trammelPhase != moonNum)
1611 		g_game->updateMoons(true);
1612 
1613 	print("Moons advanced");
1614 	return isDebuggerActive();
1615 }
1616 
cmdOpacity(int argc,const char ** argv)1617 bool Debugger::cmdOpacity(int argc, const char **argv) {
1618 	g_context->_opacity = !g_context->_opacity;
1619 	print("Opacity is %s", g_context->_opacity ? "on" : "off");
1620 	return isDebuggerActive();
1621 }
1622 
cmdReagents(int argc,const char ** argv)1623 bool Debugger::cmdReagents(int argc, const char **argv) {
1624 	for (int i = 0; i < REAG_MAX; i++)
1625 		g_ultima->_saveGame->_reagents[i] = 99;
1626 
1627 	print("Reagents given");
1628 	return isDebuggerActive();
1629 }
1630 
cmdFullStats(int argc,const char ** argv)1631 bool Debugger::cmdFullStats(int argc, const char **argv) {
1632 	for (int i = 0; i < g_ultima->_saveGame->_members; i++) {
1633 		g_ultima->_saveGame->_players[i]._str = 50;
1634 		g_ultima->_saveGame->_players[i]._dex = 50;
1635 		g_ultima->_saveGame->_players[i]._intel = 50;
1636 
1637 		if (g_ultima->_saveGame->_players[i]._hpMax < 800) {
1638 			g_ultima->_saveGame->_players[i]._xp = 9999;
1639 			g_ultima->_saveGame->_players[i]._hpMax = 800;
1640 			g_ultima->_saveGame->_players[i]._hp = 800;
1641 		}
1642 	}
1643 
1644 	g_context->_stats->update();
1645 	print("Full Stats given");
1646 	return isDebuggerActive();
1647 }
1648 
cmdHunger(int argc,const char ** argv)1649 bool Debugger::cmdHunger(int argc, const char **argv) {
1650 	_disableHunger = !_disableHunger;
1651 	print("Party hunger %s",
1652 		_disableHunger ? "off" : "on");
1653 
1654 	return isDebuggerActive();
1655 }
1656 
cmdSummon(int argc,const char ** argv)1657 bool Debugger::cmdSummon(int argc, const char **argv) {
1658 	Common::String creature;
1659 
1660 	if (argc == 2) {
1661 		creature = argv[1];
1662 	} else if (isDebuggerActive()) {
1663 		print("summon <creature name>");
1664 		return true;
1665 	} else {
1666 		print("Summon!");
1667 		print("What?");
1668 		creature = gameGetInput();
1669 	}
1670 
1671 	summonCreature(creature);
1672 	return isDebuggerActive();
1673 }
1674 
cmdTorch(int argc,const char ** argv)1675 bool Debugger::cmdTorch(int argc, const char **argv) {
1676 	print("Torch: %d", g_context->_party->getTorchDuration());
1677 	if (!isDebuggerActive())
1678 		g_screen->screenPrompt();
1679 
1680 	return isDebuggerActive();
1681 }
1682 
cmdTransport(int argc,const char ** argv)1683 bool Debugger::cmdTransport(int argc, const char **argv) {
1684 	if (!g_context->_location->_map->isWorldMap()) {
1685 		print("Not here!");
1686 		return isDebuggerActive();
1687 	}
1688 
1689 	_horse = g_context->_location->_map->_tileSet->getByName("horse")->getId();
1690 	_ship = g_context->_location->_map->_tileSet->getByName("ship")->getId();
1691 	_balloon = g_context->_location->_map->_tileSet->getByName("balloon")->getId();
1692 
1693 	MapCoords coords = g_context->_location->_coords;
1694 	MapTile *choice;
1695 	Tile *tile;
1696 
1697 	// Get the transport of choice
1698 	char transport;
1699 	if (argc >= 2) {
1700 		transport = argv[1][0];
1701 	} else if (isDebuggerActive()) {
1702 		print("transport <transport name>");
1703 		return isDebuggerActive();
1704 	} else {
1705 		transport = ReadChoiceController::get("shb \033\015");
1706 	}
1707 
1708 	switch (transport) {
1709 	case 's':
1710 		choice = &_ship;
1711 		break;
1712 	case 'h':
1713 		choice = &_horse;
1714 		break;
1715 	case 'b':
1716 		choice = &_balloon;
1717 		break;
1718 	default:
1719 		print("Unknown transport");
1720 		return isDebuggerActive();
1721 	}
1722 
1723 	tile = g_context->_location->_map->_tileSet->get(choice->getId());
1724 	Direction dir;
1725 
1726 	if (argc == 3) {
1727 		dir = directionFromName(argv[2]);
1728 	} else if (isDebuggerActive()) {
1729 		dir = DIR_NONE;
1730 	} else {
1731 		print("%s", tile->getName().c_str());
1732 
1733 		// Get the direction in which to create the transport
1734 		ReadDirController readDir;
1735 		eventHandler->pushController(&readDir);
1736 
1737 		printN("Dir: ");
1738 		dir = readDir.waitFor();
1739 	}
1740 
1741 	coords.move(dir, g_context->_location->_map);
1742 
1743 	if (coords != g_context->_location->_coords) {
1744 		bool ok;
1745 		MapTile *ground = g_context->_location->_map->tileAt(coords, WITHOUT_OBJECTS);
1746 
1747 		switch (transport) {
1748 		case 's':
1749 			ok = ground->getTileType()->isSailable();
1750 			break;
1751 		case 'h':
1752 			ok = ground->getTileType()->isWalkable();
1753 			break;
1754 		case 'b':
1755 			ok = ground->getTileType()->isWalkable();
1756 			break;
1757 		default:
1758 			ok = false;
1759 			break;
1760 		}
1761 
1762 		if (ok) {
1763 			g_context->_location->_map->addObject(*choice, *choice, coords);
1764 			print("%s created!", tile->getName().c_str());
1765 		} else if (!choice) {
1766 			print("Invalid transport!");
1767 		} else {
1768 			print("Can't place %s there!", tile->getName().c_str());
1769 		}
1770 	}
1771 
1772 	return isDebuggerActive();
1773 }
1774 
cmdUp(int argc,const char ** argv)1775 bool Debugger::cmdUp(int argc, const char **argv) {
1776 	if ((g_context->_location->_context & CTX_DUNGEON) && (g_context->_location->_coords.z > 0)) {
1777 		g_context->_location->_coords.z--;
1778 
1779 		return false;
1780 	} else {
1781 		print("Leaving...");
1782 		g_game->exitToParentMap();
1783 		g_music->playMapMusic();
1784 
1785 		return isDebuggerActive();
1786 	}
1787 }
1788 
cmdDown(int argc,const char ** argv)1789 bool Debugger::cmdDown(int argc, const char **argv) {
1790 	if ((g_context->_location->_context & CTX_DUNGEON) && (g_context->_location->_coords.z < 7)) {
1791 		g_context->_location->_coords.z++;
1792 		return false;
1793 	} else {
1794 		print("Not here");
1795 		return isDebuggerActive();
1796 	}
1797 }
1798 
cmdVirtue(int argc,const char ** argv)1799 bool Debugger::cmdVirtue(int argc, const char **argv) {
1800 	if (argc == 1) {
1801 		for (int i = 0; i < 8; i++)
1802 			g_ultima->_saveGame->_karma[i] = 0;
1803 
1804 		g_context->_stats->update();
1805 		print("Full virtues");
1806 	} else {
1807 		int virtue = strToInt(argv[1]);
1808 
1809 		if (virtue <= 0 || virtue >= VIRT_MAX) {
1810 			print("Invalid virtue");
1811 		} else {
1812 			print("Improved %s", getVirtueName((Virtue)virtue));
1813 
1814 			if (g_ultima->_saveGame->_karma[virtue] == 99)
1815 				g_ultima->_saveGame->_karma[virtue] = 0;
1816 			else if (g_ultima->_saveGame->_karma[virtue] != 0)
1817 				g_ultima->_saveGame->_karma[virtue] += 10;
1818 			if (g_ultima->_saveGame->_karma[virtue] > 99)
1819 				g_ultima->_saveGame->_karma[virtue] = 99;
1820 			g_context->_stats->update();
1821 		}
1822 	}
1823 
1824 	return isDebuggerActive();
1825 }
1826 
cmdWind(int argc,const char ** argv)1827 bool Debugger::cmdWind(int argc, const char **argv) {
1828 	Common::String windDir;
1829 
1830 	if (argc == 2) {
1831 		windDir = argv[1];
1832 	} else if (isDebuggerActive()) {
1833 		print("wind <direction or 'lock'>");
1834 		return true;
1835 	} else {
1836 		print("Wind Dir ('l' to lock)");
1837 		windDir = gameGetInput();
1838 	}
1839 
1840 	windDir.toLowercase();
1841 	if (windDir == "lock" || windDir == "l") {
1842 		g_context->_windLock = !g_context->_windLock;
1843 		print("Wind direction is %slocked",
1844 			g_context->_windLock ? "" : "un");
1845 	} else {
1846 		Direction dir = directionFromName(windDir);
1847 
1848 		if (dir == DIR_NONE) {
1849 			print("Unknown direction");
1850 			return isDebuggerActive();
1851 		} else {
1852 			g_context->_windDirection = dir;
1853 		}
1854 	}
1855 
1856 	return false;
1857 }
1858 
cmdListTriggers(int argc,const char ** argv)1859 bool Debugger::cmdListTriggers(int argc, const char **argv) {
1860 	CombatMap *map = nullptr;
1861 
1862 	if (isCombat() && (map = static_cast<CombatController *>(
1863 			eventHandler->getController())->getMap()) != nullptr
1864 			&& map->isDungeonRoom()) {
1865 		Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_prev->_map);
1866 		assert(dungeon);
1867 		Trigger *triggers = dungeon->_rooms[dungeon->_currentRoom]._triggers;
1868 		assert(triggers);
1869 		int i;
1870 
1871 		print("Triggers!");
1872 
1873 		for (i = 0; i < 4; i++) {
1874 			print("%.1d)xy tile xy xy", i + 1);
1875 			print("  %.1X%.1X  %.3d %.1X%.1X %.1X%.1X",
1876 				triggers[i].x, triggers[i].y,
1877 				triggers[i]._tile,
1878 				triggers[i]._changeX1, triggers[i]._changeY1,
1879 				triggers[i].changeX2, triggers[i].changeY2);
1880 		}
1881 		prompt();
1882 		dontEndTurn();
1883 
1884 	} else {
1885 		print("Not here!");
1886 	}
1887 
1888 	return isDebuggerActive();
1889 }
1890 
1891 } // End of namespace Ultima4
1892 } // End of namespace Ultima
1893