#include "world.hpp"
const tile_t tile_defaults[] = {
{ tile_t::TILE_NONE, 0, false, false, false, tile_t::TILE_MODEL_FLAT, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_FLOOR, 1, false, false, false, tile_t::TILE_MODEL_FLAT, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_WATER, 2, true, false, false, tile_t::TILE_MODEL_FLAT, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_BOX, 1, true, false, true, tile_t::TILE_MODEL_CUBE_LOW, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_ROCK, 3, true, false, false, tile_t::TILE_MODEL_SPIKE, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_DOOR, 1, false, false, false, tile_t::TILE_MODEL_FLAT, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_GOAL, 1, false, false, false, tile_t::TILE_MODEL_FLAT, tile_t::TILE_ACTION_NONE, {0} },
{ tile_t::TILE_PLAYER, 0, true, false, false, tile_t::TILE_MODEL_CUBE_LOW, tile_t::TILE_ACTION_NONE, {0} }
};
unsigned Layout::id_cnt = 0;
Layout::Layout(unsigned ww, unsigned hh): border_buf((unsigned*)calloc(ww*hh, sizeof(unsigned))), buf((tile_t*)calloc(ww*hh,sizeof(tile_t))), bottom_buf((tile_t*)calloc(ww*hh,sizeof(tile_t))), w(ww), h(hh), id(++id_cnt) {
assert(w>0 && h>0);
for (unsigned i=0; i<w*h; ++i) { // XXX: needed?
bottom_buf[i] = tile_defaults[tile_t::TILE_NONE];
}
}
Layout::~Layout() {
free(border_buf);
free(buf);
free(bottom_buf);
}
void Layout::find_borders() {
for (unsigned y=0; y<h; ++y) {
for (unsigned x=0; x<w; ++x) {
const unsigned t = at(x, y).tile_border_type;
if (t != 1) continue; // only for "floors"
unsigned rv = 0;
if (x == w-1 || y == h-1 || at(x+1, y).tile_border_type != t || at(x, y+1).tile_border_type != t || at(x+1, y+1).tile_border_type != t) rv |= 1u; // tr
if (x == w-1 || y == 0 || at(x+1, y).tile_border_type != t || at(x, y-1).tile_border_type != t || at(x+1, y-1).tile_border_type != t) rv |= 2u; // br
if (x == 0 || y == 0 || at(x-1, y).tile_border_type != t || at(x, y-1).tile_border_type != t || at(x-1, y-1).tile_border_type != t) rv |= 4u; // bl
if (x == 0 || y == h-1 || at(x-1, y).tile_border_type != t || at(x, y+1).tile_border_type != t || at(x-1, y+1).tile_border_type != t) rv |= 8u; // tl
border_buf[(y*w)+x] = rv;
}
}
}
World::World(Layout* l): world(l), w(l->w), h(l->h), x(player_x), y(player_y) {
for (unsigned x=0; x<w; ++x) {
for (unsigned y=0; y<h; ++y) {
if (world->at(x, y).start) {
player_x = x;
player_y = y;
return;
}
}
}
assert(false);
}
World::~World() {
delete world;
}
const Layout* World::world_get() const {
return world;
}
bool World::player_step(int xdir, int ydir) {
if (!xdir && !ydir) return false;
assert(!xdir != !ydir);
assert(xdir >= -1 && xdir <= 1);
assert(ydir >= -1 && ydir <= 1);
int new_x = (int)player_x + xdir;
int new_y = (int)player_y + ydir;
if (new_x < 0 || new_y < 0) return false;
return player_goto((unsigned)new_x, (unsigned)new_y);
}
bool World::player_push(int xdir, int ydir) {
if (!xdir && !ydir) return false;
assert(!xdir != !ydir);
assert(xdir >= -1 && xdir <= 1);
assert(ydir >= -1 && ydir <= 1);
int new_x = (int)player_x + xdir;
int new_y = (int)player_y + ydir;
if (new_x < 1 || new_y < 1 || new_x >= (int)w-1 || new_y >= (int)h-1) return false;
if (!world->at(new_x, new_y).movable) return false;
int new_move_x = new_x + xdir;
int new_move_y = new_y + ydir;
if (world->at(new_move_x, new_move_y).clip) return false;
assert(world->at(new_move_x, new_move_y).tile != tile_t::TILE_NONE);
if (world->below(new_move_x, new_move_y).tile != tile_t::TILE_NONE) return false;
world->below(new_move_x, new_move_y) = world->at(new_move_x, new_move_y); // backup
world->at(new_move_x, new_move_y) = world->at(new_x, new_y); // move
if (world->below(new_x, new_y).tile != tile_t::TILE_NONE) { // restore
assert(!world->below(new_x, new_y).clip);
world->at(new_x, new_y) = world->below(new_x, new_y);
world->below(new_x, new_y) = tile_defaults[tile_t::TILE_NONE];
} else { // default
world->at(new_x, new_y) = tile_defaults[tile_t::TILE_FLOOR];
}
player_x = new_x;
player_y = new_y;
return true;
}
bool World::player_goto(unsigned x, unsigned y) {
if (x >= w || y >= h) return false;
if (x == player_x && y == player_y) return false;
if (world->at(x, y).clip) return false;
player_x = x;
player_y = y;
return true;
}
const tile_t& World::at(unsigned x, unsigned y, unsigned& len, unsigned& borders) const {
len = 1;
borders = world->borders(x, y);
const tile_t& rv = world->at(x, y);
while (x<w-1 && world->at(++x, y).tile == rv.tile && world->at(x, y).model == rv.model && world->borders(x, y) == borders) {
len++;
}
return rv;
}
Worlds::Worlds(): world(NULL) {
}
Worlds::Worlds(unsigned i, World* w): world(w) {
push(i, w);
}
void Worlds::push(unsigned id, World* w) {
assert(id != 0);
assert(get(id) == NULL);
worlds.insert(std::pair<unsigned, World*>(id, w));
}
World* Worlds::get(unsigned id) {
std::map<unsigned, World*>::iterator it = worlds.find(id);
return (it == worlds.end())? NULL: it->second;
}
bool Worlds::set(unsigned id) {
World* w = get(id);
if (w) {
world = w;
return true;
} else {
return false;
}
}
bool Worlds::all_done() const {
for (std::map<unsigned, World*>::const_iterator it = worlds.begin(); it != worlds.end(); it++) {
if (!it->second->world_get()->is_done()) return false;
}
return true;
}
Worlds::~Worlds() {
while (!worlds.empty()) {
delete worlds.begin()->second;
worlds.erase(worlds.begin());
}
}