/* 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 "ultima/ultima0/views/dungeon.h" #include "ultima/ultima0/ultima0.h" #include "ultima/ultima0/metaengine.h" #include "ultima/ultima0/gfx/font.h" #include "ultima/ultima0/gfx/dungeon.h" #include "ultima/ultima0/data/monster_logic.h" namespace Ultima { namespace Ultima0 { namespace Views { Dungeon::Dungeon() : View("Dungeon") { } bool Dungeon::msgFocus(const FocusMessage &msg) { showMessage(""); showLines(""); MetaEngine::setKeybindingMode(KBMODE_DUNGEONS); if (!g_engine->isMidiPlaying()) g_engine->playMidi("dungeon.mid"); return true; } bool Dungeon::msgUnfocus(const UnfocusMessage &msg) { MetaEngine::setKeybindingMode(KBMODE_MINIMAL); return true; } void Dungeon::draw() { auto s = getSurface(); s.clear(); // Draw the dungeon view Graphics::ManagedSurface dungArea(s, Common::Rect(0, 0, s.w, s.h - Gfx::GLYPH_HEIGHT * 4)); Gfx::Dungeon::draw(&dungArea); // Allow the status area to draw View::draw(); if (g_engine->_showMinimap) { s.frameRect(Common::Rect(s.w - DUNGEON_MINIMAP_SIZE - 4, 0, s.w, DUNGEON_MINIMAP_SIZE + 4), C_GREY); s.frameRect(Common::Rect(s.w - DUNGEON_MINIMAP_SIZE - 3, 1, s.w - 1, DUNGEON_MINIMAP_SIZE + 3), C_GREY); Graphics::ManagedSurface minimapArea(s, Common::Rect(s.w - DUNGEON_MINIMAP_SIZE - 2, 2, s.w - 2, DUNGEON_MINIMAP_SIZE + 2)); drawMinimap(minimapArea); } } void Dungeon::drawMinimap(Graphics::ManagedSurface &mapArea) { const auto &player = g_engine->_player; const auto &dungeon = g_engine->_dungeon; int tile; for (int y = 0; y < DUNGEON_MAP_SIZE; ++y) { for (int x = 0; x < DUNGEON_MAP_SIZE; ++x) { const Common::Rect r(x * MINIMAP_TILE_SIZE, y * MINIMAP_TILE_SIZE, (x + 1) * MINIMAP_TILE_SIZE, (y + 1) * MINIMAP_TILE_SIZE); if (x == player._dungeonPos.x && y == player._dungeonPos.y) { mapArea.fillRect(r, C_CYAN); } else if (dungeon.findMonster(Common::Point(x, y)) >= 0) { mapArea.fillRect(r, C_RED); } else { tile = dungeon._map[x][y]; if (tile == DT_SPACE || tile == DT_DOOR || tile == DT_HIDDENDOOR) mapArea.fillRect(r, C_BLACK); else if (tile == DT_SOLID) mapArea.fillRect(r, C_WHITE); else if (tile == DT_LADDERUP || tile == DT_LADDERDN) mapArea.fillRect(r, C_GREEN); else mapArea.fillRect(r, C_ROSE); } } } } bool Dungeon::msgAction(const ActionMessage &msg) { auto &player = g_engine->_player; if (isDelayActive()) return false; switch (msg._action) { case KEYBIND_UP: showMessage("Move Forward"); moveForward(); break; case KEYBIND_DOWN: showMessage("Turn Around"); player.dungeonTurnLeft(); player.dungeonTurnLeft(); break; case KEYBIND_LEFT: showMessage("Turn Left"); player.dungeonTurnLeft(); break; case KEYBIND_RIGHT: showMessage("Turn Right"); player.dungeonTurnRight(); break; case KEYBIND_INFO: // Show character info screen showMessage(""); replaceView("Info"); break; case KEYBIND_ENTER: interact(); break; case KEYBIND_PASS: showMessage(""); break; case KEYBIND_ATTACK: showMessage(" \x9""Attack!"); _status.draw(); // Render the message before we switch views addView("Attack"); break; case KEYBIND_QUIT: // "Quit" in the original which merely saves the game. For ScummVM, // we open the GMM, allowing the user to either save or quit g_engine->openMainMenuDialog(); return true; case KEYBIND_MINIMAP: g_engine->_showMinimap = !g_engine->_showMinimap; redraw(); break; default: showMessage("Huh???"); break; } endOfTurn(); return true; } bool Dungeon::msgKeypress(const KeypressMessage &msg) { if (isDelayActive()) return false; showMessage("Huh???"); endOfTurn(); return true; } void Dungeon::endOfTurn() { auto &player = g_engine->_player; auto &dungeon = g_engine->_dungeon; if (player._attr[AT_HP] <= 0) { g_engine->stopMidi(); replaceView("Dead"); return; } // Check for monster attacks MonsterLogic::checkForAttacks(player, dungeon); player._object[OB_FOOD] = MAX(player._object[OB_FOOD] - 0.1, 0.0); if (player._object[OB_FOOD] == 0) { showMessage("You have starved..."); g_engine->stopMidi(); delaySeconds(1); } redraw(); } void Dungeon::moveForward() { auto &dungeon = g_engine->_dungeon; auto &player = g_engine->_player; Common::Point newPos = player._dungeonPos + player._dungeonDir; if (!ISWALKTHRU(dungeon._map[newPos.x][newPos.y]) || dungeon.findMonster(newPos) >= 0) return; // Set new position player._dungeonPos = newPos; // What's here ? int n = dungeon._map[player._dungeonPos.x][player._dungeonPos.y]; if (n == DT_PIT) { // Fell in a pit player._level++; // Down a level showMessage("Aaarrrgghhh! A Trap !"); showLines(Common::String::format("Falling to Level %d.", player._level)); player._attr[AT_HP] -= (3 + urand() % (3 * player._level)); dungeon.create(player); // Create the new level } else if (n == DT_GOLD) { // Gold here // Remove the gold dungeon._map[player._dungeonPos.x][player._dungeonPos.y] = DT_SPACE; int gold = (urand() % (5 * player._level)) + player._level; // Calculate amount showMessage("Gold !!!!!"); Common::String msg = Common::String::format("%d pieces of eight ", gold); player._attr[AT_GOLD] = MIN(player._attr[AT_GOLD] + gold, 9999); // Add to total if (gold > 0) { int objNum = urand() % MAX_OBJ; // Decide which object const char *name = OBJECT_INFO[objNum]._name; const char *prefix = "a"; // Decide a,an or some if (strchr("aeiou", tolower(*name))) prefix = "an"; if (objNum == 0) prefix = "some"; msg += Common::String::format("\nand %s %s.", prefix, name); player._object[objNum] = MIN(player._object[objNum] + 1, 9999); // Bump the total } showLines(msg); } } void Dungeon::interact() { auto &dungeon = g_engine->_dungeon; auto &player = g_engine->_player; // Identify what's there int t = dungeon._map[player._dungeonPos.x][player._dungeonPos.y]; bool done = false; if (t == DT_LADDERUP) { // Climbing up a ladder player._level--; done = true; if (player._level == 0) { showMessage("Leave Dungeon."); if (player._hpGain > 0) showLines(Common::String::format("Thou has gained %d HP", player._hpGain)); player._attr[AT_HP] += player._hpGain; player._hpGain = 0; delaySeconds(1); // Brief delay to show text before leaving dungeon } else { showMessage("Use Ladder"); showLines(Common::String::format("Go up to Level %d.", player._level)); } } else if (t == DT_LADDERDN) { // Climbing down a ladder player._level++; done = true; showMessage("Use Ladder"); showLines(Common::String::format("Go down to Level %d.\n", player._level)); } if (done) { if (player._level > 0) // New Dungeon Map Required dungeon.create(player); } else { showMessage("Huh???"); } } bool Dungeon::msgGame(const GameMessage &msg) { if (msg._name == "ENDOFTURN") { endOfTurn(); return true; } return false; } void Dungeon::timeout() { const auto &player = g_engine->_player; g_engine->stopMidi(); replaceView((player._level == 0) ? "WorldMap" : "Dead"); } } // namespace Views } // namespace Ultima0 } // namespace Ultima