301 lines
7.9 KiB
C++
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
|