Initial commit
This commit is contained in:
389
engines/ultima/ultima0/data/data.cpp
Normal file
389
engines/ultima/ultima0/data/data.cpp
Normal 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
|
||||
146
engines/ultima/ultima0/data/data.h
Normal file
146
engines/ultima/ultima0/data/data.h
Normal 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
|
||||
133
engines/ultima/ultima0/data/defines.h
Normal file
133
engines/ultima/ultima0/data/defines.h
Normal 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
|
||||
203
engines/ultima/ultima0/data/monster_logic.cpp
Normal file
203
engines/ultima/ultima0/data/monster_logic.cpp
Normal 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
|
||||
67
engines/ultima/ultima0/data/monster_logic.h
Normal file
67
engines/ultima/ultima0/data/monster_logic.h
Normal 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
|
||||
Reference in New Issue
Block a user