Files
2026-02-02 04:50:13 +01:00

1758 lines
51 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/file.h"
#include "common/tokenizer.h"
#include "image/png.h"
#include "image/bmp.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/inverter_process.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/gumps/fast_area_vis_gump.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/gumps/minimap_gump.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/quit_gump.h"
#include "ultima/ultima8/gumps/shape_viewer_gump.h"
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/misc/id_man.h"
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/usecode/bit_set.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/camera_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/actors/quick_avatar_mover_process.h"
#include "ultima/ultima8/world/actors/avatar_mover_process.h"
#include "ultima/ultima8/world/actors/pathfinder.h"
#include "ultima/ultima8/world/target_reticle_process.h"
#include "ultima/ultima8/world/item_selection_process.h"
#include "ultima/ultima8/world/actors/main_actor.h"
namespace Ultima {
namespace Ultima8 {
Debugger *g_debugger;
Debugger::Debugger() : GUI::Debugger() {
g_debugger = this;
registerCmd("quit", WRAP_METHOD(Debugger, cmdQuit));
registerCmd("Ultima8Engine::quit", WRAP_METHOD(Debugger, cmdQuit));
registerCmd("Ultima8Engine::saveGame", WRAP_METHOD(Debugger, cmdSaveGame));
registerCmd("Ultima8Engine::loadGame", WRAP_METHOD(Debugger, cmdLoadGame));
registerCmd("Ultima8Engine::newGame", WRAP_METHOD(Debugger, cmdNewGame));
registerCmd("Ultima8Engine::engineStats", WRAP_METHOD(Debugger, cmdEngineStats));
registerCmd("Ultima8Engine::setVideoMode", WRAP_METHOD(Debugger, cmdSetVideoMode));
registerCmd("Ultima8Engine::toggleAvatarInStasis", WRAP_METHOD(Debugger, cmdAvatarInStasis));
registerCmd("Ultima8Engine::togglePaintEditorItems", WRAP_METHOD(Debugger, cmdShowEditorItems));
registerCmd("Ultima8Engine::toggleShowTouchingItems", WRAP_METHOD(Debugger, cmdShowTouchingItems));
registerCmd("Ultima8Engine::closeItemGumps", WRAP_METHOD(Debugger, cmdCloseItemGumps));
registerCmd("CameraProcess::moveToAvatar", WRAP_METHOD(Debugger, cmdCameraOnAvatar));
registerCmd("AudioProcess::listSFX", WRAP_METHOD(Debugger, cmdListSFX));
registerCmd("AudioProcess::playSFX", WRAP_METHOD(Debugger, cmdPlaySFX));
registerCmd("AudioProcess::stopSFX", WRAP_METHOD(Debugger, cmdStopSFX));
registerCmd("Cheat::toggle", WRAP_METHOD(Debugger, cmdCheatMode));
registerCmd("Cheat::maxstats", WRAP_METHOD(Debugger, cmdMaxStats));
registerCmd("Cheat::heal", WRAP_METHOD(Debugger, cmdHeal));
registerCmd("Cheat::toggleInvincibility", WRAP_METHOD(Debugger, cmdInvincibility));
registerCmd("Cheat::items", WRAP_METHOD(Debugger, cmdCheatItems));
registerCmd("Cheat::equip", WRAP_METHOD(Debugger, cmdCheatEquip));
registerCmd("Cheat::hackMover", WRAP_METHOD(Debugger, cmdHackMover));
registerCmd("GameMapGump::toggleHighlightItems", WRAP_METHOD(Debugger, cmdHighlightItems));
registerCmd("GameMapGump::toggleFootpads", WRAP_METHOD(Debugger, cmdFootpads));
registerCmd("GameMapGump::gridlines", WRAP_METHOD(Debugger, cmdGridlines));
registerCmd("GameMapGump::dumpMap", WRAP_METHOD(Debugger, cmdDumpMap));
registerCmd("GameMapGump::dumpAllMaps", WRAP_METHOD(Debugger, cmdDumpAllMaps));
registerCmd("GameMapGump::incrementSortOrder", WRAP_METHOD(Debugger, cmdIncrementSortOrder));
registerCmd("GameMapGump::decrementSortOrder", WRAP_METHOD(Debugger, cmdDecrementSortOrder));
registerCmd("Kernel::processTypes", WRAP_METHOD(Debugger, cmdProcessTypes));
registerCmd("Kernel::processInfo", WRAP_METHOD(Debugger, cmdProcessInfo));
registerCmd("Kernel::listProcesses", WRAP_METHOD(Debugger, cmdListProcesses));
registerCmd("Kernel::toggleFrameByFrame", WRAP_METHOD(Debugger, cmdFrameByFrame));
registerCmd("Kernel::advanceFrame", WRAP_METHOD(Debugger, cmdAdvanceFrame));
registerCmd("MainActor::teleport", WRAP_METHOD(Debugger, cmdTeleport));
registerCmd("MainActor::mark", WRAP_METHOD(Debugger, cmdMark));
registerCmd("MainActor::recall", WRAP_METHOD(Debugger, cmdRecall));
registerCmd("MainActor::listmarks", WRAP_METHOD(Debugger, cmdListMarks));
registerCmd("MainActor::name", WRAP_METHOD(Debugger, cmdName));
registerCmd("MainActor::useBackpack", WRAP_METHOD(Debugger, cmdUseBackpack));
registerCmd("MainActor::useInventory", WRAP_METHOD(Debugger, cmdUseInventory));
registerCmd("MainActor::useRecall", WRAP_METHOD(Debugger, cmdUseRecall));
registerCmd("MainActor::useBedroll", WRAP_METHOD(Debugger, cmdUseBedroll));
registerCmd("MainActor::useKeyring", WRAP_METHOD(Debugger, cmdUseKeyring));
registerCmd("MainActor::nextWeapon", WRAP_METHOD(Debugger, cmdNextWeapon));
registerCmd("MainActor::nextInvItem", WRAP_METHOD(Debugger, cmdNextInventory));
registerCmd("MainActor::useInventoryItem", WRAP_METHOD(Debugger, cmdUseInventoryItem));
registerCmd("MainActor::useMedikit", WRAP_METHOD(Debugger, cmdUseMedikit));
registerCmd("MainActor::useEnergyCube", WRAP_METHOD(Debugger, cmdUseEnergyCube));
registerCmd("MainActor::detonateBomb", WRAP_METHOD(Debugger, cmdDetonateBomb));
registerCmd("MainActor::dropWeapon", WRAP_METHOD(Debugger, cmdDropWeapon));
registerCmd("MainActor::toggleCombat", WRAP_METHOD(Debugger, cmdCombat));
registerCmd("ItemSelectionProcess::startSelection", WRAP_METHOD(Debugger, cmdStartSelection));
registerCmd("ItemSelectionProcess::useSelectedItem", WRAP_METHOD(Debugger, cmdUseSelection));
registerCmd("ItemSelectionProcess::grabItems", WRAP_METHOD(Debugger, cmdGrabItems));
registerCmd("ObjectManager::objectTypes", WRAP_METHOD(Debugger, cmdObjectTypes));
registerCmd("ObjectManager::objectInfo", WRAP_METHOD(Debugger, cmdObjectInfo));
registerCmd("QuickAvatarMoverProcess::toggle", WRAP_METHOD(Debugger, cmdQuickMover));
registerCmd("QuickAvatarMoverProcess::toggleClipping", WRAP_METHOD(Debugger, cmdClipping));
registerCmd("UCMachine::getGlobal", WRAP_METHOD(Debugger, cmdGetGlobal));
registerCmd("UCMachine::setGlobal", WRAP_METHOD(Debugger, cmdSetGlobal));
registerCmd("UCMachine::traceObjID", WRAP_METHOD(Debugger, cmdTraceObjID));
registerCmd("UCMachine::tracePID", WRAP_METHOD(Debugger, cmdTracePID));
registerCmd("UCMachine::traceClass", WRAP_METHOD(Debugger, cmdTraceClass));
registerCmd("UCMachine::traceAll", WRAP_METHOD(Debugger, cmdTraceAll));
registerCmd("UCMachine::stopTrace", WRAP_METHOD(Debugger, cmdStopTrace));
registerCmd("FastAreaVisGump::toggle", WRAP_METHOD(Debugger, cmdToggleFastArea));
registerCmd("InverterProcess::invertScreen", WRAP_METHOD(Debugger, cmdInvertScreen));
registerCmd("MenuGump::showMenu", WRAP_METHOD(Debugger, cmdShowMenu));
registerCmd("MiniMapGump::toggle", WRAP_METHOD(Debugger, cmdToggleMinimap));
registerCmd("MiniMapGump::generate", WRAP_METHOD(Debugger, cmdGenerateMinimap));
registerCmd("MiniMapGump::clear", WRAP_METHOD(Debugger, cmdClearMinimap));
registerCmd("MovieGump::play", WRAP_METHOD(Debugger, cmdPlayMovie));
registerCmd("MusicProcess::playMusic", WRAP_METHOD(Debugger, cmdPlayMusic));
registerCmd("QuitGump::verifyQuit", WRAP_METHOD(Debugger, cmdVerifyQuit));
registerCmd("ShapeViewerGump::U8ShapeViewer", WRAP_METHOD(Debugger, cmdU8ShapeViewer));
registerCmd("RenderSurface::benchmark", WRAP_METHOD(Debugger, cmdBenchmarkRenderSurface));
#ifdef DEBUG_PATHFINDER
registerCmd("Pathfinder::visualDebug", WRAP_METHOD(Debugger, cmdVisualDebugPathfinder));
#endif
}
Debugger::~Debugger() {
g_debugger = nullptr;
}
bool Debugger::cmdSaveGame(int argc, const char **argv) {
if (argc == 2) {
if (!Ultima8Engine::get_instance()->canSaveGameStateCurrently()) {
debugPrintf("Saving game is currently unavailable\n");
return true;
}
// Save a _game with the given name into the quicksave slot
Common::Error result = Ultima8Engine::get_instance()->saveGameState(1, argv[1]);
if (result.getCode() != Common::kNoError) {
debugPrintf("Saving game failed: %s\n", result.getDesc().c_str());
return true;
}
} else {
Ultima8Engine::get_instance()->saveGameDialog();
}
return false;
}
bool Debugger::cmdLoadGame(int argc, const char **argv) {
if (argc == 2) {
// Load a _game from the quicksave slot. The second parameter is ignored,
// it just needs to be present to differentiate from showing the GUI load dialog
Ultima8Engine::get_instance()->loadGameState(1);
} else {
Ultima8Engine::get_instance()->loadGameDialog();
}
return false;
}
bool Debugger::cmdNewGame(int argc, const char **argv) {
Ultima8Engine::get_instance()->newGame();
return false;
}
bool Debugger::cmdQuit(int argc, const char **argv) {
Ultima8Engine::get_instance()->_isRunning = false;
return false;
}
bool Debugger::cmdEngineStats(int argc, const char **argv) {
Kernel::get_instance()->kernelStats();
ObjectManager::get_instance()->objectStats();
UCMachine::get_instance()->usecodeStats();
World::get_instance()->worldStats();
return true;
}
bool Debugger::cmdSetVideoMode(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("Usage: %s <width> <height>\n", argv[0]);
return true;
} else {
Ultima8Engine::get_instance()->changeVideoMode(strtol(argv[1], 0, 0), strtol(argv[2], 0, 0));
return false;
}
}
bool Debugger::cmdAvatarInStasis(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
bool flag = !g->isAvatarInStasis();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
g->setAvatarInStasis(flag);
debugPrintf("avatarInStasis = %s\n", strBool(flag));
return true;
}
bool Debugger::cmdShowEditorItems(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
bool flag = !g->isShowEditorItems();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
g->setShowEditorItems(flag);
debugPrintf("showEditorItems = %s\n", strBool(flag));
return false;
}
bool Debugger::cmdShowTouchingItems(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
bool flag = !g->isShowTouchingItems();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
g->setShowTouchingItems(flag);
debugPrintf("showTouchingItems = %s\n", strBool(flag));
return false;
}
bool Debugger::cmdCloseItemGumps(int argc, const char **argv) {
Ultima8Engine *g = Ultima8Engine::get_instance();
g->getDesktopGump()->CloseItemDependents();
return false;
}
bool Debugger::cmdListSFX(int argc, const char **argv) {
AudioProcess *ap = AudioProcess::get_instance();
if (!ap) {
debugPrintf("Error: No AudioProcess\n");
} else {
for (const auto &si : ap->_sampleInfo) {
debugPrintf("Sample: num %d, obj %d, loop %d, prio %d",
si._sfxNum, si._objId, si._loops, si._priority);
if (!si._barked.empty()) {
debugPrintf(", speech: \"%s\"",
si._barked.substr(si._curSpeechStart, si._curSpeechEnd - si._curSpeechStart).c_str());
}
debugPrintf("\n");
}
}
return true;
}
bool Debugger::cmdStopSFX(int argc, const char **argv) {
AudioProcess *ap = AudioProcess::get_instance();
if (!ap) {
debugPrintf("Error: No AudioProcess\n");
return true;
} else if (argc < 2) {
debugPrintf("Usage: %s <sfxNum> [objId]\n", argv[0]);
return true;
} else {
int sfxNum = static_cast<int>(strtol(argv[1], 0, 0));
ObjId objId = (argc >= 3) ? static_cast<ObjId>(strtol(argv[2], 0, 0)) : 0;
ap->stopSFX(sfxNum, objId);
return false;
}
}
bool Debugger::cmdPlaySFX(int argc, const char **argv) {
AudioProcess *ap = AudioProcess::get_instance();
if (!ap) {
debugPrintf("Error: No AudioProcess\n");
return true;
} else if (argc < 2) {
debugPrintf("Usage: %s <sfxNum>\n", argv[0]);
return true;
} else {
int sfxNum = static_cast<int>(strtol(argv[1], 0, 0));
ap->playSFX(sfxNum, 0x60, 0, 0);
return false;
}
}
bool Debugger::cmdCheatMode(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
bool flag = !g->areCheatsEnabled();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
g->setCheatMode(flag);
debugPrintf("Cheats = %s\n", strBool(flag));
return true;
}
bool Debugger::cmdMaxStats(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *mainActor = getMainActor();
if (GAME_IS_CRUSADER) {
mainActor->setHP(mainActor->getMaxHP());
mainActor->setMana(mainActor->getMaxMana());
} else {
// constants!!
mainActor->setStr(25);
mainActor->setDex(25);
mainActor->setInt(25);
mainActor->setHP(mainActor->getMaxHP());
mainActor->setMana(mainActor->getMaxMana());
AudioProcess *audioproc = AudioProcess::get_instance();
if (audioproc)
audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
}
return false;
}
bool Debugger::cmdCheatItems(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *av = getMainActor();
if (!av) return true;
Container *backpack = getContainer(av->getEquip(7)); // CONSTANT!
if (!backpack) return true;
// obsidian
Item *money = ItemFactory::createItem(143, 7, 500, 0, 0, 0, 0, true);
money->moveToContainer(backpack);
money->setGumpLocation(40, 20);
// skull of quakes
Item *skull = ItemFactory::createItem(814, 0, 0, 0, 0, 0, 0, true);
skull->moveToContainer(backpack);
skull->setGumpLocation(60, 20);
// recall item
Item *recall = ItemFactory::createItem(833, 0, 0, 0, 0, 0, 0, true);
recall->moveToContainer(backpack);
recall->setGumpLocation(20, 20);
// sword
Item *sword = ItemFactory::createItem(420, 0, 0, 0, 0, 0, 0, true);
sword->moveToContainer(backpack);
sword->setGumpLocation(20, 30);
Item *flamesting = ItemFactory::createItem(817, 0, 0, 0, 0, 0, 0, true);
flamesting->moveToContainer(backpack);
flamesting->setGumpLocation(20, 30);
Item *hammer = ItemFactory::createItem(815, 0, 0, 0, 0, 0, 0, true);
hammer->moveToContainer(backpack);
hammer->setGumpLocation(20, 30);
Item *slayer = ItemFactory::createItem(816, 0, 0, 0, 0, 0, 0, true);
slayer->moveToContainer(backpack);
slayer->setGumpLocation(20, 30);
// necromancy reagents
Item *bagitem = ItemFactory::createItem(637, 0, 0, 0, 0, 0, 0, true);
bagitem->moveToContainer(backpack);
bagitem->setGumpLocation(70, 40);
bagitem = ItemFactory::createItem(637, 0, 0, 0, 0, 0, 0, true);
Container *bag = dynamic_cast<Container *>(bagitem);
Item *reagents = ItemFactory::createItem(395, 0, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(10, 10);
reagents = ItemFactory::createItem(395, 6, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(30, 10);
reagents = ItemFactory::createItem(395, 8, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(50, 10);
reagents = ItemFactory::createItem(395, 9, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(20, 30);
reagents = ItemFactory::createItem(395, 10, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(40, 30);
reagents = ItemFactory::createItem(395, 14, 50, 0, 0, 0, 0, true);
reagents->moveToContainer(bag);
reagents->setGumpLocation(60, 30);
bagitem->moveToContainer(backpack);
bagitem->setGumpLocation(70, 20);
// theurgy foci
bagitem = ItemFactory::createItem(637, 0, 0, 0, 0, 0, 0, true);
bag = dynamic_cast<Container *>(bagitem);
Item *focus = ItemFactory::createItem(396, 8, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(10, 10);
focus = ItemFactory::createItem(396, 9, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(25, 10);
focus = ItemFactory::createItem(396, 10, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(40, 10);
focus = ItemFactory::createItem(396, 11, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(55, 10);
focus = ItemFactory::createItem(396, 12, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(70, 10);
focus = ItemFactory::createItem(396, 13, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(10, 30);
focus = ItemFactory::createItem(396, 14, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(30, 30);
focus = ItemFactory::createItem(396, 15, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(50, 30);
focus = ItemFactory::createItem(396, 17, 0, 0, 0, 0, 0, true);
focus->moveToContainer(bag);
focus->setGumpLocation(70, 30);
bagitem->moveToContainer(backpack);
bagitem->setGumpLocation(0, 30);
// oil flasks
Item *flask = ItemFactory::createItem(579, 0, 0, 0, 0, 0, 0, true);
flask->moveToContainer(backpack);
flask->setGumpLocation(30, 40);
flask = ItemFactory::createItem(579, 0, 0, 0, 0, 0, 0, true);
flask->moveToContainer(backpack);
flask->setGumpLocation(30, 40);
flask = ItemFactory::createItem(579, 0, 0, 0, 0, 0, 0, true);
flask->moveToContainer(backpack);
flask->setGumpLocation(30, 40);
// zealan shield
Item *shield = ItemFactory::createItem(828, 0, 0, 0, 0, 0, 0, true);
shield->moveToContainer(backpack);
shield->randomGumpLocation();
shield = ItemFactory::createItem(539, 0, 0, 0, 0, 0, 0, true);
shield->moveToContainer(backpack);
shield->randomGumpLocation();
// armour
Item *armour = ItemFactory::createItem(64, 0, 0, 0, 0, 0, 0, true);
armour->moveToContainer(backpack);
armour->randomGumpLocation();
// death disks
Item *disk = ItemFactory::createItem(750, 0, 0, 0, 0, 0, 0, true);
disk->moveToContainer(backpack);
disk->randomGumpLocation();
disk = ItemFactory::createItem(750, 0, 0, 0, 0, 0, 0, true);
disk->moveToContainer(backpack);
disk->randomGumpLocation();
disk = ItemFactory::createItem(750, 0, 0, 0, 0, 0, 0, true);
disk->moveToContainer(backpack);
disk->randomGumpLocation();
return false;
}
bool Debugger::cmdCheatEquip(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *av = getMainActor();
if (!av) return false;
Container *backpack = getContainer(av->getEquip(7)); // CONSTANT!
if (!backpack) return false;
Item *item;
// move all current equipment to backpack
for (unsigned int i = 0; i < 7; ++i) {
item = getItem(av->getEquip(i));
if (item) {
item->moveToContainer(backpack, false); // no weight/volume check
item->randomGumpLocation();
}
}
// give new equipment:
// deceiver
item = ItemFactory::createItem(822, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
// armour
item = ItemFactory::createItem(841, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
// shield
item = ItemFactory::createItem(842, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
// helmet
item = ItemFactory::createItem(843, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
// arm guards
item = ItemFactory::createItem(844, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
// leggings
item = ItemFactory::createItem(845, 0, 0, 0, 0, 0, 0, true);
av->setEquip(item, false);
return false;
}
bool Debugger::cmdHeal(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *mainActor = getMainActor();
mainActor->setHP(mainActor->getMaxHP());
mainActor->setMana(mainActor->getMaxMana());
return false;
}
bool Debugger::cmdInvincibility(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
if (!g->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *av = getMainActor();
bool flag = !av->hasActorFlags(Actor::ACT_INVINCIBLE);
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
if (flag) {
av->setActorFlag(Actor::ACT_INVINCIBLE);
debugPrintf("Avatar invincible.\n");
} else {
av->clearActorFlag(Actor::ACT_INVINCIBLE);
debugPrintf("Avatar is no longer invincible.\n");
}
return true;
}
bool Debugger::cmdHackMover(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Ultima8Engine *g = Ultima8Engine::get_instance();
bool flag = !g->isHackMoverEnabled();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
g->setHackMoverEnabled(flag);
debugPrintf("Hack mover = %s\n", strBool(flag));
return false;
}
bool Debugger::cmdHighlightItems(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
bool flag = !GameMapGump::is_highlightItems();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
GameMapGump::Set_highlightItems(flag);
return false;
}
bool Debugger::cmdFootpads(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
bool flag = !GameMapGump::getShowFootpads();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
GameMapGump::setShowFootpads(flag);
return false;
}
bool Debugger::cmdGridlines(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off|<number>]\n", argv[0]);
return true;
}
int gridlines = GameMapGump::getGridlines() == 0 ? -1 : 0;
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
gridlines = -1;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
gridlines = 0;
else
gridlines = atoi(argv[1]);
}
// ensure a sane minimum value
if (gridlines > 0 && gridlines < 8) {
gridlines = 8;
}
GameMapGump::setGridlines(gridlines);
return false;
}
void Debugger::dumpCurrentMap() {
// Increase number of available object IDs.
ObjectManager::get_instance()->allow64kObjects();
// top/bottom/left/right render coordinates
int32 left = 16384;
int32 right = -16384;
int32 top = 16384;
int32 bot = -16384;
const int32 camheight = 256;
const CurrentMap *curmap = World::get_instance()->getCurrentMap();
const int32 chunksize = curmap->getChunkSize();
// Work out the map limits in chunks
for (int32 y = 0; y < MAP_NUM_CHUNKS; y++) {
for (int32 x = 0; x < MAP_NUM_CHUNKS; x++) {
const Std::list<Item *> *list = curmap->getItemList(x, y);
// Should iterate the items!
// (items could extend outside of this chunk and they have height)
if (list && list->size() != 0) {
// Bounds of render coordinates for items in this chunk
int32 l = (x * chunksize - y * chunksize) / 4 - (chunksize / 4);
int32 r = (x * chunksize - y * chunksize) / 4 + (chunksize / 4);
int32 t = (x * chunksize + y * chunksize) / 8 - (chunksize / 2);
int32 b = (x * chunksize + y * chunksize) / 8;
t -= 256; // approx. adjustment for height of items in chunk
if (l < left) left = l;
if (r > right) right = r;
if (t < top) top = t;
if (b > bot) bot = b;
}
}
}
if (right == -16384) {
debugPrintf("Map seems empty, nothing to dump.\n");
// No objects?
return;
}
// camera height
bot += camheight;
top += camheight;
const int32 awidth = right - left;
const int32 aheight = bot - top;
//
// If you are doing a once-off dump and get this error, try building ScummVM
// with int32 size for Surfaces. It breaks other engines but this is just a
// once-off type debugging feature anyway.
//
// Most U8 maps can be dumped without needing int32, but most Crusader maps
// need a patch to work.
//
Graphics::Surface nullsurface;
if ((sizeof(nullsurface.pitch) == 2 && awidth > 8191) ||
(sizeof(nullsurface.h) == 2 && aheight > 32767 )) {
warning("WARN: Can't dump map, image will not fit into 16 bit dimensions.");
return;
}
GameMapGump *g = new GameMapGump(0, 0, awidth, aheight);
// HACK: Setting both INVISIBLE and TRANSPARENT flags on the Avatar
// will make him completely invisible.
getMainActor()->setFlag(Item::FLG_INVISIBLE);
getMainActor()->setExtFlag(Item::EXT_TRANSPARENT);
CurrentMap *currentMap = World::get_instance()->getCurrentMap();
currentMap->setWholeMapFast();
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
RenderSurface *s = new RenderSurface(awidth, aheight, screen->format);
debugPrintf("Rendering map...\n");
// Camera coordinates in world-coords (in the middle of the map)
int32 midx = left + (right - left) / 2;
int32 midy = top + (bot - top) / 2;
int32 cx = midx * 2 + midy * 4;
int32 cy = midy * 4 - midx * 2;
// Now render the map
s->BeginPainting();
s->SetOrigin(0, 0);
Point3 pt(cx + camheight * 4, cy + camheight * 4, camheight);
CameraProcess::SetCameraProcess(new CameraProcess(pt));
g->Paint(s, 256, false);
s->EndPainting();
#ifdef USE_PNG
Common::Path filename(Common::String::format("map_%03d.png", currentMap->getNum()));
#else
Common::Path filename(Common::String::format("map_%03d.bmp", currentMap->getNum()));
#endif
Common::DumpFile dumpFile;
bool result = dumpFile.open(filename);
if (result) {
#ifdef USE_PNG
result = Image::writePNG(dumpFile, *(s->getRawSurface()));
#else
result = Image::writeBMP(dumpFile, *(s->getRawSurface()));
#endif
}
if (result) {
debugPrintf("Map dumped: %s\n", filename.toString().c_str());
} else {
debugPrintf("Could not write file: %s\n", filename.toString().c_str());
}
delete g;
delete s;
}
bool Debugger::cmdDumpMap(int argc, const char **argv) {
// Save because we're going to potentially break the game by enlarging
// the fast area and available object IDs.
int slot = Ultima8Engine::get_instance()->getAutosaveSlot();
Common::Error result = Ultima8Engine::get_instance()->saveGameState(slot, "Pre-dumpMap save");
if (result.getCode() != Common::kNoError) {
debugPrintf("Could not dump map: pre-dumpMap save failed\n");
return false;
}
if (argc > 1) {
int mapno = atoi(argv[1]);
debugPrintf("Switching to map %d\n", mapno);
bool success = World::get_instance()->switchMap(mapno);
if (!success) {
debugPrintf("Dump failed: switch to map %d FAILED\n", mapno);
return false;
}
}
dumpCurrentMap();
// Reload
Ultima8Engine::get_instance()->loadGameState(slot);
return false;
}
bool Debugger::cmdDumpAllMaps(int argc, const char **argv) {
// Save because we're going to potentially break the game by enlarging
// the fast area and available object IDs and changing maps
int slot = Ultima8Engine::get_instance()->getAutosaveSlot();
Common::Error result = Ultima8Engine::get_instance()->saveGameState(slot, "Pre-dumpMap save");
if (result.getCode() != Common::kNoError) {
debugPrintf("Could not dump map: pre-dumpMap save failed\n");
return false;
}
for (int i = 0; i < 256; i++) {
if (World::get_instance()->switchMap(i)) {
debugPrintf("Dumping map %d...\n", i);
dumpCurrentMap();
}
}
// Reload
Ultima8Engine::get_instance()->loadGameState(slot);
return false;
}
bool Debugger::cmdIncrementSortOrder(int argc, const char **argv) {
int32 count = argc > 1 ? strtol(argv[1], 0, 0) : 1;
GameMapGump *gump = Ultima8Engine::get_instance()->getGameMapGump();
if (gump)
gump->IncSortOrder(count);
return false;
}
bool Debugger::cmdDecrementSortOrder(int argc, const char **argv) {
int32 count = argc > 1 ? strtol(argv[1], 0, 0) : 1;
GameMapGump *gump = Ultima8Engine::get_instance()->getGameMapGump();
if (gump)
gump->IncSortOrder(-count);
return false;
}
bool Debugger::cmdProcessTypes(int argc, const char **argv) {
Kernel::get_instance()->processTypes();
return true;
}
bool Debugger::cmdListProcesses(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [itemnum]\n", argv[0]);
} else {
Kernel *kern = Kernel::get_instance();
ObjId item = 0;
if (argc == 2) {
item = static_cast<ObjId>(strtol(argv[1], 0, 0));
debugPrintf("Processes for item %d:\n", item);
} else {
debugPrintf("Processes:\n");
}
for (const auto *p : kern->_processes) {
if (argc == 1 || p->_itemNum == item) {
debugPrintf("%s\n", p->dumpInfo().c_str());
}
}
}
return true;
}
bool Debugger::cmdProcessInfo(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <objectnum>\n", argv[0]);
} else {
Kernel *kern = Kernel::get_instance();
ProcId procid = static_cast<ProcId>(strtol(argv[1], 0, 0));
Process *p = kern->getProcess(procid);
if (p == 0) {
debugPrintf("No such process: %d\n", procid);
} else {
debugPrintf("%s\n", p->dumpInfo().c_str());
}
}
return true;
}
bool Debugger::cmdFrameByFrame(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
Kernel *kern = Kernel::get_instance();
bool flag = !kern->isFrameByFrame();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
kern->setFrameByFrame(flag);
debugPrintf("FrameByFrame = %s\n", strBool(flag));
if (flag)
kern->pause();
else
kern->unpause();
return true;
}
bool Debugger::cmdAdvanceFrame(int argc, const char **argv) {
Kernel *kern = Kernel::get_instance();
if (kern->isFrameByFrame()) {
kern->unpause();
debugPrintf("FrameByFrame: Next Frame\n");
}
return true;
}
bool Debugger::cmdTeleport(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
MainActor *mainActor = getMainActor();
int curmap = mainActor->getMapNum();
switch (argc - 1) {
case 1:
mainActor->teleport(curmap,
strtol(argv[1], 0, 0));
break;
case 2:
mainActor->teleport(strtol(argv[1], 0, 0),
strtol(argv[2], 0, 0));
break;
case 3:
mainActor->teleport(curmap,
strtol(argv[1], 0, 0),
strtol(argv[2], 0, 0),
strtol(argv[3], 0, 0));
break;
case 4:
mainActor->teleport(strtol(argv[1], 0, 0),
strtol(argv[2], 0, 0),
strtol(argv[3], 0, 0),
strtol(argv[4], 0, 0));
break;
default:
debugPrintf("Usage:\n");
debugPrintf("%s <mapnum> <x> <y> <z>: teleport to (x,y,z) on map mapnum\n", argv[0]);
debugPrintf("%s <x> <y> <z>: teleport to (x,y,z) on current map\n", argv[0]);
debugPrintf("%s <mapnum> <eggnum>: teleport to target egg eggnum on map mapnum\n", argv[0]);
debugPrintf("%s <eggnum>: teleport to target egg eggnum on current map\n", argv[0]);
return true;
}
return false;
}
bool Debugger::cmdMark(int argc, const char **argv) {
if (argc == 1) {
debugPrintf("Usage: %s <mark>: set named mark to this location\n", argv[0]);
return true;
}
MainActor *mainActor = getMainActor();
int curmap = mainActor->getMapNum();
Point3 pt = mainActor->getLocation();
Common::String key = Common::String::format("mark_%s", argv[1]);
Common::String value = Common::String::format("%d %d %d %d", curmap, pt.x, pt.y, pt.z);
ConfMan.set(key, value);
debugPrintf("Set mark \"%s\" to %s\n", argv[1], value.c_str());
return true;
}
bool Debugger::cmdRecall(int argc, const char **argv) {
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats are disabled\n");
return true;
}
if (argc == 1) {
debugPrintf("Usage: %s <mark>: recall to named mark\n", argv[0]);
return true;
}
MainActor *mainActor = getMainActor();
Common::String key = Common::String::format("mark_%s", argv[1]);
if (!ConfMan.hasKey(key)) {
debugPrintf("recall: no such mark\n");
return true;
}
Common::String target = ConfMan.get(key);
int t[4];
int n = sscanf(target.c_str(), "%d%d%d%d", &t[0], &t[1], &t[2], &t[3]);
if (n != 4) {
debugPrintf("recall: invalid mark\n");
return true;
}
mainActor->teleport(t[0], t[1], t[2], t[3]);
return false;
}
bool Debugger::cmdListMarks(int argc, const char **argv) {
const Common::ConfigManager::Domain *domain = ConfMan.getActiveDomain();
Common::ConfigManager::Domain::const_iterator dit;
Common::StringArray marks;
for (dit = domain->begin(); dit != domain->end(); ++dit) {
if (dit->_key.hasPrefix("mark_")) {
marks.push_back(dit->_key.substr(5));
}
}
Common::sort(marks.begin(), marks.end());
for (const auto &m : marks) {
debugPrintf("%s\n", m.c_str());
}
return true;
}
bool Debugger::cmdName(int argc, const char **argv) {
MainActor *av = getMainActor();
if (argc > 1)
av->setName(argv[1]);
debugPrintf("MainActor::name = \"%s\"\n", av->getName().c_str());
return true;
}
bool Debugger::cmdUseBackpack(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use backpack: avatarInStasis");
return false;
}
MainActor *av = getMainActor();
Item *backpack = getItem(av->getEquip(ShapeInfo::SE_BACKPACK));
if (backpack)
backpack->callUsecodeEvent_use();
return false;
}
bool Debugger::cmdNextInventory(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use inventory: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->nextInvItem();
return false;
}
bool Debugger::cmdNextWeapon(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't change weapon: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->nextWeapon();
return false;
}
bool Debugger::cmdUseInventoryItem(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use active inventory item: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
ObjId activeitemid = av->getActiveInvItem();
if (activeitemid) {
Item *item = getItem(activeitemid);
if (item) {
av->useInventoryItem(item);
}
}
return false;
}
bool Debugger::cmdUseMedikit(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use medikit: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->useInventoryItem(0x351);
return false;
}
bool Debugger::cmdUseEnergyCube(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use energy cube: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->useInventoryItem(0x582);
return false;
}
bool Debugger::cmdDetonateBomb(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't detonate bomb: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->detonateBomb();
return false;
}
bool Debugger::cmdDropWeapon(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't drop weapon: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
MainActor *av = getMainActor();
av->dropWeapon();
return false;
}
bool Debugger::cmdUseInventory(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use inventory: avatarInStasis");
return false;
}
MainActor *av = getMainActor();
av->callUsecodeEvent_use();
return false;
}
bool Debugger::cmdUseRecall(int argc, const char **argv) {
MainActor *av = getMainActor();
if (GAME_IS_U8)
av->useInventoryItem(833);
else {
TargetReticleProcess *reticle = TargetReticleProcess::get_instance();
if (reticle) {
reticle->toggle();
}
}
return false;
}
bool Debugger::cmdUseBedroll(int argc, const char **argv) {
MainActor *av = getMainActor();
av->useInventoryItem(534);
return false;
}
bool Debugger::cmdUseKeyring(int argc, const char **argv) {
MainActor *av = getMainActor();
av->useInventoryItem(79);
return false;
}
bool Debugger::cmdCameraOnAvatar(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isCruStasis()) {
debugPrintf("Can't move camera: cruStasis");
return false;
}
Actor *actor = getControlledActor();
if (actor) {
Point3 pt = actor->getCentre();
if (pt.x > 0 || pt.y > 0)
CameraProcess::SetCameraProcess(new CameraProcess(pt));
}
return false;
}
bool Debugger::cmdCombat(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't toggle combat: avatarInStasis");
return false;
}
MainActor *av = getMainActor();
bool flag = !av->isInCombat();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
if (flag)
av->setInCombat(0);
else
av->clearInCombat();
return false;
}
bool Debugger::cmdStartSelection(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't select items: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
// Clear this flag on selection to match original behavior.
Ultima8Engine::get_instance()->setCrusaderTeleporting(false);
ItemSelectionProcess *proc = ItemSelectionProcess::get_instance();
if (proc)
proc->selectNextItem(false);
return false;
}
bool Debugger::cmdUseSelection(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't use items: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
ItemSelectionProcess *proc = ItemSelectionProcess::get_instance();
if (proc)
proc->useSelectedItem();
return false;
}
bool Debugger::cmdGrabItems(int argc, const char **argv) {
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugPrintf("Can't grab items: avatarInStasis");
return false;
}
if (!Ultima8Engine::get_instance()->isAvatarControlled()) {
return false;
}
// Clear this flag on selection to match original behavior.
Ultima8Engine::get_instance()->setCrusaderTeleporting(false);
ItemSelectionProcess *proc = ItemSelectionProcess::get_instance();
if (proc)
proc->selectNextItem(true);
return false;
}
bool Debugger::cmdObjectTypes(int argc, const char **argv) {
ObjectManager::get_instance()->objectTypes();
return true;
}
bool Debugger::cmdObjectInfo(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <objectnum>\n", argv[0]);
} else {
ObjectManager *objMan = ObjectManager::get_instance();
ObjId objid = static_cast<ObjId>(strtol(argv[1], 0, 0));
Object *obj = objMan->getObject(objid);
if (obj == 0) {
bool reserved = false;
if (objid >= 256) // CONSTANT!
reserved = objMan->_objIDs->isIDUsed(objid);
else
reserved = objMan->_actorIDs->isIDUsed(objid);
if (reserved)
debugPrintf("Reserved objid: %d\n", objid);
else
debugPrintf("No such object: %d\n", objid);
} else {
debugPrintf("%s\n", obj->dumpInfo().c_str());
}
}
return true;
}
bool Debugger::cmdQuickMover(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
bool flag = !QuickAvatarMoverProcess::isEnabled();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
QuickAvatarMoverProcess::setEnabled(flag);
debugPrintf("QuickAvatarMoverProcess::_enabled = %s\n", strBool(flag));
return false;
}
bool Debugger::cmdClipping(int argc, const char **argv) {
if (argc > 2) {
debugPrintf("Usage: %s [on|off]\n", argv[0]);
return true;
}
if (!Ultima8Engine::get_instance()->areCheatsEnabled()) {
debugPrintf("Cheats aren't enabled\n");
return true;
}
bool flag = !QuickAvatarMoverProcess::isClipping();
if (argc > 1) {
if (scumm_stricmp(argv[1], "on") == 0 || scumm_stricmp(argv[1], "true") == 0)
flag = true;
else if (scumm_stricmp(argv[1], "off") == 0 || scumm_stricmp(argv[1], "false") == 0)
flag = false;
}
QuickAvatarMoverProcess::setClipping(flag);
debugPrintf("QuickAvatarMoverProcess::_clipping = %s\n", strBool(flag));
return true;
}
bool Debugger::cmdGetGlobal(int argc, const char **argv) {
UCMachine *uc = UCMachine::get_instance();
if (argc != 3) {
debugPrintf("Usage: %s <offset> <size>\n", argv[0]);
return true;
}
unsigned int offset = strtol(argv[1], 0, 0);
unsigned int size = strtol(argv[2], 0, 0);
debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getEntries(offset, size));
return true;
}
bool Debugger::cmdSetGlobal(int argc, const char **argv) {
UCMachine *uc = UCMachine::get_instance();
if (argc != 4) {
debugPrintf("Usage: %s <offset> <size> <value>\n", argv[0]);
return true;
}
unsigned int offset = strtol(argv[1], 0, 0);
unsigned int size = strtol(argv[2], 0, 0);
unsigned int value = strtol(argv[3], 0, 0);
uc->_globals->setEntries(offset, size, value);
debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getEntries(offset, size));
return true;
}
bool Debugger::cmdTracePID(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <pid>\n", argv[0]);
return true;
}
uint16 pid = static_cast<uint16>(strtol(argv[1], 0, 0));
UCMachine *uc = UCMachine::get_instance();
uc->_tracingEnabled = true;
uc->_tracePIDs.insert(pid);
debugPrintf("UCMachine: tracing process %d\n", pid);
return true;
}
bool Debugger::cmdTraceObjID(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <objid>\n", argv[0]);
return true;
}
uint16 objid = static_cast<uint16>(strtol(argv[1], 0, 0));
UCMachine *uc = UCMachine::get_instance();
uc->_tracingEnabled = true;
uc->_traceObjIDs.insert(objid);
debugPrintf("UCMachine: tracing object %d\n", objid);
return true;
}
bool Debugger::cmdTraceClass(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <class>\n", argv[0]);
return true;
}
uint16 ucclass = static_cast<uint16>(strtol(argv[1], 0, 0));
UCMachine *uc = UCMachine::get_instance();
uc->_tracingEnabled = true;
uc->_traceClasses.insert(ucclass);
debugPrintf("UCMachine: tracing class %d\n", ucclass);
return true;
}
bool Debugger::cmdTraceAll(int argc, const char **argv) {
UCMachine *uc = UCMachine::get_instance();
uc->_tracingEnabled = true;
uc->_traceAll = true;
debugPrintf("UCMachine: tracing all usecode\n");
return true;
}
bool Debugger::cmdStopTrace(int argc, const char **argv) {
UCMachine *uc = UCMachine::get_instance();
uc->_traceObjIDs.clear();
uc->_tracePIDs.clear();
uc->_traceClasses.clear();
uc->_tracingEnabled = false;
uc->_traceAll = false;
debugPrintf("Trace stopped\n");
return true;
}
bool Debugger::cmdVerifyQuit(int argc, const char **argv) {
QuitGump::verifyQuit();
return false;
}
bool Debugger::cmdU8ShapeViewer(int argc, const char **argv) {
ShapeViewerGump::U8ShapeViewer();
return false;
}
bool Debugger::cmdShowMenu(int argc, const char **argv) {
World *world = World::get_instance();
// In Crusader escape is also used to stop controlling another NPC
if (world && world->getControlledNPCNum() != kMainActorId) {
world->setControlledNPCNum(kMainActorId);
return false;
}
if (Ultima8Engine::get_instance()->isCruStasis()) {
Ultima8Engine::get_instance()->moveKeyEvent();
debugPrintf("Not opening menu: cruStasis\n");
return false;
}
Gump *gump = Ultima8Engine::get_instance()->getDesktopGump()->FindGump<ModalGump>();
if (gump) {
// ensure any modal gump gets the message to close before we open the menu.
gump->Close();
return false;
}
MenuGump::showMenu();
return false;
}
bool Debugger::cmdToggleFastArea(int argc, const char **argv) {
Ultima8Engine *app = Ultima8Engine::get_instance();
Gump *desktop = app->getDesktopGump();
Gump *favg = desktop->FindGump<FastAreaVisGump>();
if (!favg) {
favg = new FastAreaVisGump;
favg->InitGump(0);
favg->setRelativePosition(Gump::TOP_RIGHT, -4, 4);
} else {
favg->Close();
}
return false;
}
bool Debugger::cmdInvertScreen(int argc, const char **argv) {
InverterProcess::invertScreen();
return false;
}
bool Debugger::cmdPlayMovie(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <moviename>\n", argv[0]);
return true;
}
Common::String filename = Common::String::format("static/%s.skf", argv[1]);
auto *skf = new Common::File();
if (!skf->open(filename.c_str())) {
debugPrintf("movie not found.\n");
delete skf;
return true;
}
MovieGump::U8MovieViewer(skf, false, false, true);
return false;
}
bool Debugger::cmdPlayMusic(int argc, const char **argv) {
if (MusicProcess::_theMusicProcess) {
if (argc != 2) {
debugPrintf("Usage: %s <tracknum>\n", argv[0]);
} else {
debugPrintf("Playing track %s\n", argv[1]);
MusicProcess::_theMusicProcess->playMusic_internal(atoi(argv[1]));
return false;
}
} else {
debugPrintf("No Music Process\n");
}
return true;
}
bool Debugger::cmdToggleMinimap(int argc, const char **argv) {
Ultima8Engine *app = Ultima8Engine::get_instance();
Gump *desktop = app->getDesktopGump();
Gump *mmg = desktop->FindGump<MiniMapGump>();
if (!mmg) {
mmg = new MiniMapGump(4, 4);
mmg->InitGump(0);
mmg->setRelativePosition(Gump::TOP_LEFT, 4, 4);
} else if (mmg->IsHidden()) {
mmg->UnhideGump();
} else {
mmg->HideGump();
}
return false;
}
bool Debugger::cmdGenerateMinimap(int argc, const char **argv) {
Ultima8Engine *app = Ultima8Engine::get_instance();
Gump *desktop = app->getDesktopGump();
MiniMapGump *gump = dynamic_cast<MiniMapGump *>(desktop->FindGump<MiniMapGump>());
if (gump) {
gump->generate();
if (argc > 1) {
Common::Path filename(argv[1]);
bool result = gump->dump(filename);
if (result) {
debugPrintf("Mini map dumped: %s\n", filename.toString().c_str());
} else {
debugPrintf("Could not write file: %s\n", filename.toString().c_str());
}
}
}
return false;
}
bool Debugger::cmdClearMinimap(int argc, const char **argv) {
Ultima8Engine *app = Ultima8Engine::get_instance();
Gump *desktop = app->getDesktopGump();
MiniMapGump *gump = dynamic_cast<MiniMapGump *>(desktop->FindGump<MiniMapGump>());
if (gump) {
gump->clear();
}
return false;
}
bool Debugger::cmdBenchmarkRenderSurface(int argc, const char **argv) {
if (argc != 4) {
debugPrintf("Usage: %s <shapenum> <framenum> <iterations>\n", argv[0]);
return true;
}
int shapenum = atoi(argv[1]);
int frame = atoi(argv[2]);
int count = atoi(argv[3]);
GameData *gamedata = GameData::get_instance();
Shape *s = gamedata->getMainShapes()->getShape(shapenum);
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
RenderSurface *surface = new RenderSurface(320, 200, screen->format);
surface->BeginPainting();
uint32 start, end;
uint32 blendColor = TEX32_PACK_RGBA(0x7F, 0x00, 0x00, 0x7F);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->Paint(s, frame, 160, 100);
}
end = g_system->getMillis();
debugPrintf("Paint: %d\n", end - start);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->PaintTranslucent(s, frame, 160, 100);
}
end = g_system->getMillis();
debugPrintf("PaintTranslucent: %d\n", end - start);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->Paint(s, frame, 160, 100, true);
}
end = g_system->getMillis();
debugPrintf("PaintMirrored: %d\n", end - start);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->PaintInvisible(s, frame, 160, 100, false, false);
}
end = g_system->getMillis();
debugPrintf("PaintInvisible: %d\n", end - start);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->PaintHighlight(s, frame, 160, 100, false, false, blendColor);
}
end = g_system->getMillis();
debugPrintf("PaintHighlight: %d\n", end - start);
start = g_system->getMillis();
for (int i = 0; i < count; i++) {
surface->PaintHighlightInvis(s, frame, 160, 100, false, false, blendColor);
}
end = g_system->getMillis();
debugPrintf("PaintHighlightInvis: %d\n", end - start);
surface->EndPainting();
delete surface;
return true;
}
bool Debugger::cmdVisualDebugPathfinder(int argc, const char **argv) {
#ifdef DEBUG_PATHFINDER
if (argc != 2) {
debugPrintf("Usage: %s <objid>\n", argv[0]);
debugPrintf("Specify objid -1 to stop tracing.\n");
return true;
}
int p = strtol(argv[1], 0, 0);
if (p == -1) {
Pathfinder::_visualDebugActor = 0xFFFF;
debugPrintf("Pathfinder: stopped visual tracing\n");
} else {
Pathfinder::_visualDebugActor = (uint16)p;
debugPrintf("Pathfinder: visually tracing _actor %d\n", Pathfinder::_visualDebugActor);
}
#endif
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima