/* 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 . * */ #include "common/endian.h" #include "common/stream.h" #include "common/system.h" #include "mm/mm1/gfx/dta.h" #include "mm/mm1/gfx/screen_decoder.h" #include "mm/mm1/maps/maps.h" #include "mm/mm1/maps/map00.h" #include "mm/mm1/maps/map01.h" #include "mm/mm1/maps/map02.h" #include "mm/mm1/maps/map03.h" #include "mm/mm1/maps/map04.h" #include "mm/mm1/maps/map05.h" #include "mm/mm1/maps/map06.h" #include "mm/mm1/maps/map07.h" #include "mm/mm1/maps/map08.h" #include "mm/mm1/maps/map09.h" #include "mm/mm1/maps/map10.h" #include "mm/mm1/maps/map11.h" #include "mm/mm1/maps/map12.h" #include "mm/mm1/maps/map13.h" #include "mm/mm1/maps/map14.h" #include "mm/mm1/maps/map15.h" #include "mm/mm1/maps/map16.h" #include "mm/mm1/maps/map17.h" #include "mm/mm1/maps/map18.h" #include "mm/mm1/maps/map19.h" #include "mm/mm1/maps/map20.h" #include "mm/mm1/maps/map21.h" #include "mm/mm1/maps/map22.h" #include "mm/mm1/maps/map23.h" #include "mm/mm1/maps/map24.h" #include "mm/mm1/maps/map25.h" #include "mm/mm1/maps/map26.h" #include "mm/mm1/maps/map27.h" #include "mm/mm1/maps/map28.h" #include "mm/mm1/maps/map29.h" #include "mm/mm1/maps/map30.h" #include "mm/mm1/maps/map31.h" #include "mm/mm1/maps/map32.h" #include "mm/mm1/maps/map33.h" #include "mm/mm1/maps/map34.h" #include "mm/mm1/maps/map35.h" #include "mm/mm1/maps/map36.h" #include "mm/mm1/maps/map37.h" #include "mm/mm1/maps/map38.h" #include "mm/mm1/maps/map39.h" #include "mm/mm1/maps/map40.h" #include "mm/mm1/maps/map41.h" #include "mm/mm1/maps/map42.h" #include "mm/mm1/maps/map43.h" #include "mm/mm1/maps/map44.h" #include "mm/mm1/maps/map45.h" #include "mm/mm1/maps/map46.h" #include "mm/mm1/maps/map47.h" #include "mm/mm1/maps/map48.h" #include "mm/mm1/maps/map49.h" #include "mm/mm1/maps/map50.h" #include "mm/mm1/maps/map51.h" #include "mm/mm1/maps/map52.h" #include "mm/mm1/maps/map53.h" #include "mm/mm1/maps/map54.h" #include "mm/mm1/maps/map55.h" #include "mm/mm1/mm1.h" namespace MM { namespace MM1 { Maps::Maps *g_maps; namespace Maps { static const byte LOOKUPS_START[4] = { 0, 0, 14, 34 }; static const byte COLOR_OFFSET[55] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; static const uint16 TILE_AREA1[] = { 0x10D, 0x0B0B, 0x50A, 0x11A, 0x0B18, 0x517 }; static const uint16 TILE_AREA2[] = { 0xB0B, 0x50A, 0x10D, 0x0F08, 0x907, 0x11A, 0x0B18, 0x517 }; static const uint16 TILE_AREA3[] = { 0xB0B, 0x10D, 0x517, 0x0B18, 0x11A, 0x50A }; static const uint16 *TILE_AREAS[3] = { TILE_AREA1, TILE_AREA2, TILE_AREA3 }; static const byte TILE_OFFSET[3] = { 1, 7, 15 }; #define RESOURCE_TILES_COUNT 12 static const uint16 TILE_WIDTHS[RESOURCE_TILES_COUNT] = { 32, 40, 24, 16, 32, 40, 24, 16, 176, 96, 48, 16 }; static const uint16 TILE_HEIGHTS[RESOURCE_TILES_COUNT] = { 128, 96, 64, 32, 128, 96, 64, 32, 96, 64, 32, 16 }; static byte TILE_COLORS[18] = { 0xe6, 0xe6, 0xe6, 0x72, 0x72, 0x72, 0x62, 0x62, 0x62, 0x62, 0x62, 0xe1, 0x53, 0x53, 0xff, 0x43, 0x43, 0x63 }; Maps::Maps() { g_maps = this; _maps.push_back(new Map00()); _maps.push_back(new Map01()); _maps.push_back(new Map02()); _maps.push_back(new Map03()); _maps.push_back(new Map04()); _maps.push_back(new Map05()); _maps.push_back(new Map06()); _maps.push_back(new Map07()); _maps.push_back(new Map08()); _maps.push_back(new Map09()); _maps.push_back(new Map10()); _maps.push_back(new Map11()); _maps.push_back(new Map12()); _maps.push_back(new Map13()); _maps.push_back(new Map14()); _maps.push_back(new Map15()); _maps.push_back(new Map16()); _maps.push_back(new Map17()); _maps.push_back(new Map18()); _maps.push_back(new Map19()); _maps.push_back(new Map20()); _maps.push_back(new Map21()); _maps.push_back(new Map22()); _maps.push_back(new Map23()); _maps.push_back(new Map24()); _maps.push_back(new Map25()); _maps.push_back(new Map26()); _maps.push_back(new Map27()); _maps.push_back(new Map28()); _maps.push_back(new Map29()); _maps.push_back(new Map30()); _maps.push_back(new Map31()); _maps.push_back(new Map32()); _maps.push_back(new Map33()); _maps.push_back(new Map34()); _maps.push_back(new Map35()); _maps.push_back(new Map36()); _maps.push_back(new Map37()); _maps.push_back(new Map38()); _maps.push_back(new Map39()); _maps.push_back(new Map40()); _maps.push_back(new Map41()); _maps.push_back(new Map42()); _maps.push_back(new Map43()); _maps.push_back(new Map44()); _maps.push_back(new Map45()); _maps.push_back(new Map46()); _maps.push_back(new Map47()); _maps.push_back(new Map48()); _maps.push_back(new Map49()); _maps.push_back(new Map50()); _maps.push_back(new Map51()); _maps.push_back(new Map52()); _maps.push_back(new Map53()); _maps.push_back(new Map54()); _maps.push_back(new Map55()); } Maps::~Maps() { for (uint i = 0; i < _maps.size(); ++i) delete _maps[i]; g_maps = nullptr; } void Maps::load(uint mapId) { _mapId = mapId; _currentMap = _maps[mapId]; _currentMap->load(); } void Maps::synchronize(Common::Serializer &s) { // Store a count of the number of maps, just in case new ones // are added in later as easter eggs just for ScummVM int mapCount = _maps.size(); s.syncAsByte(mapCount); for (int i = 0; i < mapCount; ++i) { s.syncBytes(_maps[i]->_visited, MAP_SIZE); } } void Maps::synchronizeCurrent(Common::Serializer &s) { // Save current map s.syncAsUint16LE(_id); s.syncAsByte(_section); s.syncAsByte(_mapPos.x); s.syncAsByte(_mapPos.y); s.syncAsByte(_forwardMask); if (s.isLoading()) { updateMasksOffsets(); changeMap(_id, _section); } } void Maps::select(uint16 id, byte section) { _id = id; _section = section; uint mapId = getIndex(id, section); load(mapId); } void Maps::display(uint16 id, byte section) { select(id, section); loadTiles(); g_events->send("Game", GameMessage("UPDATE")); } void Maps::loadTown(TownId townId) { switch (townId) { case SORPIGAL: town15setup(); _mapPos = Common::Point(8, 3); display(0x604); break; case PORTSMITH: town23setup(); _mapPos = Common::Point(3, 12); display(0xc03); break; case ALGARY: town23setup(); _mapPos = Common::Point(14, 8); display(0x302); break; case DUSK: town4setup(); _mapPos = Common::Point(11, 8); display(0x802); break; case ERLIQUIN: town15setup(); _mapPos = Common::Point(4, 4); display(0xB1A); break; default: break; } byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x]; if (!visited) visited = VISITED_NORMAL; } void Maps::town15setup() { _forwardMask = DIRMASK_N; updateMasksOffsets(); } void Maps::town23setup() { _forwardMask = DIRMASK_W; updateMasksOffsets(); } void Maps::town4setup() { _forwardMask = DIRMASK_E; updateMasksOffsets(); } uint Maps::getIndex(uint16 id, byte section) { uint idx = LOOKUPS_START[section]; // Find map by Id for (; idx < _maps.size() && id != _maps[idx]->getId(); ++idx) {} assert(idx < _maps.size()); _colorOffset = COLOR_OFFSET[idx]; return idx; } void Maps::loadTiles() { _loadArea = _currentMap->dataByte(MAP_1); _loadId = _currentMap->dataWord(MAP_2); _loadSection = 1; loadTile(); _loadArea = _currentMap->dataByte(MAP_1); _loadId = _currentMap->dataWord(MAP_4); _loadSection = 2; loadTile(); _loadArea = _currentMap->dataByte(MAP_1); _loadId = _currentMap->dataWord(MAP_6); _loadSection = 3; loadTile(); } void Maps::loadTile() { assert(_loadArea >= 1 && _loadArea <= 3); const uint16 *arr = TILE_AREAS[_loadArea - 1]; int ctr = TILE_OFFSET[_loadArea - 1]; int entryIndex; for (; *arr != _loadId; ++arr, ++ctr) { } _loadFlag = 0xff; if (ctr >= 19) { if (ctr != 19) _loadFlag = 0xaa; ctr = 1; } // Get the entry from the wallpix.dta file entryIndex = ctr - 1; Gfx::DTA dta(WALLPIX_DTA); Common::SeekableReadStream *entry = dta.load(entryIndex); entry->skip(2); // Decode the tiles Common::Array &tiles = _tiles[_loadSection - 1]; tiles.clear(); tiles.resize(RESOURCE_TILES_COUNT); Gfx::ScreenDecoder decoder; byte colors = TILE_COLORS[entryIndex]; assert(colors); decoder._indexes[0] = 0; decoder._indexes[1] = colors & 0xf; decoder._indexes[2] = (colors >> 4) & 0xf; decoder._indexes[3] = 15; for (int i = 0; i < RESOURCE_TILES_COUNT; ++i) { if (!decoder.loadStream(*entry, TILE_WIDTHS[i], TILE_HEIGHTS[i])) error("Failed decoding tile"); tiles[i].copyFrom(*decoder.getSurface()); } } void Maps::turnLeft() { _forwardMask = _leftMask; updateMasksOffsets(); } void Maps::turnRight() { _forwardMask = _rightMask; updateMasksOffsets(); } void Maps::turnAround() { _forwardMask = _backwardsMask; updateMasksOffsets(); } void Maps::step(const Common::Point &delta) { _mapPos += delta; byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x]; if (!visited) { visited = VISITED_NORMAL; } int section = 0, id = 0; if (_mapPos.x < 0) { _mapPos.x = MAP_W - 1; id = _currentMap->dataWord(MAP_WEST_EXIT_ID); section = _currentMap->dataByte(MAP_WEST_EXIT_SECTION); } else if (_mapPos.x >= MAP_W) { _mapPos.x = 0; id = _currentMap->dataWord(MAP_EAST_EXIT_ID); section = _currentMap->dataByte(MAP_EAST_EXIT_SECTION); } else if (_mapPos.y < 0) { _mapPos.y = MAP_H - 1; id = _currentMap->dataWord(MAP_SOUTH_EXIT_ID); section = _currentMap->dataByte(MAP_SOUTH_EXIT_SECTION); } else if (_mapPos.y >= MAP_H) { _mapPos.y = 0; id = _currentMap->dataWord(MAP_NORTH_EXIT_ID); section = _currentMap->dataByte(MAP_NORTH_EXIT_SECTION); } else { return; } changeMap(id, section); } void Maps::changeMap(uint16 id, byte section) { // At this point, a new map is being entered select(id, section); loadTiles(); visitedTile(); g_events->send("Game", GameMessage("UPDATE")); } void Maps::visitedTile() { byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x]; if (!visited) visited = VISITED_NORMAL; } void Maps::clearSpecial() { _currentState &= ~CELL_SPECIAL; _currentMap->_states[_mapOffset] &= ~CELL_SPECIAL; } Common::Point Maps::getMoveDelta(byte mask) { switch (mask) { case DIRMASK_E: return Common::Point(1, 0); case DIRMASK_W: return Common::Point(-1, 0); case DIRMASK_S: return Common::Point(0, -1); default: return Common::Point(0, 1); } } void Maps::updateMasksOffsets() { switch (_forwardMask) { case DIRMASK_N: _leftMask = DIRMASK_W; _rightMask = DIRMASK_E; _backwardsMask = DIRMASK_S; _forwardOffset = MAP_W; _leftOffset = -1; _rightOffset = 1; _backwardsOffset = -MAP_W; break; case DIRMASK_E: _leftMask = DIRMASK_N; _rightMask = DIRMASK_S; _backwardsMask = DIRMASK_W; _forwardOffset = 1; _leftOffset = MAP_W; _rightOffset = -MAP_W; _backwardsOffset = -1; break; case DIRMASK_S: _leftMask = DIRMASK_E; _rightMask = DIRMASK_W; _backwardsMask = DIRMASK_N; _forwardOffset = -MAP_W; _leftOffset = 1; _rightOffset = -1; _backwardsOffset = MAP_W; break; case DIRMASK_W: _leftMask = DIRMASK_S; _rightMask = DIRMASK_N; _backwardsMask = DIRMASK_E; _forwardOffset = -1; _leftOffset = -MAP_W; _rightOffset = MAP_W; _backwardsOffset = 1; break; default: break; } } } // namespace Maps } // namespace MM1 } // namespace MM