Files
scummvm-cursorfix/engines/ultima/ultima0/views/dungeon.cpp
2026-02-02 04:50:13 +01:00

301 lines
7.9 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 "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<int>(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<int>(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