/* * 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 #include #include 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_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(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(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(p[0])), y (round_down(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((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 { 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 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(); }