206 lines
6.7 KiB
C++
206 lines
6.7 KiB
C++
void overworld_update()
|
|
{
|
|
change_maps();
|
|
update_warp();
|
|
}
|
|
|
|
constexpr inline world_t direction_offset(const world_t& world, const enum actor_t::direction dir)
|
|
{
|
|
switch (dir) {
|
|
default: [[fallthrough]];
|
|
case actor_t::up: return {world.x , world.y - 1};
|
|
case actor_t::down: return {world.x , world.y + 1};
|
|
case actor_t::left: return {world.x - 1, world.y };
|
|
case actor_t::right: return {world.x + 1, world.y };
|
|
}
|
|
}
|
|
|
|
constexpr inline uint8_t get_tile_ix(const map_t& map, const world_t& coord)
|
|
{
|
|
// keep this synchronized with render_screen()
|
|
|
|
const uint8_t block_ix = get_block(map, coord.to_block());
|
|
const tileset_t& tileset = tilesets[map.tileset];
|
|
const uint8_t * block_start = &(tileset.blockset.start)[block_ix * 4 * 4];
|
|
const int32_t quadrant_x = 2 * (coord.x & 1);
|
|
const int32_t quadrant_y = 2 * (coord.y & 1);
|
|
|
|
const int32_t block_row = 4 * (quadrant_y + 1);
|
|
const uint8_t tile_ix = block_start[block_row + quadrant_x];
|
|
|
|
return tile_ix;
|
|
}
|
|
|
|
constexpr inline enum actor_t::collision collision(const map_t& map,
|
|
const world_t& world,
|
|
enum actor_t::direction direction)
|
|
{
|
|
const world_t coord = direction_offset(world, direction);
|
|
uint8_t collision_tile_ix = get_tile_ix(map, coord);
|
|
|
|
const tileset_t& tileset = tilesets[map.tileset];
|
|
for (uint32_t i = 0; i < tileset.collision.size; i++) {
|
|
if (tileset.collision.start[i] == collision_tile_ix)
|
|
return actor_t::passable;
|
|
}
|
|
|
|
// check ledge_tile pairs
|
|
|
|
uint8_t actor_tile_ix = get_tile_ix(map, world);
|
|
|
|
for (uint32_t i = 0; i < ledge_tiles_length; i++) {
|
|
const ledge_tile_t& lt = ledge_tiles[i];
|
|
if (direction == lt.direction
|
|
&& actor_tile_ix == lt.actor
|
|
&& collision_tile_ix == lt.collision) {
|
|
return actor_t::jump;
|
|
}
|
|
}
|
|
|
|
return actor_t::impassable;
|
|
}
|
|
|
|
constexpr inline void collision_move(const map_t& map, actor_t::direction dir)
|
|
{
|
|
const enum actor_t::collision c = collision(map, state.player.world, dir);
|
|
state.player.move(c, dir);
|
|
}
|
|
|
|
void change_maps()
|
|
{
|
|
const map_t& map = maps[state.map];
|
|
|
|
#define _has(_dir_) (map.connections[map_t::connection_t::_dir_].map != map_t::unconnected)
|
|
#define _get(_dir_) (maps[map.connections[map_t::connection_t::_dir_].map])
|
|
#define _offset(_dir_) (map.connections[map_t::connection_t::_dir_].offset)
|
|
|
|
const block_t block = state.player.world.to_block();
|
|
if (block.y < 0) {
|
|
// north
|
|
if (_has(north)) {
|
|
const map_t& north_map = _get(north);
|
|
state.player.world.y = ((north_map.height + block.y) << 1) | (state.player.world.y & 1);
|
|
state.player.world.x = ((block.x - _offset(north)) << 1) | (state.player.world.x & 1);
|
|
state.map = map.connections[map_t::connection_t::north].map;
|
|
}
|
|
} else if (block.y >= static_cast<int32_t>(map.height)) {
|
|
// south
|
|
if (_has(south)) {
|
|
state.player.world.y = ((block.y - map.height) << 1) | (state.player.world.y & 1);
|
|
state.player.world.x = ((block.x - _offset(south)) << 1) | (state.player.world.x & 1);
|
|
state.map = map.connections[map_t::connection_t::south].map;
|
|
}
|
|
} else if (block.x < 0) {
|
|
// west
|
|
if (_has(west)) {
|
|
const map_t& west_map = _get(west);
|
|
state.player.world.x = ((west_map.width + block.x) << 1) | (state.player.world.x & 1);
|
|
state.player.world.y = ((block.y - _offset(west)) << 1) | (state.player.world.y & 1);
|
|
state.map = map.connections[map_t::connection_t::west].map;
|
|
}
|
|
} else if (block.x >= static_cast<int32_t>(map.width)) {
|
|
// east
|
|
if (_has(east)) {
|
|
state.player.world.x = ((block.x - map.width) << 1) | (state.player.world.x & 1);
|
|
state.player.world.y = ((block.y - _offset(east)) << 1) | (state.player.world.y & 1);
|
|
state.map = map.connections[map_t::connection_t::east].map;
|
|
}
|
|
}
|
|
|
|
#undef _offset
|
|
#undef _get
|
|
#undef _has
|
|
}
|
|
|
|
struct collision_direction_t {
|
|
enum actor_t::collision collision;
|
|
enum actor_t::direction direction;
|
|
};
|
|
|
|
void check_sign()
|
|
{
|
|
const world_t coord = direction_offset(state.player.world, state.player.facing);
|
|
const map_t& map = maps[state.map];
|
|
const object_t& obj = map_objects[state.map];
|
|
|
|
for (uint32_t i = 0; i < obj.bg_length; i++) {
|
|
const bg_event_t& event = obj.bg_events[i];
|
|
const bool position_match = event.position.x == coord.x && event.position.y == coord.y;
|
|
if (position_match && event.sign_id != 0xff) {
|
|
const start_size_t& text = map.text_pointers[event.sign_id];
|
|
}
|
|
}
|
|
}
|
|
|
|
void update_warp()
|
|
{
|
|
if (state.player.moving) return;
|
|
world_t& coord = state.player.world;
|
|
|
|
for (uint32_t j = 0; j < map_objects[state.map].warp_length; j++) {
|
|
const warp_event_t& warp = map_objects[state.map].warp_events[j];
|
|
if (coord.x == warp.position.x && coord.y == warp.position.y) {
|
|
if (warp.destination.map == map_t::last_map) {
|
|
if (state.last_map == map_t::last_map) {
|
|
// use last_map as a sentinel value for "the player hasn't
|
|
// warped yet". This should never happen in normal gameplay.
|
|
continue;
|
|
}
|
|
|
|
state.map = state.last_map;
|
|
} else {
|
|
// Only change last_map if the current map is an "outside"
|
|
// map. Warps that use last_map are designed to be used this
|
|
// way.
|
|
if (is_outside(state.map)) state.last_map = state.map;
|
|
|
|
state.map = warp.destination.map;
|
|
}
|
|
// warp_index starts at 1
|
|
const warp_event_t& dest = map_objects[state.map].warp_events[warp.destination.warp_index - 1];
|
|
coord.x = dest.position.x;
|
|
coord.y = dest.position.y;
|
|
|
|
// force the player to move off of the warp
|
|
|
|
const collision_direction_t c_d = find_direction(maps[state.map], state.player.world, state.player.facing);
|
|
state.player.move(c_d.collision, c_d.direction);
|
|
|
|
// must return: because map state.map changed, the rest of this
|
|
// loop is invalid
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr inline collision_direction_t find_direction(const map_t& map, const world_t& world, const enum actor_t::direction facing)
|
|
{
|
|
// first, try `facing`, as this is typically correct in non- edge-cases
|
|
const enum actor_t::collision c_facing = collision(map, world, facing);
|
|
if (c_facing != actor_t::impassable)
|
|
return {c_facing, facing};
|
|
|
|
// otherwise try all other directions
|
|
// (this checks facing a second time for no reason)
|
|
constexpr enum actor_t::direction dirs[] = {actor_t::up, actor_t::down, actor_t::left, actor_t::right};
|
|
for (const enum actor_t::direction& dir : dirs) {
|
|
const enum actor_t::collision c = collision(map, world, dir);
|
|
if (c != actor_t::impassable)
|
|
return {c, dir};
|
|
}
|
|
|
|
return {actor_t::impassable, facing};
|
|
}
|
|
|
|
constexpr inline bool is_outside(const enum map_t::map& map_id)
|
|
{
|
|
const map_t& map = maps[map_id];
|
|
switch (map.tileset) {
|
|
case tileset_t::overworld: [[fallthrough]];
|
|
case tileset_t::plateau:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|