520 lines
12 KiB
C++
520 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2002,2003,2004 Daniel Heck
|
|
*
|
|
* 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 2
|
|
* 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, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "errors.hh"
|
|
#include "enigma.hh"
|
|
#include "ecl.hh"
|
|
#include "main.hh"
|
|
|
|
#include <iostream>
|
|
#include <ctime>
|
|
#include <set>
|
|
|
|
using namespace std;
|
|
using namespace ecl;
|
|
using namespace enigma;
|
|
|
|
|
|
/* -------------------- Game Type -------------------- */
|
|
|
|
static const char *versionName[GAMET_COUNT+1] = {
|
|
"enigma", // same indices as enum GameType
|
|
"oxyd1",
|
|
"per.oxyd",
|
|
"oxyd.extra",
|
|
"oxyd.magnum",
|
|
0
|
|
};
|
|
|
|
GameType enigma::GetGameType(std::string name) {
|
|
GameType type = GAMET_UNKNOWN;
|
|
for (int v = 0; v<GAMET_COUNT; ++v) {
|
|
if (0 == strcmp(name.c_str(), versionName[v])) {
|
|
type = GameType(v);
|
|
break;
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
std::string enigma::GetGameTypeName(GameType type) {
|
|
if (type >= GAMET_FIRST && type <= GAMET_LAST)
|
|
return versionName[type];
|
|
else
|
|
return "unknown";
|
|
}
|
|
|
|
/* -------------------- Direction -------------------- */
|
|
|
|
Direction enigma::reverse(Direction d) {
|
|
static Direction rdir[] = { NODIR, EAST, NORTH, WEST, SOUTH };
|
|
return rdir[d+1];
|
|
}
|
|
|
|
Direction enigma::rotate_cw (Direction d)
|
|
{
|
|
static Direction rdir[] = { NODIR, NORTH, WEST, SOUTH, EAST };
|
|
return rdir[d+1];
|
|
}
|
|
|
|
Direction enigma::rotate_ccw (Direction d)
|
|
{
|
|
static Direction rdir[] = { NODIR, SOUTH, EAST, NORTH, WEST };
|
|
return rdir[d+1];
|
|
}
|
|
|
|
Direction
|
|
direction_fromto(GridPos source, GridPos target)
|
|
{
|
|
// source and target have to be adjacent
|
|
|
|
int dx = target.x-source.x;
|
|
int dy = target.y-source.y;
|
|
Direction d = NODIR;
|
|
|
|
if (dx == 0) {
|
|
if (dy == -1) d = NORTH;
|
|
else if (dy == 1) d = SOUTH;
|
|
}
|
|
else if (dy == 0) {
|
|
if (dx == -1) d = WEST;
|
|
else if (dx == 1) d = EAST;
|
|
}
|
|
|
|
ASSERT(d != NODIR, XLevelRuntime,
|
|
"direction_fromto: source and target not adjacent");
|
|
return d;
|
|
}
|
|
|
|
string enigma::to_suffix(Direction d) {
|
|
static const char *sfx[] = { "", "-w", "-s", "-e", "-n" };
|
|
return sfx[d+1];
|
|
}
|
|
|
|
|
|
/* -------------------- DirectionBits -------------------- */
|
|
|
|
DirectionBits
|
|
enigma::rotate(DirectionBits d, bool clockwise)
|
|
{
|
|
if (clockwise) {
|
|
d = DirectionBits(((d>>1) | (d<<3)) & ALL_DIRECTIONS);
|
|
} else {
|
|
d = DirectionBits(((d>>3) | (d<<1)) & ALL_DIRECTIONS);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
|
|
/* -------------------- Value implementation -------------------- */
|
|
|
|
Value::Value(const char* str)
|
|
: type(STRING)
|
|
{
|
|
val.str = new char[strlen(str)+1];
|
|
strcpy(val.str, str);
|
|
}
|
|
|
|
Value::~Value()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
|
|
Value::Value(const string& str)
|
|
: type(STRING)
|
|
{
|
|
val.str = new char[str.length()+1];
|
|
strcpy(val.str, str.c_str());
|
|
}
|
|
|
|
Value::Value (const Value& other) : type(NIL) {
|
|
this->operator=(other);
|
|
}
|
|
|
|
Value& Value::operator= (const Value& other) {
|
|
if (this != &other) {
|
|
if (other.type == STRING) {
|
|
assign(other.val.str);
|
|
} else {
|
|
clear();
|
|
type = other.type;
|
|
val = other.val;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
void Value::assign(const char* s) {
|
|
clear();
|
|
type = STRING;
|
|
val.str = new char[strlen(s)+1];
|
|
strcpy(val.str, s);
|
|
}
|
|
|
|
void Value::assign(double d)
|
|
{
|
|
clear(); type=DOUBLE; val.dval=d;
|
|
}
|
|
|
|
|
|
void Value::clear() {
|
|
if (type == STRING)
|
|
delete[] val.str;
|
|
type = NIL;
|
|
}
|
|
|
|
double Value::get_double() const throw()
|
|
{
|
|
ASSERT(type == DOUBLE, XLevelRuntime, "get_double: type not double");
|
|
return val.dval;
|
|
}
|
|
|
|
const char* Value::get_string() const throw()
|
|
{
|
|
ASSERT(type == STRING, XLevelRuntime, "get_string: type not string");
|
|
return val.str;
|
|
}
|
|
|
|
Buffer& enigma::operator<<(Buffer& buf, const Value& val)
|
|
{
|
|
buf << Uint8(val.get_type());
|
|
|
|
switch (val.get_type()) {
|
|
case Value::NIL:
|
|
break;
|
|
case Value::DOUBLE:
|
|
buf << val.get_double();
|
|
break;
|
|
case Value::STRING:
|
|
{
|
|
const char* str = val.get_string();
|
|
buf << (Uint16)strlen(str);
|
|
buf.write(str, strlen(str));
|
|
} break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// Buffer& enigma::operator>>(Buffer& buf, Value& val)
|
|
// {
|
|
// Uint8 type = Value::NIL;
|
|
// buf >> type;
|
|
|
|
// switch (type) {
|
|
// case Value::NIL:
|
|
// // ## fixme
|
|
// break;
|
|
// case Value::DOUBLE:
|
|
// {
|
|
// double tmp;
|
|
// if (buf >> tmp)
|
|
// val = Value(tmp);
|
|
// } break;
|
|
// case Value::STRING:
|
|
// {
|
|
// Uint16 len;
|
|
// if (buf >> len) {
|
|
// char* tmp = new char[len+1];
|
|
// tmp[len] = 0;
|
|
// if (buf.read(tmp, len))
|
|
// val.assign(tmp);
|
|
// delete[] tmp;
|
|
// }
|
|
// } break;
|
|
// }
|
|
// return buf;
|
|
// }
|
|
|
|
int enigma::to_int(const Value &v) {
|
|
switch (v.get_type()) {
|
|
case Value::DOUBLE: return round_nearest<int>(v.get_double());
|
|
case Value::STRING: return atoi(v.get_string());
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
bool enigma::to_bool(const Value &v) {
|
|
return (v.get_type() != Value::NIL);
|
|
}
|
|
|
|
double enigma::to_double(const Value &v) {
|
|
switch (v.get_type()) {
|
|
case Value::DOUBLE: return v.get_double();
|
|
case Value::STRING: return atof(v.get_string());
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
const char * enigma::to_string(const Value &v) {
|
|
static char buf[30];
|
|
switch (v.get_type()) {
|
|
case Value::NIL: return "";
|
|
case Value::DOUBLE:
|
|
snprintf(buf, sizeof(buf), "%f", v.get_double());
|
|
return buf;
|
|
case Value::STRING: return v.get_string();
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
Direction enigma::to_direction (const Value &v) {
|
|
int val = Clamp(to_int(v), 0, 3);
|
|
return static_cast<Direction>(val);
|
|
}
|
|
|
|
ostream& enigma::operator<<(ostream& os, const Value& val)
|
|
{
|
|
switch (val.get_type()) {
|
|
case Value::NIL: os << "nil"; break;
|
|
case Value::DOUBLE: os << val.get_double(); break;
|
|
case Value::STRING: os << val.get_string(); break;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
/* -------------------- GridPos -------------------- */
|
|
|
|
GridPos::GridPos(const ecl::V2& p)
|
|
: x (round_down<int>(p[0])),
|
|
y (round_down<int>(p[1]))
|
|
{}
|
|
|
|
|
|
std::ostream& enigma::operator<<(std::ostream& os, const GridPos& val)
|
|
{
|
|
return os << '(' << val.x << ',' << val.y << ')';
|
|
}
|
|
|
|
/*
|
|
516
|
|
203
|
|
748
|
|
*/
|
|
GridPos enigma::get_neighbour (GridPos p, int i)
|
|
{
|
|
ASSERT (i >= 0 && i <= 9, XLevelRuntime, "get_neighbour: index out of bounds");
|
|
static int xoff[9] = { 0,0,-1,1,0,-1,1,-1,1 };
|
|
static int yoff[9] = { 0,-1,0,0,1,-1,-1,1,1 };
|
|
return GridPos(p.x + xoff[i], p.y + yoff[i]);
|
|
}
|
|
|
|
/* -------------------- GridLoc -------------------- */
|
|
|
|
bool enigma::to_gridloc (const char *str, GridLoc &l) {
|
|
GridLoc loc;
|
|
const char *numstr = str + 3;
|
|
|
|
if (strncmp (str, "fl(", 3) == 0)
|
|
loc.layer = GRID_FLOOR;
|
|
else if (strncmp (str, "it(", 3) == 0)
|
|
loc.layer = GRID_ITEMS;
|
|
else if (strncmp (str, "st(", 3) == 0)
|
|
loc.layer = GRID_STONES;
|
|
else
|
|
numstr = str;
|
|
|
|
if (2 != sscanf (numstr, "%d %d", &loc.pos.x, &loc.pos.y))
|
|
return false;
|
|
l = loc;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* -------------------- Random numbers -------------------- */
|
|
|
|
void enigma::Randomize ()
|
|
{
|
|
srand (time(NULL));
|
|
}
|
|
|
|
void enigma::Randomize (unsigned seed)
|
|
{
|
|
srand (seed);
|
|
}
|
|
|
|
int enigma::IntegerRand (int min, int max)
|
|
{
|
|
int r = round_down<int>((max-min+1) * (rand()/(RAND_MAX+1.0)));
|
|
return r+min;
|
|
}
|
|
|
|
double enigma::DoubleRand (double min, double max)
|
|
{
|
|
return min + double(rand())/RAND_MAX * (max-min);
|
|
}
|
|
|
|
|
|
/* -------------------- Time & Date -------------------- */
|
|
|
|
#define MAX_DATE_LENGTH 256
|
|
const char *enigma::date(const char *format) { // format see 'man strftime'
|
|
static char *result = 0;
|
|
char buffer[MAX_DATE_LENGTH];
|
|
|
|
time_t t;
|
|
time(&t);
|
|
|
|
struct tm *tm = localtime(&t);
|
|
strftime(buffer, MAX_DATE_LENGTH, format, tm);
|
|
|
|
if (result) free(result);
|
|
result = strdup(buffer);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* -------------------- Resource management -------------------- */
|
|
|
|
namespace
|
|
{
|
|
struct FontDescr {
|
|
// Variables
|
|
string name;
|
|
string ttf_name;
|
|
int ttf_size;
|
|
string bitmap_name;
|
|
int r, g, b;
|
|
|
|
// Constructor
|
|
FontDescr (const string &name_,
|
|
const string &ttf_name_,
|
|
int ttf_size_,
|
|
const string &bitmap_name_,
|
|
int r_, int g_, int b_)
|
|
: name (name_),
|
|
ttf_name (ttf_name_),
|
|
ttf_size (ttf_size_),
|
|
bitmap_name (bitmap_name_),
|
|
r (r_), g(g_), b(b_)
|
|
{}
|
|
};
|
|
|
|
class FontCache : public PtrCache<Font> {
|
|
public:
|
|
Font *acquire (const std::string &name) {
|
|
Font *f = 0;
|
|
if (m_fonts.has_key (name)) {
|
|
const FontDescr &fd = m_fonts[name];
|
|
f = load_ttf (fd.ttf_name, fd.ttf_size, fd.r, fd.g, fd.b);
|
|
if (f == 0) {
|
|
std::cerr << "Could not load .ttf file " << fd.ttf_name << "\n";
|
|
f = load_bmf (fd.bitmap_name);
|
|
}
|
|
}
|
|
else {
|
|
f = load_bmf (name);
|
|
}
|
|
return f;
|
|
}
|
|
|
|
void define_font (const FontDescr &descr) {
|
|
remove (descr.name); // remove entry in cache (if any)
|
|
if (m_fonts.has_key (descr.name))
|
|
m_fonts[descr.name]= descr;
|
|
else
|
|
m_fonts.insert (descr.name, descr);
|
|
}
|
|
|
|
private:
|
|
|
|
Font *load_bmf (const string &name) {
|
|
string png, bmf;
|
|
if (app.resourceFS->findFile(string("fonts/")+name+".png", png) &&
|
|
app.resourceFS->findFile(string("fonts/")+name+".bmf", bmf))
|
|
{
|
|
return ecl::LoadBitmapFont(png.c_str(), bmf.c_str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Font *load_ttf (const string &name, int ptsize, int r, int g, int b) {
|
|
string ttf;
|
|
if (app.resourceFS->findFile(string("fonts/") + name, ttf))
|
|
return ecl::LoadTTF (ttf.c_str(), ptsize, r, g, b);
|
|
return 0;
|
|
}
|
|
|
|
// Variables
|
|
ecl::Dict <FontDescr> m_fonts;
|
|
};
|
|
|
|
// ---------- Variables ----------
|
|
|
|
FontCache font_cache;
|
|
ImageCache image_cache;
|
|
}
|
|
|
|
ecl::Surface *ImageCache::acquire (const std::string &name)
|
|
{
|
|
return ecl::LoadImage(name.c_str());
|
|
}
|
|
|
|
void enigma::DefineFont (const char *name,
|
|
const char *ttf_name,
|
|
int ttf_size,
|
|
const char *bmf_name,
|
|
int r, int g, int b)
|
|
{
|
|
font_cache.define_font (FontDescr (name, ttf_name, ttf_size, bmf_name, r, g, b));
|
|
}
|
|
|
|
ecl::Font *enigma::GetFont (const char *name)
|
|
{
|
|
return font_cache.get(name);
|
|
}
|
|
|
|
void enigma::ClearFontCache() {
|
|
font_cache.clear();
|
|
}
|
|
|
|
ecl::Surface *enigma::LoadImage(const char *name)
|
|
{
|
|
string filename;
|
|
if (app.resourceFS->findImageFile (string(name) + ".png", filename))
|
|
return ecl::LoadImage(filename.c_str());
|
|
return 0;
|
|
}
|
|
|
|
ecl::Surface *enigma::GetImage(const char *name, const char *ext)
|
|
{
|
|
string filename;
|
|
if (app.resourceFS->findImageFile (string(name) + ext, filename))
|
|
return image_cache.get(filename);
|
|
return 0;
|
|
}
|
|
|
|
ecl::Surface *enigma::RegisterImage (const char *name, ecl::Surface *s)
|
|
{
|
|
image_cache.store(name, s);
|
|
return s;
|
|
}
|
|
|
|
void enigma::ClearImageCache() {
|
|
image_cache.clear();
|
|
}
|