223 lines
6.2 KiB
C++
223 lines
6.2 KiB
C++
// src/render/loop.cpp
|
|
#include "loop.h"
|
|
#include "../assets/background_frame.h"
|
|
#include "player_positioning.h"
|
|
#include "background.h"
|
|
#include "mbed.h"
|
|
#include "player.h"
|
|
#include "background.h"
|
|
#include "../game/game_over.h"
|
|
#include "../game/game_state.h"
|
|
#include "../game/state.h"
|
|
#include "../game/animation.h"
|
|
#include "../game/collision.h"
|
|
#include "../hardware/button.h"
|
|
#include "../render/player.h"
|
|
#include "../render/obstacle.h"
|
|
#include "../timing/speed_controller.h"
|
|
#include "../timing/movement_controller.h"
|
|
#include "../game/obstacle_system.h"
|
|
|
|
extern DigitalOut led;
|
|
ButtonHandler button(BUTTON1, LED1);
|
|
|
|
// Constants
|
|
// constexpr int PLAYER_X = 9;
|
|
constexpr int PLAYER_X = 29;
|
|
constexpr int PLAYER_Y = 6;
|
|
constexpr int STACK_SIZE = 4096;
|
|
|
|
void logic_loop(void *arg) {
|
|
int speed = *(int *)arg;
|
|
MovementState player_state;
|
|
AnimationController animation;
|
|
MovementController mover(PLAYER_X, VIEW_WIDTH);
|
|
SpeedController timing;
|
|
timing.set_ground_speed(speed);
|
|
|
|
CharacterPosition pos = {PLAYER_X, PLAYER_Y};
|
|
|
|
Timer spawn_timer;
|
|
spawn_timer.start();
|
|
Timer speed_timer;
|
|
speed_timer.start();
|
|
|
|
int anim_tick_counter = 0;
|
|
int tick_counter = 0;
|
|
bool game_over = false;
|
|
Timer game_timer;
|
|
game_timer.start();
|
|
|
|
// Spawn first obstacle
|
|
if (speed >= 5)
|
|
spawn_obstacle(CrawlObstacleType::Crawl1, VIEW_WIDTH + 10);
|
|
|
|
while (!game_over) {
|
|
tick_counter++;
|
|
|
|
if (speed_timer.elapsed_time() >= 6s) {
|
|
speed++;
|
|
timing.set_ground_speed(speed);
|
|
speed_timer.reset();
|
|
|
|
// update player's animation mode
|
|
if (speed < 4)
|
|
player_state.set_state(PlayerState::Walk);
|
|
else
|
|
player_state.set_state(PlayerState::Run);
|
|
}
|
|
mover.update_position(speed, timing.get_ground_speed());
|
|
pos.x = mover.get_position();
|
|
|
|
// Spawn periodically
|
|
if (speed >= 5 && spawn_timer.elapsed_time() > 6s) {
|
|
spawn_timer.reset();
|
|
spawn_obstacle(CrawlObstacleType::Crawl1, VIEW_WIDTH);
|
|
}
|
|
|
|
// Handle input
|
|
if (button.poll() == ButtonEvent::Pressed) {
|
|
PlayerState current = player_state.get_state();
|
|
if (current == PlayerState::Walk || current == PlayerState::Run)
|
|
player_state.start_crawl(PlayerState::Crawl1);
|
|
else if (current == PlayerState::Crawl1)
|
|
player_state.start_crawl(PlayerState::Crawl2);
|
|
}
|
|
|
|
player_state.update();
|
|
|
|
bool anim_tick = animation.tick(speed);
|
|
if (anim_tick) {
|
|
anim_tick_counter++;
|
|
player_state.toggle_walk_frame(speed, anim_tick_counter);
|
|
FrameSelection frame = player_state.get_frame_selection();
|
|
CharacterPosition draw_pos = get_aligned_frame_position(pos, frame.movement, frame.frame_index);
|
|
|
|
int ground_speed = timing.get_ground_speed();
|
|
bool collision = false;
|
|
|
|
for (int i = 0; i < MAX_OBSTACLES; i++) {
|
|
if (!obstacle_pool[i].active)
|
|
continue;
|
|
|
|
// Keep original position
|
|
int old_x = obstacle_pool[i].data.x;
|
|
int new_x = old_x - ground_speed;
|
|
|
|
// Remove if fully offscreen after movement
|
|
if (new_x + obstacle_pool[i].data.width < 0) {
|
|
obstacle_pool[i].active = false;
|
|
continue;
|
|
}
|
|
|
|
// Compute swept region = min..max
|
|
int left_swept = (new_x < old_x) ? new_x : old_x;
|
|
int right_swept = ((new_x + obstacle_pool[i].data.width) >
|
|
(old_x + obstacle_pool[i].data.width))
|
|
? (new_x + obstacle_pool[i].data.width)
|
|
: (old_x + obstacle_pool[i].data.width);
|
|
|
|
// Temporarily create a synthetic obstacle covering the swept range
|
|
Obstacle swept_obs{};
|
|
swept_obs.x = left_swept;
|
|
swept_obs.y = obstacle_pool[i].data.y;
|
|
swept_obs.width = right_swept - left_swept;
|
|
swept_obs.height = obstacle_pool[i].data.height;
|
|
|
|
// Perform continuous collision check
|
|
if (check_collision(pos, frame.movement, swept_obs)) {
|
|
collision = true;
|
|
break;
|
|
}
|
|
|
|
// Apply the actual movement after check
|
|
obstacle_pool[i].data.x = new_x;
|
|
}
|
|
|
|
|
|
g_state_mutex.lock();
|
|
|
|
g_state.player_pos = draw_pos;
|
|
g_state.current_speed = speed;
|
|
g_state.movement = frame.movement;
|
|
g_state.frame_index = frame.frame_index;
|
|
g_state.background_shift = animation.get_shift();
|
|
g_state.need_redraw = true;
|
|
g_state.game_over = collision;
|
|
|
|
for (int i = 0; i < MAX_OBSTACLES; i++) {
|
|
g_state.obstacles[i].x = obstacle_pool[i].data.x;
|
|
g_state.obstacles[i].y = obstacle_pool[i].data.y;
|
|
g_state.obstacles[i].type = obstacle_pool[i].type;
|
|
g_state.obstacles[i].active = obstacle_pool[i].active;
|
|
}
|
|
|
|
g_state_mutex.unlock();
|
|
|
|
if (collision)
|
|
game_over = true;
|
|
}
|
|
|
|
ThisThread::sleep_for(25ms);
|
|
}
|
|
|
|
game_timer.stop();
|
|
int seconds = static_cast<int>(game_timer.elapsed_time().count() / 1000000);
|
|
|
|
g_state_mutex.lock();
|
|
g_state.elapsed_seconds = seconds;
|
|
g_state.game_over = true;
|
|
g_state_mutex.unlock();
|
|
}
|
|
|
|
void render_loop_thread() {
|
|
while (true) {
|
|
g_state_mutex.lock();
|
|
bool need = g_state.need_redraw;
|
|
bool over = g_state.game_over;
|
|
GameState local = g_state;
|
|
g_state.need_redraw = false;
|
|
g_state_mutex.unlock();
|
|
|
|
if (over) {
|
|
show_game_over_screen(local.elapsed_seconds, button);
|
|
break;
|
|
}
|
|
|
|
if (need) {
|
|
draw_mask("background", local.background_shift);
|
|
draw_character(local.player_pos.x, local.player_pos.y, local.movement,
|
|
local.frame_index);
|
|
if (local.current_speed >= 5) {
|
|
for (int i = 0; i < MAX_RENDER_OBSTACLES; i++) {
|
|
if (local.obstacles[i].active)
|
|
draw_obstacle(local.obstacles[i].x, local.obstacles[i].y,
|
|
local.obstacles[i].type);
|
|
}
|
|
}
|
|
}
|
|
|
|
ThisThread::sleep_for(50ms);
|
|
}
|
|
}
|
|
|
|
void render_loop(int speed) {
|
|
g_state_mutex.lock();
|
|
g_state.need_redraw = false;
|
|
g_state.game_over = false;
|
|
g_state_mutex.unlock();
|
|
|
|
int *speed_ptr = new int(speed);
|
|
|
|
Thread logic_thread(osPriorityNormal, STACK_SIZE);
|
|
Thread render_thread(osPriorityNormal, STACK_SIZE);
|
|
|
|
logic_thread.start(callback(logic_loop, (void *)speed_ptr));
|
|
render_thread.start(render_loop_thread);
|
|
|
|
logic_thread.join();
|
|
render_thread.join();
|
|
|
|
delete speed_ptr;
|
|
}
|