Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
/* 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/console.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
Console::Console() : GUI::Debugger() {
registerCmd("view", WRAP_METHOD(Console, cmdView));
registerCmd("food", WRAP_METHOD(Console, cmdFood));
registerCmd("hp", WRAP_METHOD(Console, cmdHP));
registerCmd("gold", WRAP_METHOD(Console, cmdGold));
registerCmd("demo", WRAP_METHOD(Console, cmdDemo));
registerCmd("debug", WRAP_METHOD(Console, cmdDebug));
registerCmd("monster", WRAP_METHOD(Console, cmdMonster));
}
Console::~Console() {
}
bool Console::cmdView(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("view <view name>\n");
return true;
} else {
g_events->replaceView(argv[1]);
return false;
}
}
bool Console::cmdFood(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("food <amount>\n");
return true;
} else {
g_engine->_player._object[OB_FOOD] = atoi(argv[1]);
g_engine->focusedView()->redraw();
return false;
}
}
bool Console::cmdGold(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("gold <amount>\n");
return true;
} else {
g_engine->_player._attr[AT_GOLD] = atoi(argv[1]);
g_engine->focusedView()->redraw();
return false;
}
}
bool Console::cmdHP(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("hp <amount>\n");
return true;
} else {
g_engine->_player._attr[AT_HP] = atoi(argv[1]);
g_engine->focusedView()->redraw();
return false;
}
}
bool Console::cmdDemo(int argc, const char **argv) {
auto &p = g_engine->_player;
auto &map = g_engine->_worldMap;
p.init();
Common::strcpy_s(p._name, "Demo"); // Characters Name
p._class = 'F'; // Fighter
p._luckyNumber = 42; // Always the same.....
p._skill = 1; // Skill level 1
p._task = 1; // Starting first task
// Nice high attributes
Common::fill(p._attr, p._attr + MAX_ATTR, 15);
p._attr[AT_HP] = 18;
p._attr[AT_GOLD] = 99;
for (int i = 0; i < MAX_OBJ; i++) // Lots of nice objects
p._object[i] = (i == OB_FOOD || i == OB_BOW) ? 999 : 4.0;
p._level = 0;
map.init(p);
g_engine->replaceView("WorldMap");
return false;
}
bool Console::cmdDebug(int argc, const char **argv) {
auto &p = g_engine->_player;
auto &map = g_engine->_worldMap;
int i;
p.init();
Common::strcpy_s(p._name, "Debuggo"); // Characters Name
p._class = 'F'; // Fighter
p._luckyNumber = 42; // Always the same.....
p._skill = 1; // Skill level 1
p._task = 1; // Starting first task
for (i = 0; i < MAX_ATTR; i++) // Nice high attributes
p._attr[i] = 99;
p._attr[AT_HP] = 999;
p._attr[AT_GOLD] = 9999;
for (i = 0; i < MAX_OBJ; i++) // Lots of nice objects
p._object[i] = (i == OB_FOOD || i == OB_BOW) ? 9999.9 : 99.0;
p._level = 0;
map.init(p);
g_engine->replaceView("WorldMap");
return false;
}
bool Console::cmdMonster(int argc, const char **argv) {
const auto &player = g_engine->_player;
auto &dungeon = g_engine->_dungeon;
if (argc != 2) {
debugPrintf("monster <1 to 10>\n");
return true;
} else if (player._level == 0) {
debugPrintf("Not in a dungeon.\n");
return true;
} else {
Common::Point pt = player._dungeonPos + player._dungeonDir;
if (!ISWALKTHRU(dungeon._map[pt.x][pt.y])) {
debugPrintf("No free spot in front of player.\n");
return true;
} else {
int num = atoi(argv[1]);
if (num >= 1 && num <= 10) {
dungeon.addMonsterAtPos(player, pt, num);
g_events->send("Dungeon", GameMessage("ENDOFTURN"));
return false;
} else {
debugPrintf("Invalid monster number.\n");
return true;
}
}
}
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,49 @@
/* 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/>.
*
*/
#ifndef ULTIMA_ULTIMA0_CONSOLE_H
#define ULTIMA_ULTIMA0_CONSOLE_H
#include "gui/debugger.h"
namespace Ultima {
namespace Ultima0 {
class Console : public GUI::Debugger {
private:
bool cmdView(int argc, const char **argv);
bool cmdFood(int argc, const char **argv);
bool cmdGold(int argc, const char **argv);
bool cmdHP(int argc, const char **argv);
bool cmdDemo(int argc, const char **argv);
bool cmdDebug(int argc, const char **argv);
bool cmdMonster(int argc, const char **argv);
public:
Console();
~Console() override;
};
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,389 @@
/* 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/data/data.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
const ObjectInfo OBJECT_INFO[] = {
{ "Food", 1, 0, KEYBIND_FOOD },
{ "Rapier", 8, 10, KEYBIND_RAPIER },
{ "Axe", 5, 5, KEYBIND_AXE },
{ "Shield", 6, 1, KEYBIND_SHIELD },
{ "Bow and Arrow", 3, 4, KEYBIND_BOW },
{ "Magic Amulet", 15, 0, KEYBIND_AMULET }
};
const MonsterInfo MONSTER_INFO[] = {
{ nullptr, 0 },
{ "Skeleton", 1 },
{ "Thief", 2 },
{ "Giant Rat", 3 },
{ "Orc", 4 },
{ "Viper", 5 },
{ "Carrion Crawler", 6 },
{ "Gremlin", 7 },
{ "Mimic", 8 },
{ "Daemon", 9 },
{ "Balrog", 10 }
};
const char *ATTRIB_NAMES[] = { "Hit Points", "Strength", "Dexterity", "Stamina", "Wisdom", "Gold" };
const char *const DIRECTION_NAMES[] = { "North", "East", "South", "West" };
/*-------------------------------------------------------------------*/
void PlayerInfo::init() {
Common::fill(_name, _name + MAX_NAME + 1, '\0');
_worldPos.x = _worldPos.y = 0;
_dungeonPos.x = _dungeonPos.y = 0;
_dungeonDir.x = _dungeonDir.y = 0;
_class = '?';
_hpGain = 0;
_luckyNumber = 0;
_level = 0;
_skill = 0;
_task = 0;
_taskCompleted = false;
Common::fill(_attr, _attr + MAX_ATTR, 0);
Common::fill(_object, _object + MAX_OBJ, 0);
}
void PlayerInfo::rollAttributes() {
for (int i = 0; i < MAX_ATTR; ++i)
_attr[i] = g_engine->getRandomNumber(21) + 4;
}
void PlayerInfo::synchronize(Common::Serializer &s) {
s.syncBytes((byte *)_name, MAX_NAME + 1);
s.syncAsSint16LE(_worldPos.x);
s.syncAsSint16LE(_worldPos.y);
s.syncAsSint16LE(_dungeonPos.x);
s.syncAsSint16LE(_dungeonPos.y);
s.syncAsSint16LE(_dungeonDir.x);
s.syncAsSint16LE(_dungeonDir.y);
s.syncAsByte(_class);
s.syncAsSint32LE(_hpGain);
s.syncAsSint32LE(_level);
s.syncAsSint32LE(_skill);
s.syncAsSint32LE(_task);
s.syncAsSint32LE(_taskCompleted);
s.syncAsUint32LE(_luckyNumber);
for (int i = 0; i < MAX_ATTR; ++i)
s.syncAsUint32LE(_attr[i]);
for (int i = 0; i < MAX_OBJ; ++i) {
uint32 val = (uint32)_object[i];
s.syncAsUint32LE(val);
if (s.isLoading())
_object[i] = (double)val;
}
}
Direction PlayerInfo::dungeonDir() const {
if (_dungeonDir.y < 0)
return DIR_NORTH;
else if (_dungeonDir.x > 0)
return DIR_EAST;
else if (_dungeonDir.y > 0)
return DIR_SOUTH;
else
return DIR_WEST;
}
void PlayerInfo::setDungeonDir(Direction newDir) {
_dungeonDir.x = 0;
_dungeonDir.y = 0;
switch (newDir) {
case DIR_NORTH:
_dungeonDir.y = -1;
break;
case DIR_EAST:
_dungeonDir.x = 1;
break;
case DIR_SOUTH:
_dungeonDir.y = 1;
break;
case DIR_WEST:
_dungeonDir.x = -1;
break;
}
}
void PlayerInfo::dungeonTurnLeft() {
Direction dir = dungeonDir();
setDungeonDir((dir == DIR_NORTH) ? DIR_WEST : (Direction)((int)dir - 1));
}
void PlayerInfo::dungeonTurnRight() {
Direction dir = dungeonDir();
setDungeonDir((dir == DIR_WEST) ? DIR_NORTH : (Direction)((int)dir + 1));
}
/*-------------------------------------------------------------------*/
void WorldMapInfo::init(PlayerInfo &p) {
int c, x, y, size;
g_engine->setRandomSeed(p._luckyNumber);
size = WORLD_MAP_SIZE - 1;
// Set the boundaries
for (x = 0; x <= size; x++) {
_map[size][x] = WT_MOUNTAIN;
_map[0][x] = WT_MOUNTAIN;
_map[x][size] = WT_MOUNTAIN;
_map[x][0] = WT_MOUNTAIN;
}
// Set up the map contents
for (x = 1; x < size; x++) {
for (y = 1; y < size; y++) {
c = (int)(pow(RND(), 5.0) * 4.5); // Calculate what's there
if (c == WT_TOWN && RND() > .5) // Remove half the towns
c = WT_SPACE;
_map[x][y] = c;
}
}
// Calculate player start
x = g_engine->getRandomNumber(1, size - 1);
y = g_engine->getRandomNumber(1, size - 1);
p._worldPos.x = x; p._worldPos.y = y; // Save it
_map[x][y] = WT_TOWN; // Make it a town
// Find place for castle
do {
x = g_engine->getRandomNumber(1, size - 1);
y = g_engine->getRandomNumber(1, size - 1);
} while (_map[x][y] != WT_SPACE);
_map[x][y] = WT_BRITISH; // Put LBs castle there
}
int WorldMapInfo::read(int x, int y) const {
if (x < 0 || y < 0)
return WT_MOUNTAIN;
if (x >= WORLD_MAP_SIZE || y >= WORLD_MAP_SIZE)
return WT_MOUNTAIN;
return _map[x][y];
}
void WorldMapInfo::synchronize(Common::Serializer &s) {
for (int y = 0; y < WORLD_MAP_SIZE; ++y)
for (int x = 0; x < WORLD_MAP_SIZE; ++x)
s.syncAsByte(_map[x][y]);
}
/*-------------------------------------------------------------------*/
void DungeonMapInfo::create(const PlayerInfo &player) {
int i, x, y;
const int SIZE = DUNGEON_MAP_SIZE - 1;
// Seed the random number
g_engine->setRandomSeed(player._luckyNumber - player._worldPos.x * 40 -
player._worldPos.y * 1000 - player._level);
// Clear the dungeon
Common::fill((byte *)_map, (byte *)_map + DUNGEON_MAP_SIZE * DUNGEON_MAP_SIZE, DT_SPACE);
// Draw the boundaries
for (x = 0; x <= SIZE; x++) {
_map[SIZE][x] = DT_SOLID;
_map[0][x] = DT_SOLID;
_map[x][SIZE] = DT_SOLID;
_map[x][0] = DT_SOLID;
}
// Fill with checkerboard
for (x = 2; x < SIZE; x = x + 2) {
for (y = 1; y < SIZE; y++) {
_map[x][y] = DT_SOLID;
_map[y][x] = DT_SOLID;
}
}
// Fill with stuff
for (x = 2; x < SIZE; x = x + 2) {
for (y = 1; y < SIZE; y = y + 2) {
_map[x][y] = generateContent(_map[x][y]);
_map[y][x] = generateContent(_map[y][x]);
}
}
// Put stairs in
_map[2][1] = DT_SPACE;
// Different ends each level
if (player._level % 2 == 0) {
_map[SIZE - 3][3] = DT_LADDERDN;
_map[3][SIZE - 3] = DT_LADDERUP;
} else {
_map[SIZE - 3][3] = DT_LADDERUP;
_map[3][SIZE - 3] = DT_LADDERDN;
}
// On first floor
if (player._level == 1) {
_map[1][1] = DT_LADDERUP; // Ladder at top left
_map[SIZE - 3][3] = DT_SPACE; // No other ladder up
}
// WORKAROUND: Make sure dungeon is completable
byte mapTrace[DUNGEON_MAP_SIZE][DUNGEON_MAP_SIZE];
bool isValid;
do {
isValid = false;
// Start a new trace, mark solid spaces
for (y = 0; y < DUNGEON_MAP_SIZE; ++y)
for (x = 0; x < DUNGEON_MAP_SIZE; ++x)
mapTrace[x][y] = _map[x][y] == DT_SOLID ? DT_SOLID : DT_SPACE;
// Iterate through figuring out route
Common::Queue<Common::Point> points;
if (player._level % 2 == 0)
points.push(Common::Point(SIZE - 3, 3));
else
points.push(Common::Point(3, SIZE - 3));
while (!points.empty() && !isValid) {
Common::Point pt = points.pop();
isValid = _map[pt.x][pt.y] == DT_LADDERUP;
if (isValid)
break;
mapTrace[pt.x][pt.y] = DT_LADDERDN;
if (pt.x > 1 && _map[pt.x - 1][pt.y] != DT_SOLID && mapTrace[pt.x - 1][pt.y] == DT_SPACE)
points.push(Common::Point(pt.x - 1, pt.y));
if (pt.x < SIZE && _map[pt.x + 1][pt.y] != DT_SOLID && mapTrace[pt.x + 1][pt.y] == DT_SPACE)
points.push(Common::Point(pt.x + 1, pt.y));
if (pt.y > 1 && _map[pt.x][pt.y - 1] != DT_SOLID && mapTrace[pt.x][pt.y - 1] == DT_SPACE)
points.push(Common::Point(pt.x, pt.y - 1));
if (pt.y < SIZE && _map[pt.x][pt.y + 1] != DT_SOLID && mapTrace[pt.x][pt.y + 1] == DT_SPACE)
points.push(Common::Point(pt.x, pt.y + 1));
}
if (!isValid) {
// If a path wasn't found, randomly replace a solid square. We'll then
// loop to check whether the path can now be completed
do {
x = g_engine->getRandomNumber(1, SIZE);
y = g_engine->getRandomNumber(1, SIZE);
} while (_map[x][y] != DT_SOLID);
_map[x][y] = DT_HIDDENDOOR;
}
} while (!isValid);
// Add monsters
_monsters.clear();
for (i = 1; i <= MAX_MONSTERS; ++i)
addMonster(player, i);
}
int DungeonMapInfo::generateContent(int c) {
if (RND() > .95) c = DT_TRAP;
if (RND() > .6) c = DT_HIDDENDOOR;
if (RND() > .6) c = DT_DOOR;
if (RND() > .97) c = DT_PIT;
if (RND() > .94) c = DT_GOLD;
return c;
}
void DungeonMapInfo::addMonster(const PlayerInfo &player, int type) {
int x, y;
int level = MONSTER_INFO[type]._level;
// Limit monsters to levels
if (level - 2 > player._level)
return;
// Not always there anyway
if (RND() > 0.6)
return;
// Find a place for it. Must be empty, not player
do {
x = urand() % DUNGEON_MAP_SIZE;
y = urand() % DUNGEON_MAP_SIZE;
} while (_map[x][y] != DT_SPACE ||
(x == player._dungeonPos.x && y == player._dungeonPos.y));
addMonsterAtPos(player, Common::Point(x, y), type);
}
void DungeonMapInfo::addMonsterAtPos(const PlayerInfo &player, const Common::Point &pt, int type) {
int level = MONSTER_INFO[type]._level;
// Fill in details
MonsterEntry m;
m._type = type;
m._strength = level + 3 + player._level;
m._alive = true;
// Record position
m._loc = pt;
_monsters.push_back(m);
}
void DungeonMapInfo::synchronize(Common::Serializer &s) {
// Map data
for (int y = 0; y < DUNGEON_MAP_SIZE; ++y)
for (int x = 0; x < DUNGEON_MAP_SIZE; ++x)
s.syncAsByte(_map[x][y]);
// Monsters
uint count = _monsters.size();
s.syncAsByte(count);
if (s.isLoading())
_monsters.resize(count);
for (auto &m : _monsters)
m.synchronize(s);
}
int DungeonMapInfo::findMonster(const Common::Point &c) const {
int n = -1;
for (uint i = 0; i < _monsters.size(); i++) {
const auto &m = _monsters[i];
if (m._loc == c && m._alive)
n = i;
}
return n;
}
/*-------------------------------------------------------------------*/
void MonsterEntry::synchronize(Common::Serializer &s) {
s.syncAsByte(_loc.x);
s.syncAsByte(_loc.y);
s.syncAsByte(_type);
s.syncAsByte(_strength);
s.syncAsByte(_alive);
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,146 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_DATA_H
#define ULTIMA0_DATA_H
#include "common/array.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "common/serializer.h"
#include "ultima/ultima0/data/defines.h"
namespace Ultima {
namespace Ultima0 {
enum Direction { DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST };
struct PlayerInfo;
struct ObjectInfo {
const char *_name;
int _cost;
int _maxDamage;
int _action;
};
struct MonsterInfo {
const char *_name;
int _level;
};
extern const ObjectInfo OBJECT_INFO[];
extern const MonsterInfo MONSTER_INFO[];
extern const char *ATTRIB_NAMES[];
extern const char *const DIRECTION_NAMES[];
/**
* Monster structure
*/
struct MonsterEntry {
Common::Point _loc; // Position
int _type = 0; // Monster type
int _strength = 0; // Strength
bool _alive = false; // Alive flag
void synchronize(Common::Serializer &s);
};
/**
* Dungeon Map Structure
*/
struct DungeonMapInfo {
private:
void addMonster(const PlayerInfo &player, int type);
int generateContent(int c);
public:
byte _map[DUNGEON_MAP_SIZE][DUNGEON_MAP_SIZE] = {}; // Map information
Common::Array<MonsterEntry> _monsters; // Monster records
void create(const PlayerInfo &player);
void synchronize(Common::Serializer &s);
/**
* Find Monster ID at given location
*/
int findMonster(const Common::Point &c) const;
void addMonsterAtPos(const PlayerInfo &player, const Common::Point &pt, int type);
};
/**
* Player structure
*/
struct PlayerInfo {
char _name[MAX_NAME + 1] = {}; // Player Name
Common::Point _worldPos; // World map position
Common::Point _dungeonPos; // Dungeon map position
Common::Point _dungeonDir; // Dungeon direction facing
byte _class = '?'; // Player class (F or M)
int _hpGain = 0; // HPs gained in dungeon
int _level = 0; // Dungeon level, 0 = world map
int _skill = 0; // Skill level
int _task = 0; // Task set (-1 = none)
bool _taskCompleted = 0; // Task completed
uint32 _luckyNumber = 0; // Value used for seeding
int _attr[MAX_ATTR] = {}; // Attribute values
double _object[MAX_OBJ] = {}; // Object counts
void init();
void rollAttributes();
void synchronize(Common::Serializer &s);
/**
* Return the dungeon facing direction
*/
Direction dungeonDir() const;
/**
* Sets the dungeon direction
*/
void setDungeonDir(Direction newDir);
/**
* Turn left in the dungeon
*/
void dungeonTurnLeft();
/**
* Turn right in the dungeon
*/
void dungeonTurnRight();
};
/**
* World Map structure
*/
struct WorldMapInfo {
byte _map[WORLD_MAP_SIZE][WORLD_MAP_SIZE] = {}; // Map information
void init(PlayerInfo &p);
int read(int x, int y) const;
void synchronize(Common::Serializer &s);
};
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,133 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_DATA_DEFINES_H
#define ULTIMA0_DATA_DEFINES_H
#include "common/scummsys.h"
namespace Ultima {
namespace Ultima0 {
constexpr int DEFAULT_SCX = 640; // Default Screen Size and Depth
constexpr int DEFAULT_SCY = 400;
constexpr int WORLD_MAP_SIZE = 21; // Size of world map
constexpr int DUNGEON_MAP_SIZE = 11; // Size of dungeon map
constexpr int MAX_MONSTERS = 10; // Number of Monsters
constexpr int MAX_ATTR = 6; // Attributes
constexpr int MAX_OBJ = 6; // Objects
constexpr int WORLD_GRID_SIZE = 3; // Visible part of map is axa
constexpr int MAX_NAME = 8; // Max size player name
constexpr int MAX_VIEW_DEPTH = 9; // Max viewing depth
constexpr int MINIMAP_TILE_SIZE = 5;
constexpr int WORLD_MINIMAP_SIZE = WORLD_MAP_SIZE * MINIMAP_TILE_SIZE;
constexpr int DUNGEON_MINIMAP_SIZE = DUNGEON_MAP_SIZE * MINIMAP_TILE_SIZE;
#define RND_MAX 0x7fffffff
#define RND() (((double)g_engine->getRandomNumber())/RND_MAX)
#define urand() g_engine->getRandomNumber()
#define AKVERSION (1.000) // Version number
// Convert RGB to Colour Code
#define RGB(r,g,b) ((r?4:0)+(g?2:0)+(b?1:0))
#define C_BLACK RGB(0,0,0) // Some Colours
#define C_RED RGB(1,0,0)
#define C_GREEN RGB(0,1,0)
#define C_BLUE RGB(0,0,1)
#define C_YELLOW RGB(1,1,0)
#define C_WHITE RGB(1,1,1)
#define C_CYAN RGB(0,1,1)
#define C_PURPLE RGB(1,0,1)
#define C_ROSE 8
#define C_VIOLET 9
#define C_GREY 10
#define C_TOMATO 11
#define C_TEXT_DEFAULT C_CYAN
#define WT_SPACE (0) // World Tiles
#define WT_MOUNTAIN (1)
#define WT_TREE (2)
#define WT_TOWN (3)
#define WT_DUNGEON (4)
#define WT_BRITISH (5)
#define WT_PLAYER (-1) // Used for the player graphic
#define DT_SPACE (0) // Dungeon tiles
#define DT_SOLID (1)
#define DT_TRAP (2)
#define DT_HIDDENDOOR (3)
#define DT_DOOR (4)
#define DT_GOLD (5)
#define DT_LADDERDN (7)
#define DT_LADDERUP (8)
#define DT_PIT (9)
#define ISWALKTHRU(x) ((x) != DT_SOLID) // Tests for them
#define ISDRAWWALL(x) ((x) == DT_SOLID || (x) == DT_HIDDENDOOR)
#define ISDRAWDOOR(x) ((x) == DT_DOOR)
#define ISDRAWOPEN(x) (ISDRAWWALL(x) == 0 && ISDRAWDOOR(x) == 0)
// Object Colours
#define COL_WALL (g_engine->_player._level == 1 ? C_WHITE : C_BLUE)
#define COL_LADDER (C_RED)
#define COL_DOOR (C_BLUE)
#define COL_HOLE (C_RED)
#define COL_MONSTER (C_WHITE)
#define COL_MOUNTAIN (C_YELLOW)
#define COL_TREE (C_GREEN)
#define COL_DUNGEON (C_RED)
#define COL_TOWN (C_BLUE)
#define COL_BRITISH (C_WHITE)
#define COL_PLAYER (C_CYAN)
#define MN_SKELETON (1) // Monster types
#define MN_THIEF (2)
#define MN_RAT (3)
#define MN_ORC (4)
#define MN_VIPER (5)
#define MN_CARRION (6)
#define MN_GREMLIN (7)
#define MN_MIMIC (8)
#define MN_DAEMON (9)
#define MN_BALROG (10)
#define AT_HP (0) // Player attributes
#define AT_STRENGTH (1)
#define AT_DEXTERITY (2)
#define AT_STAMINA (3)
#define AT_WISDOM (4)
#define AT_GOLD (5)
#define OB_FOOD (0) // Object Attributes
#define OB_RAPIER (1)
#define OB_AXE (2)
#define OB_SHIELD (3)
#define OB_BOW (4)
#define OB_AMULET (5)
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,203 @@
/* 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/data/monster_logic.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
void MonsterLogic::checkForAttacks(PlayerInfo &p, DungeonMapInfo &d) {
int attacked;
double dist;
// Go through all monsters
for (MonsterEntry &m : d._monsters) {
dist = pow(m._loc.x - p._dungeonPos.x, 2); // Calculate Distance
dist = dist + pow(m._loc.y - p._dungeonPos.y, 2);
dist = sqrt(dist);
// If alive
if (m._alive) {
attacked = 0;
// If within range
if (dist < 1.3)
attacked = attack(m, p);
// If didn't attack, then move
if (attacked == 0) {
// Mimics only if near enough
if (m._type != MN_MIMIC || dist >= 3.0)
move(m, p, d);
// Recovers if didn't attack
if (m._strength < p._level * p._skill)
m._strength = m._strength + p._level;
}
}
}
}
void MonsterLogic::showLines(const Common::String &msg) {
g_events->send("DungeonStatus", GameMessage("LINES", msg));
}
int MonsterLogic::attack(MonsterEntry &m, PlayerInfo &p) {
int n;
Common::String msg = "You are being attacked\nby a ";
msg += (char)C_BLUE;
msg += MONSTER_INFO[m._type]._name;
msg += (char)C_TEXT_DEFAULT;
msg += '\n';
// Special case for Gremlin/Thief stealing
if (m._type == MN_GREMLIN || m._type == MN_THIEF)
if (RND() > 0.5)
// Half the time
return steal(msg, m, p);
n = urand() % 20; // Calculate hit chance
if (p._object[OB_SHIELD] > 0) n--;
n = n - p._attr[AT_STAMINA];
n = n + m._type + p._level;
if (n < 0) {
// Missed
msg += (char)C_TOMATO;
msg += "Missed!";
} else {
// Hit
n = urand() % m._type; // Calculate damage done.
n = n + p._level;
p._attr[AT_HP] -= n; // Adjust hit points
msg += (char)C_TOMATO;
msg += "Hit! ";
msg += (char)C_TEXT_DEFAULT;
msg += " Damage = ";
msg += (char)C_BLUE;
msg += Common::String::format("%d", n);
}
showLines(msg);
return 1;
}
void MonsterLogic::move(MonsterEntry &m, PlayerInfo &p, DungeonMapInfo &d) {
int x, y, xi, yi;
// Calculate direction
xi = yi = 0;
if (p._dungeonPos.x != m._loc.x)
xi = (p._dungeonPos.x > m._loc.x) ? 1 : -1;
if (p._dungeonPos.y != m._loc.y)
yi = (p._dungeonPos.y > m._loc.y) ? 1 : -1;
// Running away
if (m._strength < p._level * p._skill) {
xi = -xi; yi = -yi;
}
// Get position
x = m._loc.x; y = m._loc.y;
// Check move okay
if (ABS(xi) > ABS(yi)) {
if (canMoveTo(d, x + xi, yi)) yi = 0;
else if (canMoveTo(d, x, y + yi)) xi = 0;
} else {
if (canMoveTo(d, x, y + yi)) xi = 0;
else if (canMoveTo(d, x + xi, yi)) yi = 0;
}
if (xi == 0 && yi == 0)
return; // No move
x = x + xi; y = y + yi; // Work out new position
if (!canMoveTo(d, x, y)) // Fail if can't move there
return;
if (x == p._dungeonPos.x && // Can't move onto us
y == p._dungeonPos.y) return;
// Move to new position
m._loc.x = x; m._loc.y = y;
// If the tile was for a hidden door, flag it as a normal visible door
if (d._map[x][y] == DT_HIDDENDOOR)
d._map[x][y] = DT_DOOR;
}
bool MonsterLogic::canMoveTo(DungeonMapInfo &d, int x, int y) {
Common::Point c;
int t = d._map[x][y]; // See what's there
if (!ISWALKTHRU(t))
return 0; // Can't walk through walls
c.x = x; c.y = y; // Set up coord structure
// True if no monster here
return d.findMonster(c) < 0;
}
int MonsterLogic::steal(const Common::String &attackStr, MonsterEntry &m, PlayerInfo &p) {
int n;
const char *s1, *s2;
Common::String msg = attackStr;
msg += (char)C_GREEN;
msg += "A ";
msg += MONSTER_INFO[m._type]._name;
msg += " stole ";
if (m._type == MN_GREMLIN) {
// HALVES the food.... aargh
p._object[OB_FOOD] = floor(p._object[OB_FOOD]) / 2.0;
msg += "some ";
msg += (char)C_BLUE;
msg += "Food";
showLines(msg);
} else if (m._type == MN_THIEF) {
// Figure out what stolen
do {
n = urand() % MAX_OBJ;
} while (p._object[n] == 0);
p._object[n]--; // Stole one
s2 = OBJECT_INFO[n]._name;
s1 = "a";
if (strchr("aeiou", tolower(*s2))) s1 = "an";
if (n == 0) s1 = "some";
msg += s1;
msg += ' ';
msg += (char)C_BLUE;
msg += s2;
showLines(msg);
}
return 1;
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,67 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_MONSTER_LOGIC_H
#define ULTIMA0_MONSTER_LOGIC_H
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
class MonsterLogic {
private:
/**
* Shows a message in the dungeon status area
*/
static void showLines(const Common::String &msg);
/**
* Monster Attacks
*/
static int attack(MonsterEntry &m, PlayerInfo &p);
/**
* Monster Moves
*/
static void move(MonsterEntry &m, PlayerInfo &p, DungeonMapInfo &d);
/**
* Can monster move to a square
*/
static bool canMoveTo(DungeonMapInfo &d, int x, int y);
/**
* Monster Stealing
*/
static int steal(const Common::String &attackStr, MonsterEntry &m, PlayerInfo &p);
public:
/**
* Check Monsters Attacking
*/
static void checkForAttacks(PlayerInfo &p, DungeonMapInfo &d);
};
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,382 @@
/* 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/system.h"
#include "common/config-manager.h"
#include "graphics/cursorman.h"
#include "graphics/screen.h"
#include "ultima/ultima0/events.h"
#include "ultima/ultima0/gfx/gfx_surface.h"
#include "ultima/ultima0/views/views.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
Events *g_events;
constexpr int CURSOR_W = 12;
constexpr int CURSOR_H = 20;
static const byte ARROW_CURSOR[CURSOR_W * CURSOR_H] = {
0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
0, 7, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9,
0, 7, 7, 0, 9, 9, 9, 9, 9, 9, 9, 9,
0, 7, 7, 7, 0, 9, 9, 9, 9, 9, 9, 9,
0, 7, 7, 7, 7, 0, 9, 9, 9, 9, 9, 9,
0, 7, 7, 7, 7, 7, 0, 9, 9, 9, 9, 9,
0, 7, 7, 7, 7, 7, 7, 0, 9, 9, 9, 9,
0, 7, 7, 7, 7, 7, 7, 7, 0, 9, 9, 9,
0, 7, 7, 7, 7, 7, 7, 7, 7, 0, 9, 9,
0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 9,
0, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0,
0, 7, 7, 7, 0, 7, 7, 0, 9, 9, 9, 9,
0, 7, 7, 0, 0, 7, 7, 0, 9, 9, 9, 9,
0, 7, 0, 9, 9, 0, 7, 7, 0, 9, 9, 9,
0, 0, 9, 9, 9, 0, 7, 7, 0, 9, 9, 9,
0, 9, 9, 9, 9, 9, 0, 7, 7, 0, 9, 9,
9, 9, 9, 9, 9, 9, 0, 7, 7, 0, 9, 9,
9, 9, 9, 9, 9, 9, 9, 0, 7, 7, 0, 9,
9, 9, 9, 9, 9, 9, 9, 0, 7, 7, 0, 9,
9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9
};
Events::Events() : UIElement("Root", nullptr) {
g_events = this;
}
Events::~Events() {
g_events = nullptr;
}
void Events::runGame() {
uint currTime, nextFrameTime = 0;
_screen = new Graphics::Screen();
Views::Views views; // Loads all views in the structure
CursorMan.pushCursor(ARROW_CURSOR, CURSOR_W, CURSOR_H, 0, 0, 9);
// Run the game
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot == -1 || g_engine->loadGameState(saveSlot).getCode() != Common::kNoError)
// Except when loading a savegame from the launcher, default to first screen
addView("Startup");
Common::Event e;
while (!_views.empty() && !shouldQuit()) {
while (g_system->getEventManager()->pollEvent(e)) {
if (e.type == Common::EVENT_QUIT ||
e.type == Common::EVENT_RETURN_TO_LAUNCHER) {
_views.clear();
break;
} else {
processEvent(e);
}
}
if (_views.empty())
break;
g_system->delayMillis(10);
if ((currTime = g_system->getMillis()) >= nextFrameTime) {
nextFrameTime = currTime + FRAME_DELAY;
tick();
drawElements();
_screen->update();
}
}
delete _screen;
}
void Events::processEvent(Common::Event &ev) {
switch (ev.type) {
case Common::EVENT_KEYDOWN:
CursorMan.showMouse(false);
if (ev.kbd.keycode < Common::KEYCODE_NUMLOCK)
msgKeypress(KeypressMessage(ev.kbd));
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
CursorMan.showMouse(false);
msgAction(ActionMessage(ev.customType));
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_MBUTTONDOWN:
CursorMan.showMouse(true);
msgMouseDown(MouseDownMessage(ev.type, ev.mouse));
break;
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
case Common::EVENT_MBUTTONUP:
CursorMan.showMouse(true);
msgMouseUp(MouseUpMessage(ev.type, ev.mouse));
break;
case Common::EVENT_MOUSEMOVE:
CursorMan.showMouse(true);
msgMouseMove(MouseMoveMessage(ev.type, ev.mouse));
break;
default:
break;
}
}
void Events::replaceView(UIElement *ui, bool replaceAllViews) {
assert(ui);
UIElement *priorView = focusedView();
if (replaceAllViews) {
clearViews();
} else if (!_views.empty()) {
priorView->msgUnfocus(UnfocusMessage());
_views.pop();
}
_views.push(ui);
ui->redraw();
ui->msgFocus(FocusMessage(priorView));
}
void Events::replaceView(const Common::String &name, bool replaceAllViews) {
replaceView(findView(name));
}
void Events::addView(UIElement *ui) {
assert(ui);
UIElement *priorView = focusedView();
if (!_views.empty())
priorView->msgUnfocus(UnfocusMessage());
_views.push(ui);
ui->redraw();
ui->msgFocus(FocusMessage(priorView));
}
void Events::addView(const Common::String &name) {
addView(findView(name));
}
void Events::popView() {
UIElement *priorView = focusedView();
priorView->msgUnfocus(UnfocusMessage());
_views.pop();
for (int i = 0; i < (int)_views.size() - 1; ++i) {
_views[i]->redraw();
_views[i]->draw();
}
if (!_views.empty()) {
UIElement *view = focusedView();
view->msgFocus(FocusMessage(priorView));
view->redraw();
view->draw();
}
}
void Events::redrawViews() {
for (uint i = 0; i < _views.size(); ++i) {
_views[i]->redraw();
_views[i]->draw();
}
}
bool Events::isPresent(const Common::String &name) const {
for (uint i = 0; i < _views.size(); ++i) {
if (_views[i]->_name == name)
return true;
}
return false;
}
void Events::clearViews() {
if (!_views.empty())
focusedView()->msgUnfocus(UnfocusMessage());
_views.clear();
}
void Events::addKeypress(const Common::KeyCode kc) {
Common::KeyState ks;
ks.keycode = kc;
if (kc >= Common::KEYCODE_SPACE && kc <= Common::KEYCODE_TILDE)
ks.ascii = kc;
focusedView()->msgKeypress(KeypressMessage(ks));
}
/*------------------------------------------------------------------------*/
Bounds::Bounds(Common::Rect &innerBounds) :
_bounds(0, 0, DEFAULT_SCX, DEFAULT_SCY),
_innerBounds(innerBounds),
left(_bounds.left), top(_bounds.top),
right(_bounds.right), bottom(_bounds.bottom) {
}
Bounds &Bounds::operator=(const Common::Rect &r) {
_bounds = r;
_innerBounds = r;
_innerBounds.grow(-_borderSize);
return *this;
}
void Bounds::setBorderSize(size_t borderSize) {
_borderSize = borderSize;
_innerBounds = *this;
_innerBounds.grow(-_borderSize);
}
/*------------------------------------------------------------------------*/
UIElement::UIElement(const Common::String &name) :
_name(name), _parent(g_engine), _bounds(_innerBounds) {
g_engine->_children.push_back(this);
}
UIElement::UIElement(const Common::String &name, UIElement *uiParent) :
_name(name), _parent(uiParent),
_bounds(_innerBounds) {
if (_parent)
_parent->_children.push_back(this);
}
void UIElement::redraw() {
_needsRedraw = true;
for (size_t i = 0; i < _children.size(); ++i)
_children[i]->redraw();
}
void UIElement::drawElements() {
if (_needsRedraw) {
draw();
_needsRedraw = false;
}
for (size_t i = 0; i < _children.size(); ++i)
_children[i]->drawElements();
}
UIElement *UIElement::findViewGlobally(const Common::String &name) {
return g_events->findView(name);
}
void UIElement::focus() {
g_events->replaceView(this);
}
void UIElement::close() {
assert(g_events->focusedView() == this);
g_events->popView();
}
bool UIElement::isFocused() const {
return g_events->focusedView() == this;
}
void UIElement::clearSurface() {
Graphics::ManagedSurface s = getSurface();
s.fillRect(Common::Rect(s.w, s.h), 0);
}
void UIElement::draw() {
for (size_t i = 0; i < _children.size(); ++i) {
_children[i]->draw();
}
}
bool UIElement::tick() {
if (_timeoutCtr && --_timeoutCtr == 0) {
timeout();
}
for (size_t i = 0; i < _children.size(); ++i) {
if (_children[i]->tick())
return true;
}
return false;
}
UIElement *UIElement::findView(const Common::String &name) {
if (_name.equalsIgnoreCase(name))
return this;
UIElement *result;
for (size_t i = 0; i < _children.size(); ++i) {
if ((result = _children[i]->findView(name)) != nullptr)
return result;
}
return nullptr;
}
void UIElement::replaceView(UIElement *ui, bool replaceAllViews) {
g_events->replaceView(ui, replaceAllViews);
}
void UIElement::replaceView(const Common::String &name, bool replaceAllViews) {
g_events->replaceView(name, replaceAllViews);
}
void UIElement::addView(UIElement *ui) {
g_events->addView(ui);
}
void UIElement::addView(const Common::String &name) {
g_events->addView(name);
}
void UIElement::addView() {
g_events->addView(this);
}
Gfx::GfxSurface UIElement::getSurface() const {
return Gfx::GfxSurface(*g_events->getScreen(), _bounds);
}
int UIElement::getRandomNumber(int minNumber, int maxNumber) {
return g_engine->getRandomNumber(maxNumber - minNumber + 1) + minNumber;
}
int UIElement::getRandomNumber(int maxNumber) {
return g_engine->getRandomNumber(maxNumber);
}
void UIElement::delaySeconds(uint seconds) {
_timeoutCtr = seconds * FRAME_RATE;
}
void UIElement::delayFrames(uint frames) {
_timeoutCtr = frames;
}
void UIElement::timeout() {
redraw();
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,425 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_EVENTS_H
#define ULTIMA0_EVENTS_H
#include "common/array.h"
#include "common/stack.h"
#include "graphics/screen.h"
#include "ultima/ultima0/messages.h"
#include "ultima/ultima0/gfx/gfx_surface.h"
namespace Ultima {
namespace Ultima0 {
#define FRAME_RATE 20
#define FRAME_DELAY (1000 / FRAME_RATE)
class Events;
/**
* Implements a thunk layer around an element's bounds,
* allowing access to it as if it were a simple Common::Rect,
* but any changes to it will also be applied to a linked inner bounds
*/
struct Bounds {
private:
Common::Rect _bounds;
Common::Rect &_innerBounds;
int _borderSize = 0;
public:
const int16 &left;
const int16 &top;
const int16 &right;
const int16 &bottom;
public:
Bounds(Common::Rect &innerBounds);
operator const Common::Rect &() const {
return _bounds;
}
Bounds &operator=(const Common::Rect &r);
void setBorderSize(size_t borderSize);
size_t borderSize() const {
return _borderSize;
}
int16 width() const {
return _bounds.width();
}
int16 height() const {
return _bounds.height();
}
};
/**
* User interface element
*/
class UIElement {
friend class Events;
private:
int _timeoutCtr = 0;
protected:
UIElement *_parent;
Common::Array<UIElement *> _children;
Common::Rect _innerBounds;
Bounds _bounds;
bool _needsRedraw = true;
Common::String _name;
protected:
/**
* Set a delay countdown in seconds, after which timeout() is called
*/
void delaySeconds(uint seconds);
/**
* Set a delay countdown in frames, after which timeout() is called
*/
void delayFrames(uint frames);
/**
* Returns true if a delay is active
*/
bool isDelayActive() const {
return _timeoutCtr != 0;
}
/**
* Cancels any active delay
*/
void cancelDelay() {
_timeoutCtr = 0;
}
/**
* Called when an active timeout countdown expired
*/
virtual void timeout();
private:
/**
* Outer method for doing drawing
*
*/
void drawElements();
/**
* Finds a view globally
*/
static UIElement *findViewGlobally(const Common::String &name);
public:
UIElement(const Common::String &name, UIElement *uiParent);
UIElement(const Common::String &name);
virtual ~UIElement() {
}
/**
* Returns true if the elements needs to be redrawn
*/
bool needsRedraw() const {
return _needsRedraw;
}
/**
* Sets that the element needs to be redrawn
*/
void redraw();
/**
* Focuses the element as the current view
*/
void focus();
/**
* Closes the current view. The view must have been added
* via addView, so there's a remaining view afterwards
*/
virtual void close();
/*
* Returns true if the view is focused
*/
bool isFocused() const;
/**
* Returns the view name
*/
Common::String getName() const {
return _name;
}
/**
* Sets the focus to a new view
*/
void replaceView(UIElement *ui, bool replaceAllViews = false);
void replaceView(const Common::String &name, bool replaceAllViews = false);
/**
* Adds a focused view to the view stack without replacing current one
*/
void addView(UIElement *ui);
void addView(const Common::String &name);
void addView();
void open() {
addView();
}
/**
* Returns a random number
*/
int getRandomNumber(int minNumber, int maxNumber);
int getRandomNumber(int maxNumber);
/**
* Sets the element's bounds
*/
virtual void setBounds(const Common::Rect &r) {
_bounds = r;
}
/**
* Gets the element's bounds
*/
Common::Rect getBounds() const {
return _bounds;
}
/**
* Returns a surface for drawing the element
*/
Gfx::GfxSurface getSurface() const;
/**
* Clear the surface
*/
virtual void clearSurface();
/**
* Draws the element
*/
virtual void draw();
/**
* Called for game frame ticks
*/
virtual bool tick();
/**
* Find a view by name
*/
virtual UIElement *findView(const Common::String &name);
/**
* Handles events
*/
// Mouse move only has a minimal implementation for performance reasons
protected:
virtual bool msgMouseMove(const MouseMoveMessage &msg) {
return false;
}
public:
bool send(const MouseMoveMessage &msg) {
return msgMouseMove(msg);
}
#define MESSAGE(NAME) \
protected: \
virtual bool msg##NAME(const NAME##Message &e) { \
for (Common::Array<UIElement *>::iterator it = _children.begin(); \
it != _children.end(); ++it) { \
if ((*it)->msg##NAME(e)) return true; \
} \
return false; \
} \
public: \
bool send(const Common::String &viewName, const NAME##Message &msg) { \
UIElement *view = UIElement::findViewGlobally(viewName); \
assert(view); \
return view->msg##NAME(msg); \
} \
bool send(const NAME##Message &msg) { \
return msg##NAME(msg); \
} \
MESSAGE(Focus);
MESSAGE(Unfocus);
MESSAGE(MouseEnter);
MESSAGE(MouseLeave);
MESSAGE(Keypress);
MESSAGE(MouseDown);
MESSAGE(MouseUp);
MESSAGE(Action);
MESSAGE(Game);
MESSAGE(Value);
#undef MESSAGE
};
/**
* Main events and view manager. This is kept separate from the engine
* class because the engine may add a lot of globals and bring in other
* classes. So to save on compilation time, classes that only need to
* access basic view management methods like addView or replaceView
* only need to include events.h rather than the whole engine.
*/
class Events : public UIElement {
private:
Graphics::Screen *_screen = nullptr;
Common::Stack<UIElement *> _views;
protected:
/**
* Process an event
*/
void processEvent(Common::Event &ev);
/**
* Returns true if the game should quit
*/
virtual bool shouldQuit() const = 0;
/**
* Overrides events we want to only go to the focused view
*/
#define MESSAGE(NAME) \
bool msg##NAME(const NAME##Message &e) override { \
return !_views.empty() ? focusedView()->msg##NAME(e) : false; \
}
MESSAGE(Action);
MESSAGE(Focus);
MESSAGE(Unfocus);
MESSAGE(MouseEnter);
MESSAGE(MouseLeave);
MESSAGE(Keypress);
MESSAGE(MouseDown);
MESSAGE(MouseUp);
MESSAGE(MouseMove);
#undef MESSAGE
public:
Events();
virtual ~Events();
/**
* Main game loop
*/
void runGame();
/**
* Sets the focus to a new view
*/
void replaceView(UIElement *ui, bool replaceAllViews = false);
void replaceView(const Common::String &name, bool replaceAllViews = false);
/**
* Adds a focused view to the view stack without replacing current one
*/
void addView(UIElement *ui);
void addView(const Common::String &name);
/**
* Clears the view list
*/
void clearViews();
/**
* Pops a view from the view stack
*/
void popView();
/**
* Redraws the views in order. This is used in rare cases
* where a view draws outside it's defined area, and needs
* to restore whether the background was before
*/
void redrawViews();
/**
* Returns the currently focused view, if any
*/
UIElement *focusedView() const {
return _views.empty() ? nullptr : _views.top();
}
/**
* Returns the view prior to the current view, if any
*/
UIElement *priorView() const {
return _views.size() < 2 ? nullptr :
_views[_views.size() - 2];
}
/**
* Returns true if a view of a given name is present
* at all in the visible view stack
*/
bool isPresent(const Common::String &name) const;
/**
* Returns true if combat is active
*/
bool isInCombat() const {
return isPresent("Combat");
}
/**
* Returns the underlying screen
*/
Graphics::Screen *getScreen() const {
return _screen;
}
/**
* Draws the focused view
*/
void drawElements() {
if (!_views.empty())
focusedView()->drawElements();
}
/**
* Add a keypress to the event queue
*/
void addKeypress(const Common::KeyCode kc);
/**
* Events manager doesn't have any intrinsic drawing
*/
void draw() override {
}
/**
* Called once every game frame
*/
bool tick() override {
return !_views.empty() ? focusedView()->tick() : false;
}
/**
* Calling the close method for g_events closes the active view
*/
void close() override {
focusedView()->close();
}
};
extern Events *g_events;
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,272 @@
/* 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/gfx/dungeon.h"
#include "ultima/ultima0/gfx/monster.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
int Dungeon::_xLeft;
int Dungeon::_xRight;
int Dungeon::_yBottom;
int Dungeon::_yDiffLeft;
int Dungeon::_yDiffRight;
#define HWColour(IDX) color = IDX
#define X(n) (x1 + w * (n)/10)
#define Y(n) (y1 + h * (n)/10)
#define HWLine(X1, Y1, X2, Y2) s->drawLine(X1, Y1, X2, Y2, color)
#define BOX(x1,y1,x2,y2) { HWLine(X(x1),Y(y1),X(x2),Y(y1));HWLine(X(x1),Y(y1),X(x1),Y(y2));HWLine(X(x2),Y(y2),X(x2),Y(y1));HWLine(X(x2),Y(y2),X(x1),Y(y2)); }
void Dungeon::draw(Graphics::ManagedSurface *s) {
const auto &player = g_engine->_player;
const auto &dungeon = g_engine->_dungeon;
s->clear();
double level = 0;
Common::Rect rIn, rOut;
Common::Point dir, pos, next;
int monster, front, left, right;
calcRect(s, &rOut, 0);
pos = player._dungeonPos; // Get position
// Iterate through drawing successively distinct tiles in the facing direction
do {
level++; // Next level
calcRect(s, &rIn, level);
next.x = pos.x + player._dungeonDir.x; // Next position
next.y = pos.y + player._dungeonDir.y;
dir = player._dungeonDir; rotateLeft(&dir); // To the left
left = dungeon._map[pos.x + dir.x][pos.y + dir.y];
rotateLeft(&dir); rotateLeft(&dir); // To the right
right = dungeon._map[pos.x + dir.x][pos.y + dir.y];
front = dungeon._map[next.x][next.y]; // What's in front ?
// Check for monster present
monster = dungeon.findMonster(pos);
if (monster >= 0)
monster = dungeon._monsters[monster]._type;
// Draw the dungeon
drawDungeon(s, &rOut, &rIn, left, front, right,
dungeon._map[pos.x][pos.y], monster);
pos = next; // Next position down
rOut = rIn; // Last in is new out
} while (level < MAX_VIEW_DEPTH && ISDRAWOPEN(front));
}
void Dungeon::calcRect(Graphics::ManagedSurface *s, Common::Rect *r, double level) {
const int centerX = s->w / 2, centerY = s->h / 2;
int xWidth = s->w / (level + 1);
int yWidth = s->h / (level + 1);
// Set bounding box
r->left = centerX - xWidth / 2;
r->right = centerX + xWidth / 2;
r->top = centerY - yWidth / 2;
r->bottom = centerY + yWidth / 2;
}
void Dungeon::rotateLeft(Common::Point *dir) {
int t;
if (dir->y == 0) dir->x = -dir->x;
t = dir->x;
dir->x = dir->y;
dir->y = t;
}
void Dungeon::drawDungeon(Graphics::ManagedSurface *s, Common::Rect *rOut,
Common::Rect *rIn, int left, int centre, int right, int room, int monster) {
int x1, y1, x, y, y2;
Common::Rect r;
double Scale;
int color;
HWColour(COL_WALL); // Start on the walls
// Do we draw the left edge
if (ISDRAWOPEN(left)) {
HWLine(rOut->left, rIn->top, rIn->left, rIn->top);
HWLine(rOut->left, rIn->bottom, rIn->left, rIn->bottom);
HWLine(rOut->left, rOut->top, rOut->left, rOut->bottom);
} else {
// If closed, draw left diags
HWLine(rOut->left, rOut->top, rIn->left, rIn->top);
HWLine(rOut->left, rOut->bottom, rIn->left, rIn->bottom);
}
// Do we draw the right edge
if (ISDRAWOPEN(right)) {
HWLine(rOut->right, rIn->top, rIn->right, rIn->top);
HWLine(rOut->right, rIn->bottom, rIn->right, rIn->bottom);
HWLine(rOut->right, rOut->top, rOut->right, rOut->bottom);
} else {
// If closed draw right diags
HWLine(rOut->right, rOut->top, rIn->right, rIn->top);
HWLine(rOut->right, rOut->bottom, rIn->right, rIn->bottom);
}
// Back wall ?
if (!ISDRAWOPEN(centre)) {
HWLine(rIn->left, rIn->top, rIn->right, rIn->top);
HWLine(rIn->left, rIn->bottom, rIn->right, rIn->bottom);
if (!ISDRAWOPEN(left)) // Corner if left,right closed
HWLine(rIn->left, rIn->top, rIn->left, rIn->bottom);
if (!ISDRAWOPEN(right))
HWLine(rIn->right, rIn->top, rIn->right, rIn->bottom);
}
// Set up for left side
setRange(rOut->left, rIn->left,
rOut->bottom,
rOut->bottom - rOut->top,
rIn->bottom - rIn->top);
drawWall(s, left);
// Set up for right side
setRange(rIn->right, rOut->right,
rIn->bottom,
rIn->bottom - rIn->top,
rOut->bottom - rOut->top);
drawWall(s, right);
// Set up for centre
setRange(rIn->left, rIn->right,
rIn->bottom,
rIn->bottom - rIn->top,
rIn->bottom - rIn->top);
drawWall(s, centre);
if (room == DT_LADDERUP) {
r = Common::Rect(rOut->left, rOut->top, rOut->right, rIn->top);
_DRAWPit(s, &r, 1);
}
if (room == DT_LADDERDN || room == DT_PIT) {
r = Common::Rect(rOut->left, rIn->bottom, rOut->right, rOut->bottom);
_DRAWPit(s, &r, -1);
}
// Get the object area
r = Common::Rect(
(rIn->left + rOut->left) / 2,
(rIn->top + rOut->top) / 2,
(rIn->right + rOut->right) / 2,
(rIn->bottom + rOut->bottom) / 2);
// Ladder here ?
if (room == DT_LADDERUP || room == DT_LADDERDN) {
HWColour(COL_LADDER);
y1 = r.top; y2 = r.bottom;
x = (r.right - r.left) * 3 / 10;
// Vertical lines
HWLine(r.left + x, y1, r.left + x, y2);
HWLine(r.right - x, y1, r.right - x, y2);
x1 = (y2 - y1) / 5;
// Horizontal ladder rungs
for (y = y1 + x1 / 2; y < y2; y += x1)
HWLine(r.left + x, y, r.right - x, y);
}
// Scale for monsters/gold. Scale factor has been empirically chosen
Scale = 0.35 / (r.right - r.left) * s->w;
// Monster here ?
if (monster > 0) {
HWColour(COL_MONSTER);
Monster::draw(s, (r.left + r.right) / 2, r.bottom, monster, Scale);
}
// Draw the gold (as a mimic)
if (room == DT_GOLD) {
HWColour(COL_MONSTER);
Monster::draw(s, (r.left + r.right) / 2, r.bottom, MN_MIMIC, Scale);
}
}
void Dungeon::setRange(int x1, int x2, int y, int yd1, int yd2) {
_xLeft = x1; _xRight = x2; // Set x ranges
_yBottom = y; // Set lower left y value
_yDiffLeft = yd1; _yDiffRight = yd2; // Set difference for either end
}
void Dungeon::drawWall(Graphics::ManagedSurface *s, int n) {
int x1, y1, x2, y2;
int color = 0;
if (n == DT_DOOR) {
HWColour(COL_DOOR);
x1 = 35; y1 = 0; x2 = 35; y2 = 60;
drawConvert(&x1, &y1);
drawConvert(&x2, &y2);
HWLine(x1, y1, x2, y2);
x1 = 65; y1 = 60; drawConvert(&x1, &y1);
HWLine(x1, y1, x2, y2);
x2 = 65; y2 = 0; drawConvert(&x2, &y2);
HWLine(x1, y1, x2, y2);
}
}
void Dungeon::drawConvert(int *px, int *py) {
long x, y, yd; // Longs for overflow in 16 bit
x = (_xRight - _xLeft); // Calculate width
x = x * (*px) / 100 + _xLeft; // Work out horiz value
yd = (_yDiffRight - _yDiffLeft); // Work out height of vert for x
yd = yd * (*px) / 100;
y = _yBottom + // Half of the distance
yd / 2 - // + Scaled total size
(yd + _yDiffLeft) * (*py) / 100;
*px = (int)x; // Write back, casting to int
*py = (int)y;
}
void Dungeon::_DRAWPit(Graphics::ManagedSurface *s, Common::Rect *r, int Dir) {
int x1, x2, y1;
int color;
HWColour(COL_HOLE);
y1 = (r->top - r->bottom) / 5;
r->bottom += y1; r->top -= y1;
x1 = (r->right - r->left) / 5;
r->left += x1; r->right -= x1;
x2 = 0; x1 = x1 / 2;
if (Dir > 0)
{
y1 = x1; x1 = x2; x2 = y1;
}
HWLine(r->left + x1, r->top, r->right - x1, r->top);
HWLine(r->left + x1, r->top, r->left + x2, r->bottom);
HWLine(r->left + x2, r->bottom, r->right - x2, r->bottom);
HWLine(r->right - x1, r->top, r->right - x2, r->bottom);
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,81 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_GFX_MAP_H
#define ULTIMA0_GFX_MAP_H
#include "graphics/managed_surface.h"
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
class Dungeon {
private:
// Slanted drawing constants
static int _xLeft, _xRight, _yBottom, _yDiffLeft, _yDiffRight;
/**
* Calculate display rectangle
*/
static void calcRect(Graphics::ManagedSurface *s, Common::Rect *r, double _level);
/**
* Rotate a direction left
*/
static void rotateLeft(Common::Point *Dir);
/**
* Draw part of dungeon
*/
static void drawDungeon(Graphics::ManagedSurface *s, Common::Rect *rOut,
Common::Rect *rIn, int Left, int Centre, int Right, int Room, int Monster);
/**
* Set the oblique drawing routine
*/
static void setRange(int x1, int x2, int y, int yd1, int yd2);
/**
* Draw wall object using current setting
*/
static void drawWall(Graphics::ManagedSurface *s, int n);
/**
* Convert coordinates from oblique to logical
*/
static void drawConvert(int *px, int *py);
/**
* Draw the pits/ladder hole
*/
static void _DRAWPit(Graphics::ManagedSurface *s, Common::Rect *r, int Dir);
public:
static void draw(Graphics::ManagedSurface *s);
};
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,224 @@
/* 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/gfx/font.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
static const byte FONT1[2048] = {
124,130,186,162,186,130,124,0,126,129,165,129,165,153,129,126,
126,129,165,129,153,165,129,126,108,246,246,254,124,56,16,0,
16,56,124,254,124,56,16,0,16,56,84,254,84,16,56,0,
56,124,254,254,108,16,56,0,16,24,20,20,48,112,96,0,
254,254,254,238,254,254,254,0,236,138,138,170,170,170,236,0,
142,136,136,140,136,136,232,0,174,170,170,234,170,170,174,0,
238,136,136,204,136,136,136,0,238,138,138,142,140,138,234,0,
62,34,62,34,102,238,204,0,16,84,40,198,40,84,16,0,
240,248,252,254,252,248,240,0,30,62,126,254,126,62,30,0,
16,56,124,16,124,56,16,0,238,238,238,238,238,0,238,0,
254,68,68,68,68,68,68,0,126,128,188,198,122,2,252,0,
0,0,0,0,255,255,0,0,16,56,124,16,124,56,16,254,
16,56,124,254,56,56,56,0,56,56,56,254,124,56,16,0,
16,24,252,254,252,24,16,0,16,48,126,254,126,48,16,0,
144,72,36,18,36,72,144,0,18,36,72,144,72,36,18,0,
16,40,68,146,40,68,130,0,130,68,40,146,68,40,16,0,
0,0,0,0,0,0,0,0,16,16,16,16,16,0,16,0,
40,40,40,0,0,0,0,0,68,254,68,68,68,254,68,0,
16,126,144,124,18,252,16,0,66,164,72,16,36,74,132,0,
56,68,56,112,138,132,122,0,16,16,32,0,0,0,0,0,
8,16,16,16,16,16,8,0,32,16,16,16,16,16,32,0,
16,84,56,254,56,84,16,0,16,16,16,254,16,16,16,0,
0,0,0,0,0,16,16,32,0,0,0,254,0,0,0,0,
0,0,0,0,0,0,16,0,2,4,8,16,32,64,128,0,
124,130,130,130,130,130,124,0,240,16,16,16,16,16,254,0,
252,2,2,124,128,128,254,0,252,2,2,28,2,2,252,0,
130,130,130,126,2,2,2,0,254,128,252,2,2,2,252,0,
126,128,252,130,130,130,124,0,252,2,2,2,2,2,2,0,
124,130,130,124,130,130,124,0,126,130,130,126,2,2,252,0,
0,0,0,16,0,0,16,0,0,0,0,16,0,0,16,32,
8,16,32,64,32,16,8,0,0,0,0,254,0,254,0,0,
64,32,16,8,16,32,64,0,56,68,4,8,16,0,16,0,
60,66,154,170,156,64,62,0,124,130,130,254,130,130,130,0,
252,130,130,252,130,130,252,0,124,130,128,128,128,130,124,0,
252,130,130,130,130,130,252,0,254,128,128,240,128,128,254,0,
254,128,128,240,128,128,128,0,124,130,128,142,130,130,124,0,
130,130,130,254,130,130,130,0,254,16,16,16,16,16,254,0,
62,2,2,2,130,130,124,0,130,132,136,240,136,132,130,0,
128,128,128,128,128,128,254,0,252,146,146,146,146,146,146,0,
130,194,162,146,138,134,130,0,124,130,130,130,130,130,124,0,
252,130,130,252,128,128,128,0,124,130,130,130,138,134,126,0,
252,130,130,252,130,130,130,0,126,128,128,124,2,2,252,0,
254,16,16,16,16,16,16,0,130,130,130,130,130,130,124,0,
130,130,68,68,40,40,16,0,130,130,130,146,146,146,108,0,
130,68,40,16,40,68,130,0,130,130,130,126,2,2,252,0,
254,4,8,16,32,64,254,0,56,32,32,32,32,32,56,0,
128,64,32,16,8,4,2,0,56,8,8,8,8,8,56,0,
16,40,68,130,0,0,0,0,0,0,0,0,0,0,0,255,
32,32,16,0,0,0,0,0,0,0,56,68,124,68,68,0,
0,0,120,68,120,68,120,0,0,0,60,64,64,64,60,0,
0,0,120,68,68,68,120,0,0,0,124,64,112,64,124,0,
0,0,124,64,112,64,64,0,0,0,60,64,76,68,60,0,
0,0,68,68,124,68,68,0,0,0,124,16,16,16,124,0,
0,0,28,4,4,68,56,0,0,0,68,72,112,72,68,0,
0,0,64,64,64,64,124,0,0,0,120,84,84,84,84,0,
0,0,120,68,68,68,68,0,0,0,56,68,68,68,56,0,
0,0,120,68,120,64,64,0,0,0,56,68,68,76,54,0,
0,0,120,68,120,68,68,0,0,0,60,64,56,4,120,0,
0,0,124,16,16,16,16,0,0,0,68,68,68,68,56,0,
0,0,68,68,40,40,16,0,0,0,68,68,84,108,68,0,
0,0,68,40,16,40,68,0,0,0,68,68,60,4,120,0,
0,0,124,8,16,32,124,0,8,16,16,32,16,16,8,0,
16,16,16,0,16,16,16,0,32,16,16,8,16,16,32,0,
80,40,0,0,0,0,0,0,0,16,40,68,130,130,254,0,
254,254,254,254,254,254,254,0,0,0,0,0,0,254,254,0,
0,0,124,124,124,124,124,0,0,0,0,0,0,0,124,0,
128,128,128,128,128,128,128,0,0,64,64,64,64,64,64,0,
16,24,28,30,28,24,16,0,16,48,112,240,112,48,16,0,
62,30,30,62,114,224,64,0,4,14,156,248,240,240,248,0,
64,224,114,62,30,30,62,0,248,240,240,248,156,14,4,0,
56,68,130,130,130,68,56,0,56,124,254,254,254,124,56,0,
0,124,68,68,68,124,0,0,0,124,124,124,124,124,0,0,
0,60,110,126,112,126,60,0,0,60,118,126,14,126,60,0,
0,60,126,106,126,126,106,0,0,60,126,86,126,126,86,0,
0,0,0,24,24,0,0,0,0,0,24,60,60,24,0,0,
0,12,52,36,36,108,72,0,0,0,0,0,0,0,0,0,
60,126,198,231,255,224,126,60,60,126,227,231,255,7,126,60,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,52,118,118,94,126,60,0,0,60,110,126,112,126,60,0,
0,60,126,122,110,110,44,0,0,60,126,14,126,118,60,0,
0,0,0,0,0,0,0,0,126,126,126,126,60,0,0,0,
0,15,31,31,31,31,15,0,126,127,127,127,127,127,63,0,
0,0,0,60,126,126,126,126,126,126,126,126,126,126,126,126,
0,63,127,127,127,127,127,126,126,127,127,127,127,127,127,126,
0,240,248,248,248,248,240,0,126,254,254,254,254,254,252,0,
0,255,255,255,255,255,255,0,126,255,255,255,255,255,255,0,
0,252,254,254,254,254,254,126,126,254,254,254,254,254,254,126,
0,255,255,255,255,255,255,126,126,255,255,255,255,255,255,126,
0,0,63,63,48,55,52,52,0,0,255,255,0,255,0,0,
0,0,248,248,24,216,88,88,88,88,88,88,88,88,88,88,
88,216,24,248,248,0,0,0,0,255,0,255,255,0,0,0,
52,55,48,63,63,0,0,0,52,52,52,52,52,52,52,52,
0,0,0,31,24,24,24,24,0,0,0,255,0,0,0,0,
0,0,0,240,48,48,48,48,48,48,48,48,48,48,48,48,
48,48,48,240,0,0,0,0,0,0,0,255,0,0,0,0,
24,24,24,31,0,0,0,0,24,24,24,24,24,24,24,24,
136,34,136,34,136,34,136,34,85,170,85,170,85,170,85,170,
68,170,68,170,68,170,68,170,51,102,204,153,51,102,204,153,
204,102,51,153,204,102,51,153,199,143,31,62,124,248,241,227,
227,241,248,124,62,31,143,199,174,128,186,2,234,8,171,32,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
void Font::writeChar(Graphics::ManagedSurface *dst, uint32 chr,
const Common::Point &textPos, byte textColor) {
if (chr == ' ')
return; // Don't do anything for spaces
const byte *GfxData = FONT1 + chr * 8;
Graphics::Surface charArea = dst->getSubArea(Common::Rect(textPos.x, textPos.y,
textPos.x + GLYPH_WIDTH, textPos.y + GLYPH_HEIGHT));
const int w = 1;
const int h = 1;
// Work through the 64 pixel array
for (int x = 0; x < 8; ++x) {
for (int y = 0; y < 8; ++y) {
// Check for whether character has pixel in this position
if (_FONTPixelSet(GfxData, x, y)) {
Common::Rect rc2(x * 2, y * 2, x * 2 + 2, y * 2 + 2);
charArea.fillRect(rc2, textColor);
// Neaten the diagonals
if (_FONTPixelSet(GfxData, x, y + 1) == 0 &&
_FONTPixelSet(GfxData, x - 1, y) == 0 &&
_FONTPixelSet(GfxData, x - 1, y + 1) != 0)
_FONTAngleDraw(&charArea, &rc2, -w, h, textColor);
if (_FONTPixelSet(GfxData, x, y + 1) == 0 &&
_FONTPixelSet(GfxData, x + 1, y) == 0 &&
_FONTPixelSet(GfxData, x + 1, y + 1) != 0)
_FONTAngleDraw(&charArea, &rc2, w, h, textColor);
}
}
}
}
bool Font::_FONTPixelSet(const byte * Data, int x, int y) {
if (x < 0 || y < 0 || x > 7 || y > 7)
return false;
return (Data[y] & (0x80 >> x)) ? 1 : 0;
}
void Font::_FONTAngleDraw(Graphics::Surface *s, Common::Rect *rc, int w, int h, byte colour) {
int i, m;
Common::Rect rc3;
m = ABS(w);
if (ABS(h) > m)
m = ABS(h);
for (i = 0; i < m; i++) {
rc3.left = rc->left + w * i / m;
rc3.top = rc->top + h * i / m;
rc3.setWidth(rc->width());
rc3.setHeight(rc->height());
s->fillRect(rc3, colour);
}
}
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,71 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_GFX_FONT_H
#define ULTIMA0_GFX_FONT_H
#include "common/rect.h"
#include "graphics/managed_surface.h"
#include "ultima/ultima0/data/defines.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
constexpr int GLYPH_HEIGHT = 16;
constexpr int GLYPH_WIDTH = 16;
constexpr int TEXT_WIDTH = DEFAULT_SCX / GLYPH_WIDTH;
constexpr int TEXT_HEIGHT = DEFAULT_SCY / GLYPH_HEIGHT;
class TextRect : public ::Common::Rect {
public:
TextRect() : ::Common::Rect() {}
TextRect(int left_, int top_, int right_, int bottom_) :
::Common::Rect(left_ * GLYPH_WIDTH, top_ * GLYPH_HEIGHT,
(right_ + 1) * GLYPH_WIDTH, (bottom_ + 1) * GLYPH_HEIGHT) {}
};
class Font {
private:
/**
* Returns true if a pixel is set in the source font data
*/
static bool _FONTPixelSet(const byte *Data, int x, int y);
/**
* Draw an angled line - this stops the squared corners on diagonals showing
*/
static void _FONTAngleDraw(Graphics::Surface *s, Common::Rect *rc,
int w, int h, byte colour);
public:
/**
* Draws a character onto the passed surface
*/
static void writeChar(Graphics::ManagedSurface *dst, uint32 chr,
const Common::Point &textPos, byte textColor);
};
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,109 @@
/* 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/gfx/gfx_surface.h"
#include "ultima/ultima0/gfx/font.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
void GfxSurface::writeString(const Common::Point &pt, const Common::String &str,
Graphics::TextAlign align) {
_textPos = pt;
writeString(str, align);
}
void GfxSurface::writeString(const Common::String &str, Graphics::TextAlign align) {
size_t strSize = 0;
for (const char *p = str.c_str(); *p; ++p)
strSize += Common::isPrint(*p) ? 1 : 0;
switch (align) {
case Graphics::kTextAlignCenter:
_textPos.x -= strSize / 2;
break;
case Graphics::kTextAlignRight:
_textPos.x -= strSize;
break;
case Graphics::kTextAlignLeft:
default:
break;
}
for (const char *p = str.c_str(); *p; ++p) {
if (*p == '\n') {
assert(align == Graphics::kTextAlignLeft);
newLine();
} else if (*p < 32) {
setColor((byte)*p);
} else {
writeChar(*p);
}
}
}
void GfxSurface::writeChar(uint32 chr) {
if (chr >= ' ') {
Font::writeChar(this, chr, Common::Point(_textPos.x * GLYPH_WIDTH,
_textPos.y * GLYPH_HEIGHT), _textColor);
++_textPos.x;
}
if (_textPos.x >= TEXT_WIDTH || chr == '\n') {
newLine();
}
}
void GfxSurface::newLine() {
_textPos.x = 0;
_textPos.y++;
if (_textPos.y >= TEXT_HEIGHT) {
_textPos.y = TEXT_HEIGHT - 1;
// Scroll the screen contents up
blitFrom(*this, Common::Rect(0, GLYPH_HEIGHT, DEFAULT_SCX, DEFAULT_SCY),
Common::Point(0, 0));
fillRect(Common::Rect(0, DEFAULT_SCX - GLYPH_HEIGHT, DEFAULT_SCX, DEFAULT_SCY), 0);
}
}
void GfxSurface::setTextPos(const Common::Point &pt) {
_textPos = pt;
}
byte GfxSurface::setColor(byte color) {
byte oldColor = _textColor;
_textColor = color;
return oldColor;
}
byte GfxSurface::setColor(byte r, byte g, byte b) {
byte oldColor = _textColor;
_textColor = g_engine->_palette.findBestColor(r, g, b);
return oldColor;
}
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_GFX_SURFACE_H
#define ULTIMA0_GFX_SURFACE_H
#include "graphics/font.h"
#include "graphics/managed_surface.h"
#include "ultima/ultima0/gfx/font.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
class GfxSurface : public Graphics::ManagedSurface {
private:
Common::Point _textPos;
byte _textColor = C_TEXT_DEFAULT;
void newLine();
public:
GfxSurface() : Graphics::ManagedSurface() {}
GfxSurface(Graphics::ManagedSurface &surf, const Common::Rect &bounds) : Graphics::ManagedSurface(surf, bounds) {}
/**
* Write some text to the surface
*/
void writeString(const Common::Point &pt, const Common::String &str,
Graphics::TextAlign align = Graphics::kTextAlignLeft);
void writeString(const Common::String &str, Graphics::TextAlign align = Graphics::kTextAlignLeft);
void writeChar(uint32 chr);
void setTextPos(const Common::Point &pt);
byte setColor(byte color);
byte setColor(byte r, byte g, byte b);
};
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,142 @@
/* 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/gfx/map.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
void Map::draw(Graphics::ManagedSurface *s, bool showAsMap) {
const auto &player = g_engine->_player;
const auto &map = g_engine->_worldMap;
s->clear();
int x, y, x1, y1, w, h, grid;
Common::Rect r;
grid = showAsMap ? WORLD_MAP_SIZE : (g_engine->isEnhanced() ? 7 : 3);
w = s->w / grid; h = s->h / grid; // Get grid sizes
for (x = 0; x < grid; x++) {
for (y = 0; y < grid; y++) {
r = Common::Rect(x * w, y * h, x * w + w - 1, y * h + h - 1);
if (showAsMap) {
// If map, not centered around us
x1 = x, y1 = y;
} else {
// Which cell?
x1 = player._worldPos.x - grid / 2 + x;
y1 = player._worldPos.y - grid / 2 + y;
}
drawTile(s, r, map.read(x1, y1));
// Draw us if we're there
if (x1 == player._worldPos.x && y1 == player._worldPos.y)
drawTile(s, r, WT_PLAYER);
}
}
}
#define HWColour(IDX) color = IDX
#define X(n) (x1 + w * (n)/10)
#define Y(n) (y1 + h * (n)/10)
#define HWLine(X1, Y1, X2, Y2) s->drawLine(X1, Y1, X2, Y2, color)
#define BOX(x1,y1,x2,y2) { HWLine(X(x1),Y(y1),X(x2),Y(y1));HWLine(X(x1),Y(y1),X(x1),Y(y2));HWLine(X(x2),Y(y2),X(x2),Y(y1));HWLine(X(x2),Y(y2),X(x1),Y(y2)); }
void Map::drawTile(Graphics::ManagedSurface *s, const Common::Rect &r, int obj) {
int x1 = r.left;
int y1 = r.top;
int w = r.width();
int h = r.height();
byte color = 0;
// Decide on the object
switch (obj) {
case WT_SPACE:
// Space does nothing at all
break;
case WT_MOUNTAIN:
// Mountain the cracked effect
HWColour(COL_MOUNTAIN);
HWLine(X(2), Y(6), X(2), Y(10));
HWLine(X(0), Y(8), X(2), Y(8));
HWLine(X(2), Y(6), X(4), Y(6));
HWLine(X(4), Y(6), X(4), Y(4));
HWLine(X(2), Y(2), X(4), Y(4));
HWLine(X(2), Y(2), X(2), Y(0));
HWLine(X(2), Y(2), X(0), Y(2));
HWLine(X(8), Y(4), X(4), Y(4));
HWLine(X(8), Y(4), X(8), Y(0));
HWLine(X(8), Y(2), X(10), Y(2));
HWLine(X(6), Y(4), X(6), Y(8));
HWLine(X(10), Y(8), X(6), Y(8));
HWLine(X(8), Y(8), X(8), Y(10));
break;
case WT_TREE:
// Tree is just a box
HWColour(COL_TREE);
BOX(3, 3, 7, 7);
break;
case WT_TOWN:
// Town is 5 boxes
HWColour(COL_TOWN);
BOX(2, 2, 4, 4); BOX(4, 4, 6, 6); BOX(6, 6, 8, 8);
BOX(6, 2, 8, 4); BOX(2, 6, 4, 8);
break;
case WT_DUNGEON:
// Dungeon is a cross
HWColour(COL_DUNGEON);
HWLine(X(3), Y(3), X(7), Y(7));
HWLine(X(7), Y(3), X(3), Y(7));
break;
case WT_BRITISH:
// British castle
HWColour(COL_BRITISH);
HWLine(X(2), Y(2), X(8), Y(8));
HWLine(X(8), Y(2), X(2), Y(8));
BOX(0, 0, 10, 10);
BOX(2, 2, 8, 8);
break;
case WT_PLAYER:
// Player
HWColour(COL_PLAYER);
HWLine(X(4), Y(5), X(6), Y(5));
HWLine(X(5), Y(4), X(5), Y(6));
break;
default:
break;
}
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_GFX_MAP_H
#define ULTIMA0_GFX_MAP_H
#include "graphics/managed_surface.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
class Map {
private:
static void drawTile(Graphics::ManagedSurface *s, const Common::Rect &r, int obj);
public:
static void draw(Graphics::ManagedSurface *s, bool showAsMap = false);
};
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,205 @@
/* 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/gfx/monster.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
Monster::DrawFn Monster::DRAW_FUNCTIONS[] = {
nullptr, drawSkeleton, drawThief, drawRat, drawOrc, drawViper,
drawCarrion, drawGremlin, drawMimic, drawDaemon, drawBalrog
};
int Monster::_xPos = 640;
int Monster::_yPos = 512;
constexpr int color = COL_MONSTER;
// End marker
#define END (-9999.99)
#define X(n) (x1 + w * (n)/10)
#define Y(n) (y1 + h * (n)/10)
#define HWLine(X1, Y1, X2, Y2) s->drawLine(X1, Y1, X2, Y2, color)
#define BOX(x1,y1,x2,y2) { HWLine(X(x1),Y(y1),X(x2),Y(y1));HWLine(X(x1),Y(y1),X(x1),Y(y2));HWLine(X(x2),Y(y2),X(x2),Y(y1));HWLine(X(x2),Y(y2),X(x1),Y(y2)); }
void Monster::draw(Graphics::ManagedSurface *s, int x, int y, int monster, double scale) {
// Save drawing pos
_xPos = x; _yPos = y;
if (monster == MN_MIMIC)
// Fix for Mimic/Chest
_xPos -= 90;
// Call appropriate function
assert(monster > 0 && monster <= MN_BALROG);
(*DRAW_FUNCTIONS[monster])(s, 0, 0, scale);
}
void Monster::hPlot(Graphics::ManagedSurface *s, double x, double y, ...) {
va_list alist;
double y1, x1;
// Start reading values
va_start(alist, y);
bool isPixel = true;
do {
x1 = va_arg(alist, double); // Get the next two
y1 = va_arg(alist, double);
if (x1 != END && y1 != END) {
// If legit, draw the line
HWLine(_xPos + x, _yPos + y, _xPos + x1, _yPos + y1);
isPixel = false;
} else if (isPixel)
// Single pixel only
HWLine(_xPos + x, _yPos + y, _xPos + x, _yPos + y);
x = x1; y = y1;
} while (x1 != END && y1 != END);
va_end(alist);
}
void Monster::drawSkeleton(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x - 23 / d, y, x - 15 / d, y, x - 15 / d, y - 15 / d, x - 8 / d, y - 30 / d, x + 8 / d, y - 30 / d, x + 15 / d, y - 15 / d, x + 15 / d, y, x + 23 / d, y, END, END);
hPlot(s, x, y - 26 / d, x, y - 65 / d, END, END);
hPlot(s, x - 2 / d + .5, y - 38 / d, x + 2 / d + .5, y - 38 / d, END, END);
hPlot(s, x - 3 / d + .5, y - 45 / d, x + 3 / d + .5, y - 45 / d, END, END);
hPlot(s, x - 5 / d + .5, y - 53 / d, x + 5 / d + .5, y - 53 / d, END, END);
hPlot(s, x - 23 / d, y - 56 / d, x - 30 / d, y - 53 / d, x - 23 / d, y - 45 / d, x - 23 / d, y - 53 / d, x - 8 / d, y - 38 / d, END, END);
hPlot(s, x - 15 / d, y - 45 / d, x - 8 / d, y - 60 / d, x + 8 / d, y - 60 / d, x + 15 / d, y - 45 / d, END, END);
hPlot(s, x + 15 / d, y - 42 / d, x + 15 / d, y - 57 / d, END, END);
hPlot(s, x + 12 / d, y - 45 / d, x + 20 / d, y - 45 / d, END, END);
hPlot(s, x, y - 75 / d, x - 5 / d + .5, y - 80 / d, x - 8 / d, y - 75 / d, x - 5 / d + .5, y - 65 / d, x + 5 / d + .5, y - 65 / d, x + 5 / d + .5, y - 68 / d, x - 5 / d + .5, y - 68 / d, x - 5 / d + .5, y - 65 / d, END, END);
hPlot(s, x + 5 / d + .5, y - 65 / d, x + 8 / d, y - 75 / d, x + 5 / d + .5, y - 80 / d, x - 5 / d + .5, y - 80 / d, END, END);
hPlot(s, x - 5 / d + .5, y - 72 / d, END, END);
hPlot(s, x + 5 / d + .5, y - 72 / d, END, END);
}
void Monster::drawThief(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x, y - 56 / d, x, y - 8 / d, x + 10 / d, y, x + 30 / d, y, x + 30 / d, y - 45 / d, x + 10 / d, y - 64 / d, x, y - 56 / d, END, END);
hPlot(s, x - 10 / d, y - 64 / d, x - 30 / d, y - 45 / d, x - 30 / d, y, x - 10 / d, y, x, y - 8 / d, END, END);
hPlot(s, x - 10 / d, y - 64 / d, x - 10 / d, y - 75 / d, x, y - 83 / d, x + 10 / d, y - 75 / d, x, y - 79 / d, x - 10 / d, y - 75 / d, x, y - 60 / d, x + 10 / d, y - 75 / d, x + 10 / d, y - 64 / d, END, END);
}
void Monster::drawRat(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x + 5 / d, y - 30 / d, x, y - 25 / d, x - 5 / d, y - 30 / d, x - 15 / d, y - 5 / d, x - 10 / d, y, x + 10 / d, y, x + 15 / d, y - 5 / d, END, END);
hPlot(s, x + 20 / d, y - 5 / d, x + 10 / d, y, x + 15 / d, y - 5 / d, x + 5 / d, y - 30 / d, x + 10 / d, y - 40 / d, x + 3 / d + .5, y - 35 / d, x - 3 / d + .5, y - 35 / d, x - 10 / d, y - 40 / d, x - 5 / d, y - 30 / d, END, END);
hPlot(s, x - 5 / d, y - 33 / d, x - 3 / d + .5, y - 30 / d, END, END);
hPlot(s, x + 5 / d, y - 33 / d, x + 3 / d + .5, y - 30 / d, END, END);
hPlot(s, x - 5 / d, y - 20 / d, x - 5 / d, y - 15 / d, END, END);
hPlot(s, x + 5 / d, y - 20 / d, x + 5 / d, y - 15 / d, END, END);
hPlot(s, x - 7 / d, y - 20 / d, x - 7 / d, y - 15 / d, END, END);
hPlot(s, x + 7 / d, y - 20 / d, x + 7 / d, y - 15 / d, END, END);
}
void Monster::drawOrc(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x, y, x - 15 / d, y, x - 8 / d, y - 8 / d, x - 8 / d, y - 15 / d, x - 15 / d, y - 23 / d, x - 15 / d, y - 15 / d, x - 23 / d, y - 23 / d, END, END);
hPlot(s, x - 23 / d, y - 23 / d, x - 15 / d, y - 53 / d, x - 8 / d, y - 53 / d, x - 15 / d, y - 68 / d, x - 8 / d, y - 75 / d, x, y - 75 / d, END, END);
hPlot(s, x, y, x + 15 / d, y, x + 8 / d, y - 8 / d, x + 8 / d, y - 15 / d, x + 15 / d, y - 23 / d, x + 15 / d, y - 15 / d, x + 23 / d, y - 23 / d, END, END);
hPlot(s, x + 23 / d, y - 23 / d, x + 15 / d, y - 53 / d, x + 8 / d, y - 53 / d, x + 15 / d, y - 68 / d, x + 8 / d, y - 75 / d, x, y - 75 / d, END, END);
hPlot(s, x - 15 / d, y - 68 / d, x + 15 / d, y - 68 / d, END, END);
hPlot(s, x - 8 / d, y - 53 / d, x + 8 / d, y - 53 / d, END, END);
hPlot(s, x - 23 / d, y - 15 / d, x + 8 / d, y - 45 / d, END, END);
hPlot(s, x - 8 / d, y - 68 / d, x, y - 60 / d, x + 8 / d, y - 68 / d, x + 8 / d, y - 60 / d, x - 8 / d, y - 60 / d, x - 8 / d, y - 68 / d, END, END);
hPlot(s, x, y - 38 / d, x - 8 / d, y - 38 / d, x + 8 / d, y - 53 / d, x + 8 / d, y - 45 / d, x + 15 / d, y - 45 / d, x, y - 30 / d, x, y - 38 / d, END, END);
}
void Monster::drawViper(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x - 10 / d, y - 15 / d, x - 10 / d, y - 30 / d, x - 15 / d, y - 20 / d, x - 15 / d, y - 15 / d, x - 15 / d, y, x + 15 / d, y, x + 15 / d, y - 15 / d, x - 15 / d, y - 15 / d, END, END);
hPlot(s, x - 15 / d, y - 10 / d, x + 15 / d, y - 10 / d, END, END);
hPlot(s, x - 15 / d, y - 5 / d, x + 15 / d, y - 5 / d, END, END);
hPlot(s, x, y - 15 / d, x - 5 / d, y - 20 / d, x - 5 / d, y - 35 / d, x + 5 / d, y - 35 / d, x + 5 / d, y - 20 / d, x + 10 / d, y - 15 / d, END, END);
hPlot(s, x - 5 / d, y - 20 / d, x + 5 / d, y - 20 / d, END, END);
hPlot(s, x - 5 / d, y - 25 / d, x + 5 / d, y - 25 / d, END, END);
hPlot(s, x - 5 / d, y - 30 / d, x + 5 / d, y - 30 / d, END, END);
hPlot(s, x - 10 / d, y - 35 / d, x - 10 / d, y - 40 / d, x - 5 / d, y - 45 / d, x + 5 / d, y - 45 / d, x + 10 / d, y - 40 / d, x + 10 / d, y - 35 / d, END, END);
hPlot(s, x - 10 / d, y - 40 / d, x, y - 45 / d, x + 10 / d, y - 40 / d, END, END);
hPlot(s, x - 5 / d, y - 40 / d, x + 5 / d, y - 40 / d, x + 15 / d, y - 30 / d, x, y - 40 / d, x - 15 / d, y - 30 / d, x - 5 / d + .5, y - 40 / d, END, END);
}
void Monster::drawCarrion(Graphics::ManagedSurface *s, double x, double y, double d) {
// 79-dst.recty(d) line here
hPlot(s, x - 20 / d, y - 79 / d, x - 20 / d, y - 88 / d, x - 10 / d, y - 83 / d, x + 10 / d, y - 83 / d, x + 20 / d, y - 88 / d, x + 20 / d, y - 79 / d, x - 20 / d, y - 79 / d, END, END);
hPlot(s, x - 20 / d, y - 88 / d, x - 30 / d, y - 83 / d, x - 30 / d, y - 78 / d, END, END);
hPlot(s, x + 20 / d, y - 88 / d, x + 30 / d, y - 83 / d, x + 40 / d, y - 83 / d, END, END);
hPlot(s, x - 15 / d, y - 86 / d, x - 20 / d, y - 83 / d, x - 20 / d, y - 78 / d, x - 30 / d, y - 73 / d, x - 30 / d, y - 68 / d, x - 20 / d, y - 63 / d, END, END);
hPlot(s, x - 10 / d, y - 83 / d, x - 10 / d, y - 58 / d, x, y - 50 / d, END, END);
hPlot(s, x + 10 / d, y - 83 / d, x + 10 / d, y - 78 / d, x + 20 / d, y - 73 / d, x + 20 / d, y - 40 / d, END, END);
hPlot(s, x + 15 / d, y - 85 / d, x + 20 / d, y - 78 / d, x + 30 / d, y - 76 / d, x + 30 / d, y - 60 / d, END, END);
hPlot(s, x, y - 83 / d, x, y - 73 / d, x + 10 / d, y - 68 / d, x + 10 / d, y - 63 / d, x, y - 58 / d, END, END);
}
void Monster::drawGremlin(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x + 5 / d + 1, y - 10 / d, x - 5 / d + 1, y - 10 / d, x, y - 15 / d, x + 10 / d, y - 20 / d, x + 5 / d + 1, y - 15 / d, x + 5 / d + 1, y - 10 / d, END, END);
hPlot(s, x + 5 / d + 1, y - 10 / d, x + 7 / d + 1, y - 6 / d, x + 5 / d + 1, y - 3 / d, x - 5 / d + 1, y - 3 / d, x - 7 / d + 1, y - 6 / d, x - 5 / d + 1, y - 10 / d, END, END);
hPlot(s, x + 2 / d + 1, y - 3 / d, x + 5 / d + 1, y, x + 8 / d, y, END, END);
hPlot(s, x - 2 / d + 1, y - 3 / d, x - 5 / d + 1, y, x - 8 / d, y, END, END);
hPlot(s, x + 3 / d + 1, y - 8 / d, END, END);
hPlot(s, x - 3 / d + 1, y - 8 / d, END, END);
hPlot(s, x + 3 / d + 1, y - 5 / d, x - 3 / d + 1, y - 5 / d, END, END);
}
void Monster::drawMimic(Graphics::ManagedSurface *s, double x, double y, double d) {
double xx = x;
hPlot(s, 139 - 10 / d, xx, 139 - 10 / d, xx - 10 / d, 139 + 10 / d, xx - 10 / d, 139 + 10 / d, xx, 139 - 10 / d, xx, END, END);
hPlot(s, 139 - 10 / d, xx - 10 / d, 139 - 5 / d, xx - 15 / d, 139 + 15 / d, xx - 15 / d, 139 + 15 / d, xx - 5 / d, 139 + 10 / d, xx, END, END);
hPlot(s, 139 + 10 / d, xx - 10 / d, 139 + 15 / d, xx - 15 / d, END, END);
}
void Monster::drawDaemon(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x - 14 / d, y - 46 / d, x - 12 / d, y - 37 / d, x - 20 / d, y - 32 / d, x - 30 / d, y - 32 / d, x - 22 / d, y - 24 / d, x - 40 / d, y - 17 / d, x - 40 / d, y - 7 / d, x - 38 / d, y - 5 / d, x - 40 / d, y - 3 / d, x - 40 / d, y, END, END);
hPlot(s, x - 40 / d, y, x - 36 / d, y, x - 34 / d, y - 2 / d, x - 32 / d, y, x - 28 / d, y, x - 28 / d, y - 3 / d, x - 30 / d, y - 5 / d, x - 28 / d, y - 7 / d, x - 28 / d, y - 15 / d, x, y - 27 / d, END, END);
hPlot(s, x + 14 / d, y - 46 / d, x + 12 / d, y - 37 / d, x + 20 / d, y - 32 / d, x + 30 / d, y - 32 / d, x + 22 / d, y - 24 / d, x + 40 / d, y - 17 / d, x + 40 / d, y - 7 / d, x + 38 / d, y - 5 / d, x + 40 / d, y - 3 / d, x + 40 / d, y, END, END);
hPlot(s, x + 40 / d, y, x + 36 / d, y, x + 34 / d, y - 2 / d, x + 32 / d, y, x + 28 / d, y, x + 28 / d, y - 3 / d, x + 30 / d, y - 5 / d, x + 28 / d, y - 7 / d, x + 28 / d, y - 15 / d, x, y - 27 / d, END, END);
hPlot(s, x + 6 / d, y - 48 / d, x + 38 / d, y - 41 / d, x + 40 / d, y - 42 / d, x + 18 / d, y - 56 / d, x + 12 / d, y - 56 / d, x + 10 / d, y - 57 / d, x + 8 / d, y - 56 / d, x - 8 / d, y - 56 / d, x - 10 / d, y - 58 / d, x + 14 / d, y - 58 / d, x + 16 / d, y - 59 / d, END, END);
hPlot(s, x + 16 / d, y - 59 / d, x + 8 / d, y - 63 / d, x + 6 / d, y - 63 / d, x + 2 / d + .5, y - 70 / d, x + 2 / d + .5, y - 63 / d, x - 2 / d + .5, y - 63 / d, x - 2 / d + .5, y - 70 / d, x - 6 / d, y - 63 / d, x - 8 / d, y - 63 / d, x - 16 / d, y - 59 / d, x - 14 / d, y - 58 / d, x - 10 / d, y - 58 / d, END, END);
hPlot(s, x - 10 / d, y - 57 / d, x - 12 / d, y - 56 / d, x - 18 / d, y - 56 / d, x - 36 / d, y - 47 / d, x - 36 / d, y - 39 / d, x - 28 / d, y - 41 / d, x - 28 / d, y - 46 / d, x - 20 / d, y - 50 / d, x - 18 / d, y - 50 / d, x - 14 / d, y - 46 / d, END, END);
hPlot(s, x - 28 / d, y - 41 / d, x + 30 / d, y - 55 / d, END, END);
hPlot(s, x + 28 / d, y - 58 / d, x + 22 / d, y - 56 / d, x + 22 / d, y - 53 / d, x + 28 / d, y - 52 / d, x + 34 / d, y - 54 / d, END, END);
hPlot(s, x + 20 / d, y - 50 / d, x + 26 / d, y - 47 / d, END, END);
hPlot(s, x + 10 / d, y - 58 / d, x + 10 / d, y - 61 / d, x + 4 / d, y - 58 / d, END, END);
hPlot(s, x - 10 / d, y - 58 / d, x - 10 / d, y - 61 / d, x - 4 / d, y - 58 / d, END, END);
hPlot(s, x + 40 / d, y - 9 / d, x + 50 / d, y - 12 / d, x + 40 / d, y - 7 / d, END, END);
hPlot(s, x - 8 / d, y - 25 / d, x + 6 / d, y - 7 / d, x + 28 / d, y - 7 / d, x + 28 / d, y - 9 / d, x + 20 / d, y - 9 / d, x + 6 / d, y - 25 / d, END, END);
}
void Monster::drawBalrog(Graphics::ManagedSurface *s, double x, double y, double d) {
hPlot(s, x + 6 / d, y - 60 / d, x + 30 / d, y - 90 / d, x + 60 / d, y - 30 / d, x + 60 / d, y - 10 / d, x + 30 / d, y - 40 / d, x + 15 / d, y - 40 / d, END, END);
hPlot(s, x - 6 / d, y - 60 / d, x - 30 / d, y - 90 / d, x - 60 / d, y - 30 / d, x - 60 / d, y - 10 / d, x - 30 / d, y - 40 / d, x - 15 / d, y - 40 / d, END, END);
hPlot(s, x, y - 25 / d, x + 6 / d, y - 25 / d, x + 10 / d, y - 20 / d, x + 12 / d, y - 10 / d, x + 10 / d, y - 6 / d, x + 10 / d, y, x + 14 / d, y, x + 15 / d, y - 5 / d, x + 16 / d, y, x + 20 / d, y, END, END);
hPlot(s, x + 20 / d, y, x + 20 / d, y - 6 / d, x + 18 / d, y - 10 / d, x + 18 / d, y - 20 / d, x + 15 / d, y - 30 / d, x + 15 / d, y - 45 / d, x + 40 / d, y - 60 / d, x + 40 / d, y - 70 / d, END, END);
hPlot(s, x + 40 / d, y - 70 / d, x + 10 / d, y - 55 / d, x + 6 / d, y - 60 / d, x + 10 / d, y - 74 / d, x + 6 / d, y - 80 / d, x + 4 / d + .5, y - 80 / d, x + 3 / d + .5, y - 82 / d, x + 2 / d + .5, y - 80 / d, x, y - 80 / d, END, END);
hPlot(s, x, y - 25 / d, x - 6 / d, y - 25 / d, x - 10 / d, y - 20 / d, x - 12 / d, y - 10 / d, x - 10 / d, y - 6 / d, x - 10 / d, y, x - 14 / d, y, x - 15 / d, y - 5 / d, x - 16 / d, y, x - 20 / d, y, END, END);
hPlot(s, x - 20 / d, y, x - 20 / d, y - 6 / d, x - 18 / d, y - 10 / d, x - 18 / d, y - 20 / d, x - 15 / d, y - 30 / d, x - 15 / d, y - 45 / d, x - 40 / d, y - 60 / d, x - 40 / d, y - 70 / d, END, END); // left wing
hPlot(s, x - 40 / d, y - 70 / d, x - 10 / d, y - 55 / d, x - 6 / d, y - 60 / d, x - 10 / d, y - 74 / d, x - 6 / d, y - 80 / d, x - 4 / d + .5, y - 80 / d, x - 3 / d + .5, y - 82 / d, x - 2 / d + .5, y - 80 / d, x, y - 80 / d, END, END);
hPlot(s, x - 6 / d, y - 25 / d, x, y - 6 / d, x + 10 / d, y, x + 4 / d + .5, y - 8 / d, x + 6 / d, y - 25 / d, END, END);
hPlot(s, x - 40 / d, y - 64 / d, x - 40 / d, y - 90 / d, x - 52 / d, y - 80 / d, x - 52 / d, y - 40 / d, END, END);
hPlot(s, x + 40 / d, y - 86 / d, x + 38 / d, y - 92 / d, x + 42 / d, y - 92 / d, x + 40 / d, y - 86 / d, x + 40 / d, y - 50 / d, END, END);
hPlot(s, x + 4 / d + .5, y - 70 / d, x + 6 / d, y - 74 / d, END, END);
hPlot(s, x - 4 / d + .5, y - 70 / d, x - 6 / d, y - 74 / d, END, END);
hPlot(s, x, y - 64 / d, x, y - 60 / d, END, END);
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,67 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_GFX_MONSTER_H
#define ULTIMA0_GFX_MONSTER_H
#include "graphics/managed_surface.h"
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
namespace Gfx {
class Monster {
private:
// Drawing position
static int _xPos, _yPos;
/**
* Emulate the Apple ][ HPLOT function
*/
static void hPlot(Graphics::ManagedSurface *s, double x, double y, ...);
/**
* Monster drawing functions
*/
typedef void(*DrawFn)(Graphics::ManagedSurface *s, double x, double y, double d);
static void drawSkeleton(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawThief(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawRat(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawOrc(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawViper(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawCarrion(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawGremlin(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawMimic(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawDaemon(Graphics::ManagedSurface *s, double x,double y,double d);
static void drawBalrog(Graphics::ManagedSurface *s, double x,double y,double d);
static DrawFn DRAW_FUNCTIONS[];
public:
static void draw(Graphics::ManagedSurface *s, int x, int y,
int monster, double scale);
};
} // namespace Gfx
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,45 @@
/* 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/messages.h"
namespace Ultima {
namespace Ultima0 {
MouseMessage::MouseMessage(Common::EventType type,
const Common::Point &pos) : Message(), _pos(pos) {
switch (type) {
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP:
_button = MB_RIGHT;
break;
case Common::EVENT_MBUTTONDOWN:
case Common::EVENT_MBUTTONUP:
_button = MB_MIDDLE;
break;
default:
_button = MB_LEFT;
break;
}
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,134 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_MESSAGES_H
#define ULTIMA0_MESSAGES_H
#include "common/array.h"
#include "common/events.h"
#include "common/str.h"
#include "ultima/ultima0/metaengine.h"
namespace Ultima {
namespace Ultima0 {
class UIElement;
struct Message {};
struct FocusMessage : public Message {
UIElement *_priorView = nullptr;
FocusMessage() : Message() {
}
FocusMessage(UIElement *priorView) : Message(),
_priorView(priorView) {
}
};
struct UnfocusMessage : public Message {};
struct MouseEnterMessage : public Message {};
struct MouseLeaveMessage : public Message {};
struct KeypressMessage : public Message, public Common::KeyState {
KeypressMessage() : Message() {
}
KeypressMessage(const Common::KeyState &ks) :
Message(), Common::KeyState(ks) {
}
};
struct MouseMessage : public Message {
enum Button {
MB_LEFT, MB_RIGHT, MB_MIDDLE
};
Button _button;
Common::Point _pos;
MouseMessage() : Message(), _button(MB_LEFT) {
}
MouseMessage(Button btn, const Common::Point &pos) :
Message(), _button(btn), _pos(pos) {
}
MouseMessage(Common::EventType type, const Common::Point &pos);
};
struct MouseDownMessage : public MouseMessage {
MouseDownMessage() : MouseMessage() {
}
MouseDownMessage(Button btn, const Common::Point &pos) :
MouseMessage(btn, pos) {
}
MouseDownMessage(Common::EventType type, const Common::Point &pos) :
MouseMessage(type, pos) {
}
};
struct MouseUpMessage : public MouseMessage {
MouseUpMessage() : MouseMessage() {
}
MouseUpMessage(Button btn, const Common::Point &pos) :
MouseMessage(btn, pos) {
}
MouseUpMessage(Common::EventType type, const Common::Point &pos) :
MouseMessage(type, pos) {
}
};
typedef MouseMessage MouseMoveMessage;
struct GameMessage : public Message {
Common::String _name;
int _value;
Common::String _stringValue;
GameMessage() : Message(), _value(-1) {
}
GameMessage(const Common::String &name) : Message(),
_name(name), _value(-1) {
}
GameMessage(const Common::String &name, int value) : Message(),
_name(name), _value(value) {
}
GameMessage(const Common::String &name, const Common::String &value) :
Message(), _name(name), _stringValue(value) {
}
};
struct ValueMessage : public Message {
int _value;
ValueMessage() : Message(), _value(0) {
}
ValueMessage(int value) : Message(),
_value(value) {
}
};
struct ActionMessage : public Message {
int _action;
ActionMessage() : Message(), _action(0) {
}
ActionMessage(int action) : Message(),
_action(action) {
}
};
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,204 @@
/* 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/metaengine.h"
#include "ultima/ultima0/ultima0.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/standard-actions.h"
namespace Ultima {
namespace Ultima0 {
struct KeybindingRecord {
KeybindingAction _action;
const char *_id;
const char *_desc;
const char *_key;
const char *_joy;
};
static const KeybindingRecord MINIMAL_KEYS[] = {
{ KEYBIND_ESCAPE, "ESCAPE", _s("Escape"), "ESCAPE", "JOY_Y" },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
};
static const KeybindingRecord MENU_KEYS[] = {
{ KEYBIND_UP, "UP", _s("Up"), "UP", "JOY_UP" },
{ KEYBIND_DOWN, "DOWN", _s("Down"), "DOWN", "JOY_DOWN" },
{ KEYBIND_SELECT, "SELECT", _s("Select"), "SPACE", nullptr },
{ KEYBIND_QUIT, "QUIT", _s("Quit"), "q", nullptr },
{ KEYBIND_SWING, "SWING", _s("Swing"), "s", nullptr },
{ KEYBIND_THROW, "THROW", _s("Throw"), "t", nullptr },
{ KEYBIND_FOOD, "FOOD", _s("Food"), "f", nullptr },
{ KEYBIND_RAPIER, "RAPIER", _s("Rapier"), "r", nullptr },
{ KEYBIND_AXE, "AXE", _s("Axe"), "a", nullptr },
{ KEYBIND_SHIELD, "SHIELD", _s("Shield"), "s", nullptr },
{ KEYBIND_BOW, "BOW", _s("Bow & Arrow"), "b", nullptr },
{ KEYBIND_AMULET, "AMULET", _s("Magic Amulet"), "m", nullptr },
{ KEYBIND_AMULET1, "AMULET1", _s("Amulet Option 1"), "1", nullptr },
{ KEYBIND_AMULET2, "AMULET2", _s("Amulet Option 2"), "2", nullptr },
{ KEYBIND_AMULET3, "AMULET3", _s("Amulet Option 3"), "3", nullptr },
{ KEYBIND_AMULET4, "AMULET4", _s("Amulet Option 4"), "4", nullptr },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
};
static const KeybindingRecord OVERWORLD_KEYS[] = {
{ KEYBIND_UP, "UP", _s("North"), "UP", "JOY_UP" },
{ KEYBIND_DOWN, "DOWN", _s("South"), "DOWN", "JOY_DOWN" },
{ KEYBIND_LEFT, "LEFT", _s("West"), "LEFT", "JOY_LEFT" },
{ KEYBIND_RIGHT, "RIGHT", _s("East"), "RIGHT", "JOY_RIGHT" },
{ KEYBIND_ENTER, "ENTER", _s("Enter/Exit"), "e", "JOY_B" },
{ KEYBIND_INFO, "INFO", _s("Info"), "z", "JOY_X" },
{ KEYBIND_QUIT, "QUIT", _s("Quit"), "q", nullptr },
{ KEYBIND_PASS, "PASS", _s("Pass/Wait"), "SPACE", nullptr },
{ KEYBIND_MINIMAP, "MINIMAP", _s("Minimap"), "m", nullptr },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
};
static const KeybindingRecord DUNGEON_KEYS[] = {
{ KEYBIND_UP, "UP", _s("Move Forward"), "UP", "JOY_UP" },
{ KEYBIND_DOWN, "DOWN", _s("Turn Around"), "DOWN", "JOY_DOWN" },
{ KEYBIND_LEFT, "LEFT", _s("Turn Left"), "LEFT", "JOY_LEFT" },
{ KEYBIND_RIGHT, "RIGHT", _s("Turn Right"), "RIGHT", "JOY_RIGHT" },
{ KEYBIND_ATTACK, "ATTACK", _s("Attack"), "a", "JOY_A" },
{ KEYBIND_ENTER, "ENTER", _s("Enter/Exit"), "e", "JOY_B" },
{ KEYBIND_INFO, "INFO", _s("Info"), "z", "JOY_X" },
{ KEYBIND_QUIT, "QUIT", _s("Quit"), "q", nullptr },
{ KEYBIND_PASS, "PASS", _s("Pass/Wait"), "SPACE", nullptr },
{ KEYBIND_MINIMAP, "MINIMAP", _s("Minimap"), "m", nullptr },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
};
struct KeysRecord {
const char *_id;
const char *_desc;
const KeybindingRecord *_keys;
};
static const KeysRecord ALL_RECORDS[] = {
{ "ultima0", _s("Basic Keys"), MINIMAL_KEYS },
{ "menu", _s("Menu Keys"), MENU_KEYS },
{ "overworld", _s("Overworld Keys"), OVERWORLD_KEYS },
{ "dungeon", _s("Dungeon Keys"), DUNGEON_KEYS },
{ nullptr, nullptr, nullptr }
};
static const KeysRecord MINIMAL_RECORDS[] = {
{ "ultima0", _s("Basic Keys"), MINIMAL_KEYS },
{ nullptr, nullptr, nullptr }
};
static const KeysRecord MENU_RECORDS[] = {
{ "ultima0", _s("Basic Keys"), MINIMAL_KEYS },
{ "menu", _s("Menu Keys"), MENU_KEYS },
{ nullptr, nullptr, nullptr }
};
static const KeysRecord OVERWORLD_RECORDS[] = {
{ "ultima0", _s("Basic Keys"), MINIMAL_KEYS },
{ "overworld", _s("Overworld Keys"), OVERWORLD_KEYS },
{ nullptr, nullptr, nullptr }
};
static const KeysRecord DUNGEON_RECORDS[] = {
{ "ultima0", _s("Basic Keys"), MINIMAL_KEYS },
{ "dungeon", _s("Dungeon Keys"), DUNGEON_KEYS },
{ nullptr, nullptr, nullptr }
};
static const KeysRecord *MODE_RECORDS[] = {
ALL_RECORDS,
MINIMAL_RECORDS,
MENU_RECORDS,
OVERWORLD_RECORDS,
DUNGEON_RECORDS,
};
Common::KeymapArray MetaEngine::initKeymaps(KeybindingMode mode) {
Common::KeymapArray keymapArray;
Common::Keymap *keyMap;
Common::Action *act;
const KeysRecord *recPtr = MODE_RECORDS[mode];
for (int kCtr = 0; recPtr->_id; ++recPtr, ++kCtr) {
// Core keymaps
keyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame,
recPtr->_id, recPtr->_desc);
keymapArray.push_back(keyMap);
if (kCtr == 0) {
addMouseClickActions(*keyMap);
}
for (const KeybindingRecord *r = recPtr->_keys; r->_id; ++r) {
act = new Common::Action(r->_id, _(r->_desc));
act->setCustomEngineActionEvent(r->_action);
act->addDefaultInputMapping(r->_key);
if (r->_joy)
act->addDefaultInputMapping(r->_joy);
if (r->_action == KEYBIND_ENTER)
act->addDefaultInputMapping("x"); // x also works to eXit
else if (r->_action == KEYBIND_SELECT)
act->addDefaultInputMapping("RETURN");
else if (r->_action == KEYBIND_QUIT)
act->addDefaultInputMapping("p"); // Map "Pause" to Quit
if (r->_action == KEYBIND_UP || r->_action == KEYBIND_DOWN ||
r->_action == KEYBIND_LEFT || r->_action == KEYBIND_RIGHT)
// Allow movement actions to be triggered on keyboard repeats
act->allowKbdRepeats();
keyMap->addAction(act);
}
}
return keymapArray;
}
void MetaEngine::addMouseClickActions(Common::Keymap &keyMap) {
Common::Action *act;
act = new Common::Action(Common::kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
keyMap.addAction(act);
act = new Common::Action(Common::kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
keyMap.addAction(act);
}
void MetaEngine::setKeybindingMode(KeybindingMode mode) {
Common::Keymapper *const mapper = g_engine->getEventManager()->getKeymapper();
mapper->cleanupGameKeymaps();
Common::KeymapArray arr = initKeymaps(mode);
for (uint idx = 0; idx < arr.size(); ++idx)
mapper->addGameKeymap(arr[idx]);
}
} // End of namespace Ultima0
} // End of namespace Ultima

View File

@@ -0,0 +1,72 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_METAENGINE_H
#define ULTIMA0_METAENGINE_H
#include "backends/keymapper/keymapper.h"
namespace Ultima {
namespace Ultima0 {
enum KeybindingAction {
KEYBIND_UP, KEYBIND_DOWN, KEYBIND_LEFT, KEYBIND_RIGHT,
KEYBIND_ESCAPE, KEYBIND_QUIT, KEYBIND_ENTER, KEYBIND_INFO,
KEYBIND_PASS, KEYBIND_ATTACK, KEYBIND_SWING, KEYBIND_THROW,
KEYBIND_AMULET1, KEYBIND_AMULET2, KEYBIND_AMULET3, KEYBIND_AMULET4,
KEYBIND_MINIMAP,
KEYBIND_SELECT, KEYBIND_FOOD, KEYBIND_RAPIER, KEYBIND_AXE,
KEYBIND_SHIELD, KEYBIND_BOW, KEYBIND_AMULET,
KEYBIND_NONE
};
enum KeybindingMode {
KBMODE_ALL,
KBMODE_MINIMAL,
KBMODE_MENUS,
KBMODE_OVERWORLD,
KBMODE_DUNGEONS
};
class MetaEngine {
private:
/**
* Adds the default actions for the mouse buttons
*/
static void addMouseClickActions(Common::Keymap &keyMap);
public:
/**
* Initialize keymaps
*/
static Common::KeymapArray initKeymaps(KeybindingMode mode = KBMODE_ALL);
/**
* Sets the current set of actions which are active
*/
static void setKeybindingMode(KeybindingMode mode);
};
} // End of namespace Ultima0
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,93 @@
/* 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 "audio/mididrv.h"
#include "audio/midiparser.h"
#include "common/debug.h"
#include "common/file.h"
#include "ultima/ultima0/music.h"
namespace Ultima {
namespace Ultima0 {
MusicPlayer::MusicPlayer(const char *filename) : _filename(filename) {
MidiPlayer::createDriver();
int ret = _driver->open();
if (ret == 0) {
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
_driver->setTimerCallback(this, &timerCallback);
}
}
void MusicPlayer::sendToChannel(byte channel, uint32 b) {
if (!_channelsTable[channel]) {
_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
// If a new channel is allocated during the playback, make sure
// its volume is correctly initialized.
if (_channelsTable[channel])
_channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
}
if (_channelsTable[channel])
_channelsTable[channel]->send(b);
}
void MusicPlayer::playSMF(bool loop) {
Common::StackLock lock(_mutex);
stop();
// Load MIDI resource data
Common::File musicFile;
if (!musicFile.open(_filename))
return;
int midiMusicSize = musicFile.size();
free(_midiData);
_midiData = (byte *)malloc(midiMusicSize);
musicFile.read(_midiData, midiMusicSize);
musicFile.close();
MidiParser *parser = MidiParser::createParser_SMF();
if (parser->loadMusic(_midiData, midiMusicSize)) {
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(_driver->getBaseTempo());
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_parser = parser;
syncVolume();
_isLooping = loop;
_isPlaying = true;
} else {
delete parser;
}
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,47 @@
/* 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/>.
*
*/
// Music class
#ifndef ULTIMA_ULTIMA0_MUSIC_H
#define ULTIMA_ULTIMA0_MUSIC_H
#include "audio/midiplayer.h"
namespace Ultima {
namespace Ultima0 {
class MusicPlayer : public Audio::MidiPlayer {
private:
Common::Path _filename;
public:
MusicPlayer(const char *filename);
void playSMF(bool loop = true);
// Overload Audio::MidiPlayer method
void sendToChannel(byte channel, uint32 b) override;
};
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,138 @@
/* 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/system.h"
#include "common/savefile.h"
#include "engines/util.h"
#include "graphics/paletteman.h"
#include "ultima/ultima0/ultima0.h"
#include "ultima/ultima0/console.h"
namespace Ultima {
namespace Ultima0 {
static const byte PALETTE[][3] = {
{ 0, 0, 0 }, // Black
{ 0, 0, 255 }, // Blue
{ 0, 255, 0 }, // Green,
{ 0, 255, 255 }, // Cyan
{ 255, 0, 0 }, // Red
{ 255, 0, 255 }, // Purple
{ 255, 255, 0 }, // Yellow
{ 255, 255, 255 }, // White
{ 255, 0, 128 }, // Rose
{ 80, 80, 255 }, // Violet
{ 180, 180, 180 }, // Grey
{ 255, 80, 80 } // Orange
};
Ultima0Engine *g_engine;
Ultima0Engine::Ultima0Engine(OSystem *syst, const Ultima::UltimaGameDescription *gameDesc) :
Engine(syst), /*_gameDescription(gameDesc), */_randomSource("Ultima0"),
_palette(&PALETTE[0][0], sizeof(PALETTE) / 3) {
g_engine = this;
}
Ultima0Engine::~Ultima0Engine() {
stopMidi();
}
Common::Error Ultima0Engine::run() {
// Initialize the graphics
initGraphics(DEFAULT_SCX, DEFAULT_SCY);
g_system->getPaletteManager()->setPalette(_palette);
// Set the debugger console
setDebugger(new Console());
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
// Play the game
runGame();
return Common::kNoError;
}
bool Ultima0Engine::hasFeature(EngineFeature f) const {
return (f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
bool Ultima0Engine::canSaveGameStateCurrently(Common::U32String *msg) {
auto *view = focusedView();
if (!view)
return false;
Common::String name = focusedView()->getName();
return name == "WorldMap" || name == "Dungeon";
}
Common::Error Ultima0Engine::loadGameStream(Common::SeekableReadStream *stream) {
Common::Serializer s(stream, nullptr);
syncSavegame(s);
stopMidi();
replaceView(_player._level == 0 ? "WorldMap" : "Dungeon");
return Common::kNoError;
}
Common::Error Ultima0Engine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
Common::Serializer s(nullptr, stream);
syncSavegame(s);
return Common::kNoError;
}
void Ultima0Engine::syncSavegame(Common::Serializer &s) {
_player.synchronize(s);
_worldMap.synchronize(s);
_dungeon.synchronize(s);
}
bool Ultima0Engine::savegamesExist() const {
Common::String slotName = getSaveStateName(1);
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
bool result = saveFile != nullptr;
delete saveFile;
return result;
}
void Ultima0Engine::playMidi(const char *name) {
stopMidi();
_music = new MusicPlayer(name);
_music->playSMF(true);
}
void Ultima0Engine::stopMidi() {
if (_music) {
_music->stop();
delete _music;
_music = nullptr;
}
}
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,137 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_H
#define ULTIMA0_H
#include "common/random.h"
#include "common/serializer.h"
#include "graphics/palette.h"
#include "engines/engine.h"
#include "ultima/detection.h"
#include "ultima/ultima0/data/data.h"
#include "ultima/ultima0/events.h"
#include "ultima/ultima0/music.h"
namespace Ultima {
namespace Ultima0 {
class Ultima0Engine : public Engine, public Events {
private:
Common::RandomSource _randomSource;
//const UltimaGameDescription *_gameDescription;
void syncSavegame(Common::Serializer &s);
protected:
// Engine APIs
Common::Error run() override;
public:
Graphics::Palette _palette;
PlayerInfo _player;
WorldMapInfo _worldMap;
DungeonMapInfo _dungeon;
bool _showMinimap = false;
MusicPlayer *_music = nullptr;
Ultima0Engine(OSystem *syst, const Ultima::UltimaGameDescription *gameDesc);
~Ultima0Engine() override;
/**
* Returns true if the game should quit
*/
bool shouldQuit() const override {
return Engine::shouldQuit();
}
/**
* Returns supported engine features
*/
bool hasFeature(EngineFeature f) const override;
/**
* Sets the random number seed
*/
void setRandomSeed(uint seed) {
_randomSource.setSeed(seed);
}
/**
* Get a random number
*/
uint getRandomNumber(uint maxVal = RND_MAX) { return _randomSource.getRandomNumber(maxVal); }
uint getRandomNumber(uint minVal, uint maxVal) {
return _randomSource.getRandomNumber(maxVal - minVal) + minVal;
}
/**
* Returns true if enhancements are turned on
*/
bool isEnhanced() const {
return true;
}
/**
* Returns true if a savegame can be loaded
*/
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
return true;
}
/**
* Returns true if the game can be saved
*/
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
/**
* Load a game state.
* @param stream the stream to load the savestate from
* @return returns kNoError on success, else an error code.
*/
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
/**
* Save a game state.
* @param stream The write stream to save the savegame data to
* @param isAutosave Expected to be true if an autosave is being created
* @return returns kNoError on success, else an error code.
*/
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
/**
* Returns true if any savegames exist
*/
bool savegamesExist() const;
void playMidi(const char *name);
void stopMidi();
bool isMidiPlaying() const {
return _music != nullptr;
}
};
extern Ultima0Engine *g_engine;
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,112 @@
/* 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/acknowledgements.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
static const char *LINES[] = {
"Akalabeth PC-Conversion",
"",
"",
"Written by Richard Garriott",
"",
"",
"",
"PC-Conversion by",
"Corey Roth",
"with special assistance by",
"Nathan Tucker",
"",
"",
"",
"",
"Greets and Thanx go out to",
"Jake Groshong",
"Ronny McCrory",
"Adrian Dilley",
"Russell Kabir",
"James Ashley",
"Chris Harjo",
"and everyone else out there",
"who helped me out",
"",
"",
"",
"",
"This game made possible by:",
"",
"Origin",
"and",
"JUiCEY Bird Productions"
};
bool Acknowledgements::msgFocus(const FocusMessage &msg) {
// Set up the lines to display
_ctr = -1;
_lines.clear();
for (const char *line : LINES)
_lines.push(line);
for (int i = 0; i < 25; ++i)
_lines.push("");
auto s = getSurface();
s.clear();
// Create a buffer for rendering new lines
_pendingLine.create(s.w, Gfx::GLYPH_HEIGHT);
return true;
}
void Acknowledgements::draw() {
auto s = getSurface();
// Shift up by one line
s.blitFrom(s, Common::Rect(0, 1, s.w, s.h), Common::Point(0, 0));
s.blitFrom(_pendingLine, Common::Rect(0, 0, _pendingLine.w, 1),
Common::Point(0, s.h - 1));
_pendingLine.blitFrom(_pendingLine, Common::Rect(0, 1, s.w, Gfx::GLYPH_HEIGHT),
Common::Point(0, 0));
}
bool Acknowledgements::tick() {
_ctr = (_ctr + 1) % Gfx::GLYPH_HEIGHT;
if (_ctr == 0) {
if (_lines.empty()) {
showTitle();
} else {
Common::String line = _lines.pop();
_pendingLine.writeString(Common::Point(20, 0), line, Graphics::kTextAlignCenter);
}
}
redraw();
return View::tick();
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_ACKNOWLEDGEMENTS_H
#define ULTIMA0_VIEWS_ACKNOWLEDGEMENTS_H
#include "common/queue.h"
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Acknowledgements : public View {
private:
Common::Queue<Common::String> _lines;
Gfx::GfxSurface _pendingLine;
int _ctr = 0;
void showTitle() {
replaceView("Title");
}
public:
Acknowledgements() : View("Acknowledgements") {}
~Acknowledgements() override {}
bool msgFocus(const FocusMessage &msg) override;
void draw() override;
bool tick() override;
bool msgKeypress(const KeypressMessage &msg) override {
showTitle();
return true;
}
bool msgMouseDown(const MouseDownMessage &msg) override {
showTitle();
return true;
}
bool msgAction(const ActionMessage &msg) override {
showTitle();
return true;
}
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,330 @@
/* 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/attack.h"
#include "ultima/ultima0/data/data.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
Attack::Attack() : View("Attack") {
setBounds(Gfx::TextRect(1, 22, 26, 24));
}
bool Attack::msgFocus(const FocusMessage &msg) {
_mode = WHICH_WEAPON;
_message = "";
MetaEngine::setKeybindingMode(KBMODE_MENUS);
return true;
}
bool Attack::msgUnfocus(const UnfocusMessage &msg) {
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
return true;
}
void Attack::draw() {
auto s = getSurface();
s.clear();
if (_mode == AMULET) {
s.writeString(Common::Point(0, 0), "1] Ladder Up");
s.writeString(Common::Point(0, 1), "2] Ladder Down");
s.writeString(Common::Point(0, 2), "3] Attack Monster");
s.writeString(Common::Point(0, 3), "4] Bad Magic");
return;
}
s.writeString(Common::Point(1, 1), "Which Weapon? ");
if (_mode != WHICH_WEAPON)
s.writeString(_weapon <= 0 ? "Hands" : OBJECT_INFO[_weapon]._name);
if (!_message.empty())
s.writeString(Common::Point(1, 2), _message);
}
bool Attack::msgKeypress(const KeypressMessage &msg) {
if (_mode == WHICH_WEAPON) {
selectObject(-1);
return true;
}
return false;
}
bool Attack::msgAction(const ActionMessage &msg) {
int objNum;
switch (_mode) {
case WHICH_WEAPON:
// Check for object selection, anything but food
objNum = -1;
for (uint i = OB_RAPIER; i < MAX_OBJ; ++i) {
if (msg._action == OBJECT_INFO[i]._action) {
objNum = i;
break;
}
}
selectObject(objNum);
break;
case AMULET:
if (msg._action >= KEYBIND_AMULET1 && msg._action <= KEYBIND_AMULET4) {
selectMagic(msg._action - KEYBIND_AMULET1);
}
break;
case THROW_SWING:
if (msg._action == KEYBIND_THROW) {
_message += "Throw\n";
attackMissile();
} else if (msg._action == KEYBIND_SWING) {
_message += "Swing\n";
attackWeapon();
}
break;
default:
break;
}
return true;
}
void Attack::selectObject(int objNum) {
auto &player = g_engine->_player;
_weapon = objNum;
_damage = (objNum <= 0) ? 0 : OBJECT_INFO[objNum]._maxDamage;
// Must own an object
if (player._object[_weapon] == 0) {
showMessage("Not owned.");
return;
}
// Mages are limited
if (player._class == 'M' && (objNum == OB_BOW || objNum == OB_RAPIER)) {
showMessage("Mages can't use that.");
return;
}
// Use an amulet
if (objNum == OB_AMULET) {
if (player._class == 'M') {
// Mages can properly select the magic to use
_mode = AMULET;
// Amulet selection requires all four lines
_bounds = Gfx::TextRect(0, 22, 26, 24);
redraw();
} else {
// Fighters get a random effect
selectMagic(g_engine->getRandomNumber(3));
}
return;
}
if (objNum == OB_BOW) {
attackMissile();
} else if (objNum == OB_AXE) {
_mode = THROW_SWING;
_message += "Throw or Swing ? ";
redraw();
} else {
attackWeapon();
}
}
void Attack::selectMagic(int magicNum) {
auto &dungeon = g_engine->_dungeon;
auto &player = g_engine->_player;
int i;
// Last charge?
Common::String msg;
if (urand() % 5 == 0) {
msg = "Last charge on this Amulet.\n";
player._object[OB_AMULET]--;
}
switch (magicNum) {
case 0:
// Ladder up
dungeon._map[player._dungeonPos.x][player._dungeonPos.y] = DT_LADDERUP;
break;
case 1:
// Ladder down
dungeon._map[player._dungeonPos.x][player._dungeonPos.y] = DT_LADDERDN;
break;
case 2:
// Amulet Attack
attackMissile();
break;
case 3:
// Bad Magic
switch (urand() % 3) {
case 0:
msg += "You have been turned into a Toad.\n";
for (i = AT_STRENGTH; i <= AT_WISDOM; i++)
player._attr[i] = 3;
break;
case 1:
msg += "You have been turned into a Lizard Man.\n";
for (i = AT_HP; i <= AT_WISDOM; i++)
player._attr[i] = floor(player._attr[i] * 5 / 2);
break;
case 2:
msg += "Backfire !!\n";
player._attr[AT_HP] = floor(player._attr[AT_HP]) / 2;
break;
}
break;
}
if (msg.empty()) {
timeout();
} else {
redraw();
delaySeconds(1);
}
}
void Attack::attackMissile() {
const auto &dungeon = g_engine->_dungeon;
auto &player = g_engine->_player;
Common::Point c1, c = player._dungeonPos;
int Dist = -1;
// A maximum distance of 5
for (int y = 0; y < 5; y++) {
c += player._dungeonDir;
int n = dungeon.findMonster(c); // Monster there ?
if (n >= 0) {
c1 = c;
Dist = n;
}
// If wall, or door, stop
if (!ISDRAWOPEN(dungeon._map[c.x][c.y]))
break;
}
if (Dist < 0) {
// Hit nothing
_message += "You missed !!\n";
_mode = DONE;
delaySeconds(1);
} else {
attackHitMonster(c1);
}
}
void Attack::attackWeapon() {
const auto &player = g_engine->_player;
Common::Point c = player._dungeonPos + player._dungeonDir;
attackHitMonster(c);
}
void Attack::attackHitMonster(const Common::Point &c) {
auto &player = g_engine->_player;
auto &dungeon = g_engine->_dungeon;
int n = 0, monNum, damage;
MonsterEntry *m = nullptr;
// Is there a monster there ?
monNum = dungeon.findMonster(c);
if (monNum >= 0) {
// Set up a pointer
m = &dungeon._monsters[monNum];
n = m->_type;
}
// Get weaponry info
damage = 0;
if (_weapon >= 0 && _weapon != OB_AMULET)
damage = OBJECT_INFO[_weapon]._maxDamage;
if (_weapon == OB_AMULET)
// Amulet Special Case
damage = 10 + player._level;
// If no, or not dexterous
if (monNum < 0 || player._attr[AT_DEXTERITY] - ((int)urand() % 25) < n + player._level) {
// Then a miss.
_message += "\nMissed!";
_mode = DONE;
redraw();
delaySeconds(1);
return;
}
// Scored a hit
_message += "Hit !!!\n";
// Calculate HPs lost
n = 0;
if (damage > 0)
n = (urand() % damage);
n = n + player._attr[AT_STRENGTH] / 5;
m->_strength = m->_strength - n; // Lose them
if (m->_strength < 0)
m->_strength = 0;
_message += Common::String::format("%s's Hit\nPoints now %d.\n",
MONSTER_INFO[m->_type]._name, m->_strength);
// Killed it ?
if (m->_strength == 0) {
m->_alive = false; // It has ceased to be
int gold = (m->_type + player._level); // Amount of gold
_message += Common::String::format("You get %d pieces of eight.\n", gold);
player._attr[AT_GOLD] += gold;
player._hpGain += (m->_type * player._level) / 2; // Calculate Gain
if (m->_type == player._task) // Check done LB's task
player._taskCompleted = true;
}
_mode = DONE;
redraw();
delaySeconds(1);
}
void Attack::showMessage(const Common::String &msg) {
_message = msg;
_mode = DONE;
redraw();
delaySeconds(1);
}
void Attack::timeout() {
close();
g_events->send("Dungeon", GameMessage("ENDOFTURN"));
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_ATTACK_H
#define ULTIMA0_VIEWS_ATTACK_H
#include "ultima/ultima0/views/view.h"
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Attack : public View {
private:
enum Mode {
WHICH_WEAPON, AMULET, THROW_SWING, DONE
};
Mode _mode = WHICH_WEAPON;
int _weapon = -1;
int _damage = 0;
Common::String _message;
void selectObject(int objNum);
void selectMagic(int magicNum);
void attackMissile();
void attackWeapon();
void attackHitMonster(const Common::Point &c);
void showMessage(const Common::String &msg);
public:
Attack();
~Attack() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override;
bool msgAction(const ActionMessage &msg) override;
void timeout() override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,218 @@
/* 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/castle.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
static const char *GO_FORTH = " Go now upon this quest, and may\n"
"Lady Luck be fair unto you.....\n"
".....Also I, British, have increased\n"
"each of thy attributes by one.!";
bool Castle::msgFocus(const FocusMessage &msg) {
const auto &player = g_engine->_player;
if (Common::String(player._name).empty())
_mode = NAMING;
else if (player._taskCompleted) {
_mode = TASK_COMPLETE;
} else {
_mode = TASK_INCOMPLETE;
}
g_engine->playMidi("lordbrit.mid");
return true;
}
bool Castle::msgUnfocus(const UnfocusMessage &msg) {
g_engine->stopMidi();
return true;
}
void Castle::draw() {
auto s = getSurface();
s.clear();
if (_mode <= FIRST_TASK) {
firstTime();
} else if (_mode == TASK_COMPLETE) {
taskCompleted();
} else if (_mode == TASK_INCOMPLETE) {
taskIncomplete();
}
}
void Castle::firstTime() {
auto s = getSurface();
const auto &player = g_engine->_player;
if (_mode >= NAMING && _mode <= FIRST_TASK) {
s.writeString(Common::Point(5, 2), "Welcome Peasant into the halls of\n"
"the mighty Lord British. Herein thou\n"
"may choose to dare battle with the\n"
"evil creatures of the depths, for\n"
"great reward!\n\n"
"What is thy name peasant? ");
s.setColor(C_GREY);
s.writeString(_playerName);
s.setColor(C_TEXT_DEFAULT);
}
if (_mode >= GRAND_ADVENTURE && _mode <= FIRST_TASK)
s.writeString(Common::Point(0, 10), "Doest thou wish for great adventure ?\n\n");
if (_mode == BEGONE) {
s.writeString("Then leave and begone!");
} else if (_mode == FIRST_TASK) {
s.writeString("Good! Thou shalt try to become a\nKnight!!!\n\n"
"Thy first task is to go into the\n"
"dungeons and to return only after\n"
"killing ");
s.setColor(C_VIOLET);
s.writeString(getTaskName(player._task));
pressAnyKey();
}
}
void Castle::taskCompleted() {
auto &player = g_engine->_player;
auto s = getSurface();
s.writeString(Common::Point(0, 3), "Aaaahhhh.... ");
s.writeString(player._name);
s.writeString("\n\nThou has accomplished\nthy quest.\n\n");
if (player._task == MAX_MONSTERS) {
s.writeString("Thou hast proved thyself worthy of\n"
"Knighthood, continue if thou doth wish\n"
"but thou hast accomplished the\n"
"main objective of the game.\n\n"
"Now, maybe thou art foolhardy enough to\n"
"try difficulty level ");
s.writeString(Common::String::format("%d.", player._skill + 1));
} else {
// LB gives you extra attributes
for (int i = 0; i < MAX_ATTR; i++)
player._attr[i]++;
// Choose the next task
nextTask();
s.writeString("Unfortunately, this is not enough to\n"
"become a knight.\n\n");
s.writeString("Thou must now kill ");
s.setColor(C_VIOLET);
s.writeString(getTaskName(player._task));
s.setColor(C_TEXT_DEFAULT);
s.writeString("\n\n");
s.writeString(GO_FORTH);
pressAnyKey();
}
}
void Castle::taskIncomplete() {
const auto &player = g_engine->_player;
auto s = getSurface();
s.writeString(Common::Point(0, 3),
"Why hast thou returned ?\n"
"Thou must kill ");
s.setColor(C_VIOLET);
s.writeString(getTaskName(player._task));
s.setColor(C_TEXT_DEFAULT);
s.writeString("\n\nGo now and complete thy quest");
}
void Castle::pressAnyKey() {
auto s = getSurface();
s.writeString(Common::Point(20, 23), "Press any Key to Continue",
Graphics::kTextAlignCenter);
}
bool Castle::msgKeypress(const KeypressMessage &msg) {
auto &player = g_engine->_player;
switch (_mode) {
case NAMING:
if (msg.keycode == Common::KEYCODE_BACKSPACE && !_playerName.empty()) {
_playerName.deleteLastChar();
} else if (msg.keycode == Common::KEYCODE_RETURN && !_playerName.empty()) {
Common::strcpy_s(player._name, _playerName.c_str());
_mode = GRAND_ADVENTURE;
} else if (Common::isAlpha(msg.ascii) && _playerName.size() < MAX_NAME) {
_playerName += msg.ascii;
}
redraw();
break;
case GRAND_ADVENTURE:
if (msg.keycode == Common::KEYCODE_y) {
nextTask();
_mode = FIRST_TASK;
} else if (msg.keycode == Common::KEYCODE_n) {
_mode = BEGONE;
}
redraw();
break;
default:
// All other modes exit the castle
replaceView("WorldMap");
break;
}
return true;
}
bool Castle::msgAction(const ActionMessage &msg) {
if (_mode >= FIRST_TASK) {
replaceView("WorldMap");
}
return true;
}
void Castle::nextTask() {
auto &player = g_engine->_player;
player._task = CLIP(player._attr[AT_WISDOM] / 3, 1, 10);
player._taskCompleted = false;
}
Common::String Castle::getTaskName(int taskNum) const {
Common::String mons = MONSTER_INFO[taskNum]._name;
return Common::String::format("%s %s",
strchr("aeiou", tolower(mons.firstChar())) != nullptr ? "an" : "a",
mons.c_str());
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_CASTLE_H
#define ULTIMA0_VIEWS_CASTLE_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Castle : public View {
private:
enum Mode {
NAMING, GRAND_ADVENTURE, BEGONE, FIRST_TASK, TASK_COMPLETE, TASK_INCOMPLETE
};
Mode _mode = NAMING;
Common::String _playerName;
void firstTime();
void taskCompleted();
void taskIncomplete();
void nextTask();
void pressAnyKey();
Common::String getTaskName(int taskNum) const;
public:
Castle() : View("Castle") {}
~Castle() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override;
bool msgAction(const ActionMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,136 @@
/* 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/create_character.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
bool CreateCharacter::msgFocus(const FocusMessage &msg) {
g_engine->_player.init();
_mode = LUCKY_NUMBER;
_input = "";
return true;
}
void CreateCharacter::draw() {
auto s = getSurface();
const auto &player = g_engine->_player;
s.clear();
// Lucky number
s.writeString(Common::Point(1, 4), "Type thy Lucky Number.....");
if (_mode == LUCKY_NUMBER)
s.writeString(_input);
else
s.writeString(Common::String::format("%d", player._luckyNumber));
// Level of play
if (_mode >= LEVEL) {
s.writeString(Common::Point(1, 6), "Level of play (1-10)......");
if (_mode == LEVEL)
s.writeString(_input);
else
s.writeString(Common::String::format("%d", player._skill));
}
// Stats
if (_mode >= STATS) {
for (int i = 0; i < MAX_ATTR; ++i) {
Common::String line = ATTRIB_NAMES[i];
while (line.size() < 15)
line += ".";
line += Common::String::format("%d", player._attr[i]);
s.writeString(Common::Point(1, 9 + i), line);
}
s.writeString(Common::Point(0, 16), "Shallt thou play with these qualities?");
}
// Class selection
if (_mode == CLASS) {
s.writeString(Common::Point(0, 18), "And shalt thou be a Fighter or a Mage?");
}
}
bool CreateCharacter::msgKeypress(const KeypressMessage &msg) {
auto &player = g_engine->_player;
if (_mode == LUCKY_NUMBER || _mode == LEVEL) {
if (Common::isDigit(msg.ascii) && _input.size() < 6) {
_input += msg.ascii;
redraw();
} else if (msg.keycode == Common::KEYCODE_BACKSPACE && _input.size() > 0) {
_input.deleteLastChar();
redraw();
} else if (msg.keycode == Common::KEYCODE_RETURN && !_input.empty()) {
if (_mode == LUCKY_NUMBER) {
player._luckyNumber = atoi(_input.c_str());
_input.clear();
_mode = LEVEL;
redraw();
} else {
player._skill = atoi(_input.c_str());
if (player._skill >= 1 && player._skill <= 10) {
_input.clear();
_mode = STATS;
player.rollAttributes();
redraw();
}
}
}
} else if (msg.keycode == Common::KEYCODE_y) {
_mode = CLASS;
redraw();
} else if (msg.keycode == Common::KEYCODE_n) {
player.rollAttributes();
redraw();
} else if (_mode == CLASS && (msg.keycode == Common::KEYCODE_f ||
msg.keycode == Common::KEYCODE_m)) {
player._class = toupper(msg.ascii);
characterDone();
}
return true;
}
bool CreateCharacter::msgAction(const ActionMessage &msg) {
if (msg._action == KEYBIND_ESCAPE) {
replaceView("Title");
return true;
}
return true;
}
void CreateCharacter::characterDone() {
// Generate the world map
g_engine->_worldMap.init(g_engine->_player);
// Enter the town
replaceView("Town");
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,53 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_CREATE_CHARACTER_H
#define ULTIMA0_VIEWS_CREATE_CHARACTER_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class CreateCharacter : public View {
private:
enum Mode { LUCKY_NUMBER, LEVEL, STATS, CLASS };
Mode _mode = LUCKY_NUMBER;
Common::String _input;
void characterDone();
public:
CreateCharacter() : View("CreateCharacter") {}
~CreateCharacter() override {}
bool msgFocus(const FocusMessage &msg) override;
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override;
bool msgAction(const ActionMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,51 @@
/* 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/dead.h"
#include "ultima/ultima0/gfx/font.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
Dead::Dead() : View("Dead") {
setBounds(Common::Rect(0, DEFAULT_SCY - 4 * Gfx::GLYPH_HEIGHT, DEFAULT_SCX, DEFAULT_SCY));
}
void Dead::draw() {
const auto &player = g_engine->_player;
auto s = getSurface();
s.clear();
const char *name = player._name;
if (*name == '\0')
name = "the peasant";
s.writeString(Common::Point(20, 0), "We mourn the passing of", Graphics::kTextAlignCenter);
s.writeString(Common::Point(20, 1), Common::String::format("%s and his Computer", name), Graphics::kTextAlignCenter);
s.writeString(Common::Point(20, 2), "To invoke a miracle of resurrection", Graphics::kTextAlignCenter);
s.writeString(Common::Point(20, 3), "Press a Key", Graphics::kTextAlignCenter);
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_DEAD_H
#define ULTIMA0_VIEWS_DEAD_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Dead : public View {
private:
void showTitle() {
replaceView("Title");
}
public:
Dead();
~Dead() override {}
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override {
showTitle();
return true;
}
bool msgMouseDown(const MouseDownMessage &msg) override {
showTitle();
return true;
}
bool msgAction(const ActionMessage &msg) override {
showTitle();
return true;
}
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,300 @@
/* 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

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_DUNGEON_H
#define ULTIMA0_VIEWS_DUNGEON_H
#include "ultima/ultima0/views/view.h"
#include "ultima/ultima0/views/status.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Dungeon : public View {
private:
DungeonStatus _status = DungeonStatus(this);
void drawMinimap(Graphics::ManagedSurface &mapArea);
void moveForward();
void interact();
void endOfTurn();
void showMessage(const Common::String &msg) {
_status.send(GameMessage("MSG", msg));
}
void showLines(const Common::String &msg) {
_status.send(GameMessage("LINES", msg));
}
public:
Dungeon();
~Dungeon() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgAction(const ActionMessage &msg) override;
bool msgKeypress(const KeypressMessage &msg) override;
bool msgGame(const GameMessage &msg) override;
void timeout() override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,146 @@
/* 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/info.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
Info::Info(const char *viewName) : View(viewName) {
}
bool Info::msgFocus(const FocusMessage &msg) {
MetaEngine::setKeybindingMode(KBMODE_MENUS);
return true;
}
bool Info::msgUnfocus(const UnfocusMessage &msg) {
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
return true;
}
void Info::draw() {
const auto &player = g_engine->_player;
auto s = getSurface();
int i;
s.clear();
View::draw();
// Stats
for (i = 0; i < MAX_ATTR; ++i) {
Common::String line = ATTRIB_NAMES[i];
while (line.size() < 15)
line += '.';
s.writeString(Common::Point(0, 4 + i), line);
}
// Price/Damage/Item
for (i = 0; i < MAX_OBJ; ++i) {
s.writeString(Common::Point(5, 18 + i),
Common::String::format("%d", OBJECT_INFO[i]._cost));
if (i == 0) {
s.writeString(" For 10");
s.writeString(Common::Point(15, 18 + i), "N/A");
} else if (i == 5) {
s.writeString(Common::Point(15, 18 + i), "?????");
} else {
s.writeString(Common::Point(15, 18 + i),
Common::String::format("1-%d", OBJECT_INFO[i]._maxDamage));
}
s.writeString(Common::Point(25, 18 + i), OBJECT_INFO[i]._name);
}
// Headers
s.setColor(255, 0, 128);
s.writeString(Common::Point(6, 2), "Stat's");
s.writeString(Common::Point(21, 2), "Weapons");
s.writeString(Common::Point(5, 16), "Price Damage Item");
// Amounts
s.setColor(C_VIOLET);
for (i = 0; i < MAX_ATTR; ++i)
s.writeString(Common::Point(15, 4 + i),
Common::String::format("%d", player._attr[i]));
for (i = 0; i < MAX_OBJ; ++i)
s.writeString(Common::Point(21, 4 + i),
Common::String::format("%4d-", (int)player._object[i]));
s.writeString(Common::Point(18, 10), "Q-Quit");
}
void Info::leave() {
const auto &player = g_engine->_player;
replaceView(player._level == 0 ? "WorldMap" : "DungeonMap");
}
bool Info::msgAction(const ActionMessage &msg) {
if (isDelayActive())
return false;
if (msg._action == KEYBIND_ESCAPE || msg._action == KEYBIND_QUIT) {
leave();
return true;
}
for (int i = 0; i < MAX_OBJ; ++i) {
if (msg._action == OBJECT_INFO[i]._action) {
selectObject(i);
return true;
}
}
return false;
}
bool Info::msgGame(const GameMessage &msg) {
if (msg._name == "SELECTION") {
selectObject(msg._value);
return true;
}
return false;
}
/*-------------------------------------------------------------------*/
Info::InfoObject::InfoObject(Info *parent, const Common::Point &pt, int id,
const Common::String &text) :
UIElement("InfoObject", parent), _id(id), _text(text) {
setBounds(Gfx::TextRect(pt.x, pt.y, pt.x + text.size(), pt.y));
}
void Info::InfoObject::draw() {
auto s = getSurface();
s.writeString(_text);
}
bool Info::InfoObject::msgMouseDown(const MouseDownMessage &msg) {
_parent->send(GameMessage("SELECTION", _id));
return true;
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_INFO_H
#define ULTIMA0_VIEWS_INFO_H
#include "ultima/ultima0/views/view.h"
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Info : public View {
class InfoObject : public UIElement {
public:
Common::String _text;
int _id;
public:
InfoObject(Info *parent, const Common::Point &pt, int id, const Common::String &text);
void draw() override;
bool msgMouseDown(const MouseDownMessage &msg) override;
};
private:
InfoObject _options[6] = {
InfoObject(this, Common::Point(26, 4), 0, OBJECT_INFO[0]._name),
InfoObject(this, Common::Point(26, 5), 1, OBJECT_INFO[1]._name),
InfoObject(this, Common::Point(26, 6), 2, OBJECT_INFO[2]._name),
InfoObject(this, Common::Point(26, 7), 3, OBJECT_INFO[3]._name),
InfoObject(this, Common::Point(26, 8), 4, OBJECT_INFO[4]._name),
InfoObject(this, Common::Point(26, 9), 5, OBJECT_INFO[5]._name)
};
protected:
virtual void selectObject(int item) {
}
virtual void leave();
public:
Info(const char *viewName = "Info");
~Info() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgAction(const ActionMessage &msg) override;
bool msgGame(const GameMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,124 @@
/* 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/intro.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
bool Intro::msgFocus(const FocusMessage &msg) {
_page = 0;
return true;
}
void Intro::draw() {
auto s = getSurface();
s.clear();
switch (_page) {
case 0:
s.writeString(Common::Point(5, 1), "Many, many, many years ago the");
s.writeString(Common::Point(0, 3), "Dark Lord Mondain, Archfoe of British,");
s.writeString(Common::Point(0, 5), "traversed the lands of Akalabeth,");
s.writeString(Common::Point(0, 7), "spreading evil and death as he passed.");
s.writeString(Common::Point(0, 9), "By the time Mondain was driven from the");
s.writeString(Common::Point(0, 11), "land by British, bearer of the White");
s.writeString(Common::Point(0, 13), "Light, he had done much damage unto");
s.writeString(Common::Point(0, 15), "the lands.");
s.writeString(Common::Point(0, 17), "`Tis thy duty to help rid Akalabeth of");
s.writeString(Common::Point(0, 19), "the foul beasts which infest it,");
s.writeString(Common::Point(0, 21), "while trying to stay alive!!!");
break;
case 1:
s.writeString(Common::Point(0, 0), "The Player's Stats:");
s.writeString(Common::Point(0, 2), "Hit Points- Amount of Damage a Player");
s.writeString(Common::Point(0, 4), " can absorb before Death");
s.writeString(Common::Point(0, 6), "Strength--- Related to Damage Inflicted");
s.writeString(Common::Point(0, 8), " by Player against Monsters.");
s.writeString(Common::Point(0, 10), "Dexterity-- Related to the Probability");
s.writeString(Common::Point(0, 12), " of a Player hitting a Monst.");
s.writeString(Common::Point(0, 14), "Stamina---- Related to Player Defense");
s.writeString(Common::Point(0, 16), " against Monsters");
s.writeString(Common::Point(0, 18), "Wisdom----- This Attribute is used");
s.writeString(Common::Point(0, 20), " in Special (Quest!) Routines");
s.writeString(Common::Point(0, 22), "Gold------- Money!! Cash!! Assets!!");
break;
case 2:
s.writeString(Common::Point(0, 0), "The Towns and Buying Items:");
s.writeString(Common::Point(0, 2), " To buy any items one need only");
s.writeString(Common::Point(0, 4), "type the first letter of the item");
s.writeString(Common::Point(0, 6), "wanted. The cost of the respective");
s.writeString(Common::Point(0, 8), "items is displayed while in the town.");
s.writeString(Common::Point(0, 10), "The Game is started in a town somewhere");
s.writeString(Common::Point(0, 12), "on the 20x20 map");
s.writeString(Common::Point(20, 14), "Fighters and Magi", Graphics::kTextAlignCenter);
s.writeString(Common::Point(0, 16), " The disadvantage of being a");
s.writeString(Common::Point(0, 18), "fighter is the lack of the ability to");
s.writeString(Common::Point(0, 20), "control the magic amulet, whereas magi");
s.writeString(Common::Point(0, 22), "can not use rapier and bows.");
break;
case 3:
s.writeString(Common::Point(1, 0), "Movement:");
s.writeString(Common::Point(1, 2), "-Key- Outdoors Dungeon");
s.writeString(Common::Point(1, 4), " UP Move North Move Forward");
s.writeString(Common::Point(1, 6), " LEFT Move West Turn Left");
s.writeString(Common::Point(1, 8), "RIGHT Move East Turn Right");
s.writeString(Common::Point(1, 10), " DOWN Move South Turn Around");
s.writeString(Common::Point(1, 12), " Z Statistics Statistics");
s.writeString(Common::Point(1, 14), " A N/A Attack");
s.writeString(Common::Point(1, 16), " P Pause Pause");
s.writeString(Common::Point(1, 18), " E Go Into Town Climb Ladder");
s.writeString(Common::Point(1, 20), " E Go Castle Go Hole");
s.writeString(Common::Point(1, 22), "SPACE Pass Pass");
break;
case 4:
s.writeString(Common::Point(0, 2), " Thou doest know the basics of");
s.writeString(Common::Point(0, 4), "the game, experiment with the commands.");
s.writeString(Common::Point(0, 6), "There is much left unsaid for");
s.writeString(Common::Point(0, 8), "thee to discover in the future...");
s.writeString(Common::Point(0, 10), "Go now unto the world and seek");
s.writeString(Common::Point(0, 12), "adventure where thou might!!!");
s.writeString(Common::Point(0, 14), "P.S.-Search out the Castle of");
s.writeString(Common::Point(0, 16), "Lord British, Use the -E- Key to go in!");
default:
break;
}
s.writeString(Common::Point(20, (_page == 4) ? 18 : 24), "Press any Key to Continue",
Graphics::kTextAlignCenter);
}
void Intro::nextPage() {
if (++_page < 5)
redraw();
else
replaceView("Title");
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,62 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_INTRO_H
#define ULTIMA0_VIEWS_INTRO_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Intro : public View {
private:
int _page = 0;
void nextPage();
public:
Intro() : View("Intro") {}
~Intro() override {}
bool msgFocus(const FocusMessage &msg) override;
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override {
nextPage();
return true;
}
bool msgMouseDown(const MouseDownMessage &msg) override {
nextPage();
return true;
}
bool msgAction(const ActionMessage &msg) override {
nextPage();
return true;
}
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,37 @@
/* 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/startup.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
void Startup::draw() {
auto s = getSurface();
s.clear();
s.writeString(Common::Point(5, 10), "Ultima 0 - Akalabeth!");
s.writeString(Common::Point(2, 19), "Ready?");
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_STARTUP_H
#define ULTIMA0_VIEWS_STARTUP_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Startup : public View {
private:
void showTitle() {
replaceView("Title");
}
public:
Startup() : View("Startup") {}
~Startup() override {}
void draw() override;
bool msgKeypress(const KeypressMessage &msg) override {
showTitle();
return true;
}
bool msgMouseDown(const MouseDownMessage &msg) override {
showTitle();
return true;
}
bool msgAction(const ActionMessage &msg) override {
showTitle();
return true;
}
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,114 @@
/* 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/status.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
Status::Status(const Common::String &name, UIElement *parent) : View(name, parent) {
setBounds(Common::Rect(0, DEFAULT_SCY - 4 * Gfx::GLYPH_HEIGHT, DEFAULT_SCX, DEFAULT_SCY));
}
bool Status::msgFocus(const FocusMessage &msg) {
_message.clear();
return View::msgFocus(msg);
}
void Status::draw() {
const auto &player = g_engine->_player;
auto s = getSurface();
s.clear();
if (!_message.empty())
s.writeString(Common::Point(1, 0), _message);
s.writeString(Common::Point(28, 1), "Food=");
s.writeString(Common::Point(28, 2), "H.P.=");
s.writeString(Common::Point(28, 3), "Gold=");
s.setColor(C_VIOLET);
s.writeString(Common::Point(33, 1), Common::String::format("%d", (int)player._object[OB_FOOD]));
s.writeString(Common::Point(33, 2), Common::String::format("%d", player._attr[AT_HP]));
s.writeString(Common::Point(33, 3), Common::String::format("%d", player._attr[AT_GOLD]));
}
bool Status::msgGame(const GameMessage &msg) {
if (msg._name == "MSG") {
_message = msg._stringValue;
redraw();
return true;
}
return false;
}
/*-------------------------------------------------------------------*/
bool DungeonStatus::msgFocus(const FocusMessage &msg) {
_lines.clear();
return Status::msgFocus(msg);
}
void DungeonStatus::draw() {
Status::draw();
const auto &player = g_engine->_player;
auto s = getSurface();
// Display the current direction
if (player._level > 0)
s.writeString(Common::Point(15, 0), DIRECTION_NAMES[player.dungeonDir()]);
// Draw any extra lines
for (uint i = 0; i < _lines.size(); ++i)
s.writeString(Common::Point(1, 1 + i), _lines[i]);
}
bool DungeonStatus::msgGame(const GameMessage &msg) {
if (msg._name == "LINES") {
_lines.clear();
Common::String str = msg._stringValue;
uint p;
while ((p = str.findFirstOf('\n')) != Common::String::npos) {
_lines.push_back(Common::String(str.c_str(), str.c_str() + p));
str = Common::String(str.c_str() + p + 1);
}
if (!str.empty())
_lines.push_back(str);
redraw();
return true;
} else {
if (msg._name == "MSG")
// Changing message also resets any message lines
_lines.clear();
return Status::msgGame(msg);
}
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,68 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_STATUS_H
#define ULTIMA0_VIEWS_STATUS_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Status : public View {
private:
Common::String _message;
Common::String _direction;
public:
Status(const Common::String &name, UIElement *parent);
~Status() override {}
bool msgFocus(const FocusMessage &msg) override;
void draw() override;
bool msgGame(const GameMessage &msg) override;
};
class OverworldStatus : public Status {
public:
OverworldStatus(UIElement *parent) : Status("OverworldStatus", parent) {
}
};
class DungeonStatus : public Status {
private:
Common::StringArray _lines;
public:
DungeonStatus(UIElement *parent) : Status("DungeonStatus", parent) {
}
bool msgFocus(const FocusMessage &msg) override;
void draw() override;
bool msgGame(const GameMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,144 @@
/* 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/title.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
Title::Title() : View("Title") {
}
bool Title::msgFocus(const FocusMessage &msg) {
_highlightedOption = 0;
updateSelections();
MetaEngine::setKeybindingMode(KBMODE_MENUS);
Common::String priorView = msg._priorView->getName();
if (priorView == "Startup" || priorView == "Dead") {
g_engine->playMidi("intro.mid");
}
return true;
}
bool Title::msgUnfocus(const UnfocusMessage &msg) {
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
return true;
}
void Title::updateSelections() {
const int selected = getColor(255, 0, 128);
const int white = getColor(255, 255, 255);
for (int i = 0; i < 4; ++i) {
auto &opt = _options[i];
opt._color = opt._index == _highlightedOption ? selected : white;
opt.redraw();
}
}
void Title::draw() {
auto s = getSurface();
s.clear();
View::draw();
s.writeString(Common::Point(20, 8), "Ultima 0 - Akalabeth!", Graphics::kTextAlignCenter);
}
bool Title::msgAction(const ActionMessage &msg) {
switch (msg._action) {
case KEYBIND_UP:
_highlightedOption = _highlightedOption ? _highlightedOption - 1 : 3;
updateSelections();
break;
case KEYBIND_DOWN:
_highlightedOption = (_highlightedOption + 1) % 4;
updateSelections();
break;
case KEYBIND_SELECT:
selectOption();
break;
default:
break;
}
return true;
}
bool Title::msgGame(const GameMessage &msg) {
if (msg._name == "SELECTION") {
_highlightedOption = msg._value;
updateSelections();
return true;
}
return false;
}
bool Title::msgMouseDown(const MouseDownMessage &msg) {
selectOption();
return true;
}
void Title::selectOption() {
if (_highlightedOption == 1 || _highlightedOption == 3)
g_engine->stopMidi();
if (_highlightedOption == 3) {
if (g_engine->savegamesExist()) {
g_engine->loadGameDialog();
} else {
// Otherwise to go the Create Character view
replaceView("CreateCharacter");
}
} else {
const char *VIEW_NAMES[4] = { "Intro", "CreateCharacter", "Acknowledgements" };
replaceView(VIEW_NAMES[_highlightedOption]);
}
}
/*-------------------------------------------------------------------*/
Title::TitleOption::TitleOption(Title *parent, int index, const Common::String &text, int row) :
UIElement("TitleOption", parent), _index(index), _text(text) {
int xs = 20 - text.size() / 2;
setBounds(Gfx::TextRect(xs, row, xs + text.size(), row));
}
void Title::TitleOption::draw() {
auto s = getSurface();
s.setColor(_color);
s.writeString(_text);
}
bool Title::TitleOption::msgMouseEnter(const MouseEnterMessage &msg) {
_parent->send(GameMessage("SELECTION", _index));
return true;
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,71 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_TITLE_H
#define ULTIMA0_VIEWS_TITLE_H
#include "ultima/ultima0/views/view.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Title : public View {
class TitleOption : public UIElement {
public:
int _index;
Common::String _text;
byte _color = 0;
public:
TitleOption(Title *parent, int index, const Common::String &text, int row);
void draw() override;
bool msgMouseEnter(const MouseEnterMessage &msg) override;
};
private:
TitleOption _options[4] = {
TitleOption(this, 0, "Introduction", 16),
TitleOption(this, 1, "Create a Character", 17),
TitleOption(this, 2, "Acknowledgements", 18),
TitleOption(this, 3, "Journey Onwards", 19)
};
int _highlightedOption = 0;
void updateSelections();
void selectOption();
public:
Title();
~Title() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgAction(const ActionMessage &msg) override;
bool msgGame(const GameMessage &msg) override;
bool msgMouseDown(const MouseDownMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,97 @@
/* 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/town.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
static const char *WELCOME = "Welcome to the Adventure Shop";
static const char *THANK_YOU = "Thank you m'lord";
//static const char *DONT_HAVE_THAT = "I'm Sorry We Don't have that.";
static const char *NOT_ENOUGH = "M'Lord thou can not afford that item.";
static const char *MAGES_CANT_USE = "I'm sorry, Mages can't use that.";
static const char *BYE = "Bye";
Town::Town() : Info("Town") {
}
bool Town::msgFocus(const FocusMessage &msg) {
_message = WELCOME;
g_engine->playMidi("shop.mid");
return Info::msgFocus(msg);
}
bool Town::msgUnfocus(const UnfocusMessage &msg) {
g_engine->stopMidi();
return Info::msgUnfocus(msg);
}
void Town::draw() {
Info::draw();
// General message
auto s = getSurface();
s.writeString(Common::Point(1, 12), _message.empty() ? THANK_YOU : _message);
s.writeString(Common::Point(1, 13), "Which item shallt thou buy");
_message.clear();
}
void Town::selectObject(int item) {
auto &player = g_engine->_player;
const auto &obj = OBJECT_INFO[item];
// Some things mages can't use
if (player._class == 'M') {
if (item == OB_BOW || item == OB_RAPIER) {
_message = MAGES_CANT_USE;
redraw();
return;
}
}
if (obj._cost > player._attr[AT_GOLD]) {
_message = NOT_ENOUGH;
} else {
player._attr[AT_GOLD] -= obj._cost; // Lose the money
player._object[item] = MIN<int>(player._object[item] + (item == OB_FOOD ? 10 : 1), 999);
_message = THANK_YOU;
}
redraw();
}
void Town::leave() {
_message = BYE;
delaySeconds(1);
redraw();
}
void Town::timeout() {
replaceView("WorldMap");
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,54 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_TOWN_H
#define ULTIMA0_VIEWS_TOWN_H
#include "ultima/ultima0/views/info.h"
#include "ultima/ultima0/data/data.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class Town : public Info {
private:
Common::String _message;
protected:
void selectObject(int item) override;
void leave() override;
public:
Town();
~Town() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
void timeout() override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,90 @@
/* 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/view.h"
#include "ultima/ultima0/ultima0.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
void View::checkFocusedControl(const Common::Point &mousePos) {
if (_focusedElement) {
if (!_focusedElement->getBounds().contains(mousePos)) {
_focusedElement->send(MouseLeaveMessage());
_focusedElement = nullptr;
}
} else {
for (UIElement *child : _children) {
if (child->getBounds().contains(mousePos)) {
_focusedElement = child;
child->send(MouseEnterMessage());
break;
}
}
}
}
UIElement *View::getElementAtPos(const Common::Point &pos) const {
for (UIElement *child : _children) {
if (child->getBounds().contains(pos))
return child;
}
return nullptr;
}
bool View::msgFocus(const FocusMessage &msg) {
_focusedElement = nullptr;
return UIElement::msgFocus(msg);
}
bool View::msgUnfocus(const UnfocusMessage &msg) {
if (_focusedElement)
_focusedElement->send(MouseLeaveMessage());
return UIElement::msgUnfocus(msg);
}
bool View::msgMouseMove(const MouseMoveMessage &msg) {
checkFocusedControl(msg._pos);
return true;
}
bool View::msgMouseDown(const MouseDownMessage &msg) {
UIElement *child = getElementAtPos(msg._pos);
return child ? child->send(msg) : false;
}
bool View::msgMouseUp(const MouseUpMessage &msg) {
UIElement *child = getElementAtPos(msg._pos);
return child ? child->send(msg) : false;
}
byte View::getColor(byte r, byte g, byte b) {
return g_engine->_palette.findBestColor(r, g, b);
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,80 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_VIEW_H
#define ULTIMA0_VIEWS_VIEW_H
#include "ultima/ultima0/events.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
/**
* Base view class for screens and dialogs that appear on-screen.
* The View class takes care of two important things:
* 1) By default events get sent to all controls on a view until one
* handles it. For mouse events, we instead want only the control the
* mouse cursor is over to receive the events, saving the individual
* controls from having to check if the mouse is within their bounds.
* 2) Individual elements will get a Focus/Unfocus message as the
* mouse enters and leaves them. This allows, for example, buttons
* that have been pressed to de-select if the mouse leaves their bounds.
*/
class View : public UIElement {
private:
UIElement *_focusedElement = nullptr;
/**
* Checks if a control is entered or left
*/
void checkFocusedControl(const Common::Point &mousePos);
/**
* Check for an element at the given position
*/
UIElement *getElementAtPos(const Common::Point &pos) const;
protected:
byte getColor(byte r, byte g, byte b);
public:
View(const Common::String &name, UIElement *uiParent) :
UIElement(name, uiParent) {
}
View(const Common::String &name) :
UIElement(name) {
}
virtual ~View() {
}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
bool msgMouseMove(const MouseMoveMessage &msg) override;
bool msgMouseDown(const MouseDownMessage &msg) override;
bool msgMouseUp(const MouseUpMessage &msg) override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_H
#define ULTIMA0_VIEWS_H
#include "ultima/ultima0/views/acknowledgements.h"
#include "ultima/ultima0/views/attack.h"
#include "ultima/ultima0/views/castle.h"
#include "ultima/ultima0/views/create_character.h"
#include "ultima/ultima0/views/dead.h"
#include "ultima/ultima0/views/dungeon.h"
#include "ultima/ultima0/views/info.h"
#include "ultima/ultima0/views/intro.h"
#include "ultima/ultima0/views/startup.h"
#include "ultima/ultima0/views/title.h"
#include "ultima/ultima0/views/town.h"
#include "ultima/ultima0/views/world_map.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
struct Views {
Acknowledgements _acknowledgements;
Attack _attack;
Castle _castle;
CreateCharacter _createCharacter;
Dead _dead;
Dungeon _dungeon;
Info _info;
Intro _intro;
Startup _startup;
Title _title;
Town _town;
WorldMap _worldMap;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif

View File

@@ -0,0 +1,223 @@
/* 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/world_map.h"
#include "ultima/ultima0/ultima0.h"
#include "ultima/ultima0/metaengine.h"
#include "ultima/ultima0/gfx/font.h"
#include "ultima/ultima0/gfx/map.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
WorldMap::WorldMap() : View("WorldMap") {
}
bool WorldMap::msgFocus(const FocusMessage &msg) {
showMessage("");
MetaEngine::setKeybindingMode(KBMODE_OVERWORLD);
if (!g_engine->isMidiPlaying())
g_engine->playMidi("over.mid");
return true;
}
bool WorldMap::msgUnfocus(const UnfocusMessage &msg) {
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
return true;
}
void WorldMap::draw() {
auto s = getSurface();
// Draw the map
Graphics::ManagedSurface mapArea(s, Common::Rect(0, 0, s.w, s.h - Gfx::GLYPH_HEIGHT * 4));
Gfx::Map::draw(&mapArea);
// Allow the status area to draw
View::draw();
if (g_engine->_showMinimap) {
s.frameRect(Common::Rect(s.w - WORLD_MINIMAP_SIZE - 4, 0, s.w, WORLD_MINIMAP_SIZE + 4), C_GREY);
s.frameRect(Common::Rect(s.w - WORLD_MINIMAP_SIZE - 3, 1, s.w - 1, WORLD_MINIMAP_SIZE + 3), C_GREY);
Graphics::ManagedSurface minimapArea(s, Common::Rect(s.w - WORLD_MINIMAP_SIZE - 2, 2, s.w - 2, WORLD_MINIMAP_SIZE + 2));
Gfx::Map::draw(&minimapArea, true);
}
}
bool WorldMap::msgAction(const ActionMessage &msg) {
if (isDelayActive())
return false;
switch (msg._action) {
case KEYBIND_UP:
showMessage("North");
move(0, -1);
break;
case KEYBIND_DOWN:
showMessage("South");
move(0, 1);
break;
case KEYBIND_LEFT:
showMessage("West");
move(-1, 0);
break;
case KEYBIND_RIGHT:
showMessage("East");
move(1, 0);
break;
case KEYBIND_INFO:
// Show character info screen
showMessage("");
replaceView("Info");
break;
case KEYBIND_ENTER:
enter();
break;
case KEYBIND_PASS:
showMessage("");
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("");
break;
}
endOfTurn();
return true;
}
bool WorldMap::msgKeypress(const KeypressMessage &msg) {
if (isDelayActive())
return false;
endOfTurn();
return true;
}
void WorldMap::endOfTurn() {
auto &player = g_engine->_player;
if (player._attr[AT_HP] <= 0) {
g_engine->stopMidi();
replaceView("Dead");
} else {
player._object[OB_FOOD] = MAX(player._object[OB_FOOD] - 1.0, 0.0);
if (player._object[OB_FOOD] == 0) {
showMessage("You have starved...");
delaySeconds(1);
}
}
}
void WorldMap::timeout() {
const auto &map = g_engine->_worldMap;
auto &player = g_engine->_player;
auto &dungeon = g_engine->_dungeon;
g_engine->stopMidi();
if (player._attr[AT_HP] <= 0 || player._object[OB_FOOD] <= 0) {
// Timeout from displaying player was killed
replaceView("Dead");
} else {
// Otherwise a timeout from entering a location
int t = map.read(player._worldPos.x, player._worldPos.y);
switch (t) {
case WT_TOWN:
replaceView("Town");
break;
case WT_DUNGEON:
player._level = 1; // Go to level 1
player._dungeonPos.x = 1; // Set initial position
player._dungeonPos.y = 1;
player._dungeonDir.x = 1; // And direction
player._dungeonDir.y = 0;
dungeon.create(player);
replaceView("Dungeon");
break;
case WT_BRITISH:
replaceView("Castle");
break;
default:
break;
}
}
}
void WorldMap::move(int xi, int yi) {
auto &player = g_engine->_player;
auto &map = g_engine->_worldMap;
// Calculate new position
int x1 = player._worldPos.x + xi;
int y1 = player._worldPos.y + yi;
if (map.read(x1, y1) == WT_MOUNTAIN) {
showMessage("You can't pass the mountains.");
} else {
// Move
player._worldPos.x = x1;
player._worldPos.y = y1;
redraw();
}
}
void WorldMap::enter() {
const auto &player = g_engine->_player;
const auto &map = g_engine->_worldMap;
int t = map.read(player._worldPos.x, player._worldPos.y);
switch (t) {
case WT_TOWN:
showMessage("Enter Town.");
delaySeconds(1);
break;
case WT_DUNGEON:
showMessage("Enter Dungeon.");
delaySeconds(1);
break;
case WT_BRITISH:
showMessage("Enter Castle.");
delaySeconds(1);
break;
default:
// Nope....
showMessage("Huh???");
break;
}
}
} // namespace Views
} // namespace Ultima0
} // namespace Ultima

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
#ifndef ULTIMA0_VIEWS_WORLD_MAP_H
#define ULTIMA0_VIEWS_WORLD_MAP_H
#include "ultima/ultima0/views/view.h"
#include "ultima/ultima0/views/status.h"
namespace Ultima {
namespace Ultima0 {
namespace Views {
class WorldMap : public View {
private:
OverworldStatus _status = OverworldStatus(this);
void move(int xi, int yi);
void enter();
void endOfTurn();
void showMessage(const Common::String &msg) {
_status.send(GameMessage("MSG", msg));
}
public:
WorldMap();
~WorldMap() override {}
bool msgFocus(const FocusMessage &msg) override;
bool msgUnfocus(const UnfocusMessage &msg) override;
void draw() override;
bool msgAction(const ActionMessage &msg) override;
bool msgKeypress(const KeypressMessage &msg) override;
void timeout() override;
};
} // namespace Views
} // namespace Ultima0
} // namespace Ultima
#endif