#pragma once #include "coordinates.hpp" struct actor_t { enum direction { up, down, left, right, }; enum collision { impassable, jump, passable, }; world_t world; enum direction facing; bool moving; enum collision collision; uint8_t frame; uint8_t cycle; constexpr inline int32_t frame_pixels() { switch (collision) { case jump: return frame; default: return frame; } } constexpr inline offset_t offset() { const int32_t pixels = frame_pixels(); switch (collision) { default: return {0, 0}; case jump: [[fallthrough]]; case passable: switch (facing) { case left: return {-pixels, 0}; case right: return {pixels, 0}; case up: return {0, -pixels}; case down: return {0, pixels}; default: return {0, 0}; } } } // engine/overworld/player_animations.asm // $38, $36, $34, $32, $31, $30, $30, $30, $31, $32, $33, $34, $36, $38, $3C, $3C // ^ these are in absolute position, subtract 60 from each to get the offset: // an offset of -4 is the same as not jumping static constexpr int8_t jump_screen_offset[16] = {-4, -8, -10, -12, -14, -15, -16, -16, -16, -15, -14, -13, -12, -10, -8, -4}; // in pixels constexpr inline int32_t y_offset() { if (collision == jump) return jump_screen_offset[frame >> 1 & 15]; else return -4; } constexpr inline int32_t world_increment() { switch (collision) { case jump: return 2; default: return 1; } } // call tick() before move() constexpr inline void tick() { if (!moving) return; frame = frame + 1; if (frame == (16 * world_increment())) { cycle += 1; frame = 0; int32_t inc = world_increment(); switch (collision) { default: break; case jump: [[fallthrough]]; case passable: switch (facing) { case left: world.x -= inc; break; case right: world.x += inc; break; case up: world.y -= inc; break; case down: world.y += inc; break; } } moving = false; } } constexpr inline void move(enum collision collision, enum direction dir) { /* m 1 . 2 . 3 . 4 . | - | - | - | - | */ // don't change directions in the middle of a movement animation if (this->collision != impassable && moving) return; // reset animation frame if there is no collision // if there is a collision, keep animating if (collision != impassable) frame = 0; facing = dir; moving = true; this->collision = collision; } };