scummvm/engines/mm/mm1/console.cpp
2023-02-12 20:44:50 -08:00

476 lines
12 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/file.h"
#include "common/savefile.h"
#include "common/system.h"
#include "mm/shared/utils/strings.h"
#include "mm/mm1/console.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/events.h"
#include "mm/mm1/game/spells_party.h"
namespace MM {
namespace MM1 {
Console::Console() : GUI::Debugger() {
registerCmd("dump_map", WRAP_METHOD(Console, cmdDumpMap));
registerCmd("dump_monsters", WRAP_METHOD(Console, cmdDumpMonsters));
registerCmd("dump_items", WRAP_METHOD(Console, cmdDumpItems));
registerCmd("map_string", WRAP_METHOD(Console, cmdMapString));
registerCmd("map", WRAP_METHOD(Console, cmdMap));
registerCmd("pos", WRAP_METHOD(Console, cmdPos));
registerCmd("intangible", WRAP_METHOD(Console, cmdIntangible));
registerCmd("cast", WRAP_METHOD(Console, cmdCast));
registerCmd("spells", WRAP_METHOD(Console, cmdSpellsAll));
registerCmd("encounter", WRAP_METHOD(Console, cmdEncounter));
registerCmd("encounters", WRAP_METHOD(Console, cmdEncounters));
registerCmd("specials", WRAP_METHOD(Console, cmdSpecials));
registerCmd("special", WRAP_METHOD(Console, cmdSpecial));
}
bool Console::cmdDumpMap(int argc, const char **argv) {
Common::File f;
Common::OutSaveFile *dest;
if (argc != 2) {
debugPrintf("%s <roomNum>\n", argv[0]);
} else {
int mapId = strToInt(argv[1]);
Maps::Map *map = g_globals->_maps.getMap(mapId);
// Dump the map data
if (f.open("mazedata.dta")) {
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-maze.bin", mapId), false);
assert(dest);
byte buffer[512];
f.seek(mapId * 512);
f.read(buffer, 512);
dest->write(buffer, 512);
dest->finalize();
delete dest;
f.close();
}
if (f.open(Common::String::format("%s.ovr", map->getName().c_str()))) {
int magicId = f.readUint16LE();
int codePtr = f.readUint16LE();
int codeSize = f.readUint16LE();
int dataPtr = f.readUint16LE();
int dataSize = f.readUint16LE();
int extrasSize = f.readUint16LE();
int startPtr = f.readUint16LE();
assert(magicId == 0xf2);
assert(startPtr >= codePtr &&
startPtr < (codePtr + codeSize));
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-code.bin", mapId), false);
byte *code = new byte[codeSize];
f.read(code, codeSize);
for (int i = 0; i < (codePtr % 16); ++i)
dest->writeByte(0);
dest->write(code, codeSize);
dest->flush();
delete dest;
delete[] code;
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-data.bin", mapId), false);
byte *data = new byte[dataSize];
f.read(data, dataSize);
dest->write(data, dataSize);
dest->flush();
delete dest;
delete[] data;
f.close();
debugPrintf("data: ptr=%xh, size=%xh\n", dataPtr, dataSize);
debugPrintf("code: ptr=%xh, size=%xh start=%xh\n",
codePtr, codeSize, startPtr);
debugPrintf("Extras size=%xh\n", extrasSize);
}
debugPrintf("Done.\n");
}
return true;
}
bool Console::cmdDumpMonsters(int argc, const char **argv) {
Common::File f;
Common::DumpFile df;
if (f.open("mm.exe")) {
if (df.open("monsters.txt")) {
f.seek(0x1b312);
for (int i = 0; i < 195; ++i) {
Common::String line = "\"";
for (int j = 0; j < 15; ++j)
line += f.readByte();
line += '"';
for (int j = 0; j < 16; ++j) {
line += ", ";
int val = (j == 7) ? f.readUint16LE() : f.readByte();
line += Common::String::format("%d", val);
}
df.writeString(line);
df.writeByte('\n');
}
df.close();
f.close();
debugPrintf("Done\n");
return true;
}
}
debugPrintf("Could not create\n");
return true;
}
bool Console::cmdDumpItems(int argc, const char **argv) {
Common::File f;
Common::DumpFile df;
Common::String line;
if (f.open("mm.exe")) {
if (df.open("items.txt")) {
f.seek(0x19b2a);
for (int i = 0; i < 255; ++i) {
if (i == 85) {
// Add the blank unused item line
line = "\" \", 0, 0, 0, 0, 0, 0, 0, 0, 0";
df.writeString(line);
df.writeByte('\n');
}
line = "\"";
for (int j = 0; j < 14; ++j)
line += f.readByte();
line += '"';
for (int j = 0; j < 9; ++j) {
line += ", ";
line += Common::String::format("%d",
(j == 6) ? f.readUint16BE() : f.readByte());
}
df.writeString(line);
df.writeByte('\n');
}
df.close();
f.close();
debugPrintf("Done\n");
return true;
}
}
debugPrintf("Could not create\n");
return true;
}
bool Console::cmdMapString(int argc, const char **argv) {
Common::File f;
if (argc != 3) {
debugPrintf("%s <map Id> <offset>\n", argv[0]);
} else {
int mapId = strToInt(argv[1]);
Maps::Map *map = g_globals->_maps.getMap(mapId);
int offset = strToInt(Common::String::format(
"%sh", argv[2]).c_str());
if (!f.open(Common::String::format("%s.ovr", map->getName().c_str())))
error("Failed to open map");
f.readUint16LE();
f.readUint16LE();
int codeSize = f.readUint16LE();
int dataPtr = f.readUint16LE();
f.readUint16LE();
f.readUint16LE();
f.readUint16LE();
f.skip(codeSize);
f.skip(offset - dataPtr);
// Read the string
Common::String s;
char c;
while ((c = f.readByte()) != '\0') {
if (c == '\r') {
s += "\\n";
debugPrintf("%s\n", s.c_str());
s = "";
} else {
s += c;
if (s.size() == 40) {
debugPrintf("%s\n", s.c_str());
s = "";
}
}
}
debugPrintf("%s\n", s.c_str());
f.close();
}
return true;
}
bool Console::cmdMap(int argc, const char **argv) {
Maps::Maps &maps = g_globals->_maps;
if (argc < 2) {
debugPrintf("map <mapId section> | <mapNum [ xp, yp ]>\n");
return true;
} else if (argc == 3) {
int mapId = strToInt(argv[1]);
int section = strToInt(argv[2]);
maps.changeMap(mapId, section);
} else {
int mapNum = strToInt(argv[1]);
Maps::Map &map = *maps.getMap(mapNum);
int x = g_maps->_mapPos.x, y = g_maps->_mapPos.y;
if (argc == 4) {
x = strToInt(argv[2]);
y = strToInt(argv[3]);
}
maps._mapPos.x = x;
maps._mapPos.y = y;
maps.changeMap(map.getId(), map.getDefaultSection());
}
return false;
}
bool Console::cmdPos(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("pos xp, yp\n");
return true;
} else {
Maps::Maps &maps = g_globals->_maps;
maps._mapPos.x = strToInt(argv[1]);
maps._mapPos.y = strToInt(argv[2]);
g_events->send("Game", GameMessage("UPDATE"));
return false;
}
}
bool Console::cmdIntangible(int argc, const char **argv) {
g_globals->_intangible = (argc < 2) || strcmp(argv[1], "off");
debugPrintf("Intangibility is %s\n", g_globals->_intangible ? "on" : "off");
return true;
}
bool Console::cmdCast(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("%s <level> <number>\n", argv[0]);
return true;
} else {
Character *c = g_globals->_currCharacter;
if (!c || (c->_class != CLERIC && c->_class != SORCERER
&& c->_class != ARCHER)) {
uint i;
for (i = 0; i < g_globals->_party.size(); ++i) {
if (g_globals->_party[i]._class == SORCERER) {
c = &g_globals->_party[i];
break;
}
}
if (i == g_globals->_party.size()) {
debugPrintf("Could not find sorcerer in party\n");
return true;
}
}
int spellIndex = getSpellIndex(c, strToInt(argv[1]), strToInt(argv[2]));
setSpell(spellIndex, 0, 0);
Game::SpellsParty::cast(_spellIndex, c);
return false;
}
}
bool Console::cmdSpellsAll(int argc, const char **argv) {
g_globals->_allSpells = (argc == 2) && !strcmp(argv[1], "on");
debugPrintf("All spells is %s\n",
g_globals->_allSpells ? "enabled" : "disabled");
return true;
}
bool Console::cmdEncounter(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("encounter <monster-num> [<level>, <# monsters>, <encounter type>]\n");
return true;
} else {
Game::Encounter &enc = g_globals->_encounters;
int monsterNum = strToInt(argv[1]);
if (monsterNum < 1 || monsterNum > MONSTERS_COUNT) {
debugPrintf("monster-num must be between 1 and %d\n", MONSTERS_COUNT);
return true;
}
int level = (argc == 3) ? strToInt(argv[2]) : 1;
if (level < 1 || level >= 15)
level = 1;
int count = (argc > 3) ? strToInt(argv[3]) : 1;
int encType = (argc > 4) ? strToInt(argv[2]) :
Game::NORMAL_ENCOUNTER;
if (encType != -1 || encType == 0 || encType == 1)
enc._encounterType = (Game::EncounterType)encType;
enc.clearMonsters();
for (int i = 0; i < count; ++i)
enc.addMonster(monsterNum, level);
enc._manual = true;
enc._levelIndex = 80;
bool monstersOn = g_globals->_encountersOn;
g_globals->_encountersOn = true;
enc.execute();
g_globals->_encountersOn = monstersOn;
return false;
}
}
bool Console::cmdEncounters(int argc, const char **argv) {
if (argc == 1) {
g_globals->_encountersOn = !g_globals->_encountersOn;
} else {
g_globals->_encountersOn = !scumm_stricmp(argv[1], "on");
}
debugPrintf("Encounters are %s\n",
g_globals->_encountersOn ? "on" : "off");
return true;
}
bool Console::cmdSpecials(int argc, const char **argv) {
int count = g_maps->_currentMap->dataByte(Maps::MAP_SPECIAL_COUNT);
// List specials that have code attached
for (int i = 0; i < count; ++i) {
int mapOffset = g_maps->_currentMap->dataByte(51 + i);
int x = mapOffset % MAP_W;
int y = mapOffset / MAP_W;
Common::String line = Common::String::format(
"Special #%.2d - %d, %d (", i, x, y);
int dirMask = g_maps->_currentMap->dataByte(51 + i);
if (dirMask & Maps::DIRMASK_N)
line += "N,";
if (dirMask & Maps::DIRMASK_S)
line += "S,";
if (dirMask & Maps::DIRMASK_E)
line += "E,";
if (dirMask & Maps::DIRMASK_W)
line += "W,";
line.deleteLastChar();
line += ')';
debugPrintf("%s\n", line.c_str());
}
// Iterate through the map to find special cells that are codeless
int mapOffset = 0, i;
for (int mapPos = 0; mapPos < (MAP_W * MAP_H); ++mapPos) {
bool isSpecial = (g_maps->_currentMap->_states[mapPos]
& Maps::CELL_SPECIAL) != 0;
if (!isSpecial)
continue;
int x = mapPos % MAP_W;
int y = mapPos / MAP_W;
for (i = 0; i < count; ++i) {
mapOffset = g_maps->_currentMap->dataByte(51 + i);
if (mapOffset == mapPos)
break;
}
// Add row for special if there's no code handling
if (i == count) {
Common::String line = Common::String::format(
"Special #-- - %d, %d", x, y);
debugPrintf("%s\n", line.c_str());
}
}
return true;
}
bool Console::cmdSpecial(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("special <num> to execute special in the current map\n");
return true;
}
Maps::Maps &maps = *g_maps;
Maps::Map &map = *g_maps->_currentMap;
uint count = g_maps->_currentMap->dataByte(Maps::MAP_SPECIAL_COUNT);
uint specialNum = strToInt(argv[1]);
if (specialNum > count) {
debugPrintf("Invalid special number\n");
return true;
}
// Set new position
maps._mapOffset = map[51 + specialNum];
maps._mapPos.x = maps._mapOffset % 16;
maps._mapPos.y = maps._mapOffset / 16;
// Rotate to find a direction that will trigger the special
for (int i = 0; i < 4; ++i) {
if (maps._forwardMask & map[51 + count + specialNum])
break;
maps.turnLeft();
}
// Execute the specials handler for the map
map.special();
return false;
}
} // namespace MM1
} // namespace MM