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