pokemon/actor.hpp

128 lines
2.5 KiB
C++

#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;
}
};