diff --git a/semestralka1/src/game/obstacle_manager.h b/semestralka1/src/game/obstacle_manager.h index 10e20be..933eb79 100644 --- a/semestralka1/src/game/obstacle_manager.h +++ b/semestralka1/src/game/obstacle_manager.h @@ -21,7 +21,7 @@ inline void spawn_obstacle(CrawlObstacleType type, int x_start) { obstacle_pool[i].active = true; obstacle_pool[i].type = type; obstacle_pool[i].data.x = x_start; - obstacle_pool[i].data.y = 0; // hangs from ceiling + obstacle_pool[i].data.y = 10; // hangs from ceiling if (type == CrawlObstacleType::Crawl1) { obstacle_pool[i].data.width = OBSTACLE_CRAWL1_COLLISION_WIDTH; obstacle_pool[i].data.height = OBSTACLE_CRAWL1_COLLISION_HEIGHT; diff --git a/semestralka1/src/game/obstacle_system.h b/semestralka1/src/game/obstacle_system.h new file mode 100644 index 0000000..307072a --- /dev/null +++ b/semestralka1/src/game/obstacle_system.h @@ -0,0 +1,97 @@ +// src/game/obstacle_system.h +#pragma once +#include "obstacle_manager.h" +#include "../render/player_positioning.h" +#include "../assets/obstacle_crawl_frames.h" +#include "collision.h" +#include "mbed.h" + +// Small helper for minimal mbed-safe clamp +inline int clamp_min(int a, int b) { return (a > b) ? a : b; } +inline int clamp_max(int a, int b) { return (a < b) ? a : b; } + +// Handles spawning, movement, rendering, and collision of obstacles +class ObstacleSystem { +public: + ObstacleSystem() : tick_counter_(0), spawn_index_(0) {} + + // Called once per frame + bool update_and_draw(const CharacterPosition &player_pos, + MovementType player_movement) { + // Update timers, move existing obstacles + tick_counter_++; + if (tick_counter_ % UPDATE_INTERVAL == 0) { + update_obstacles(1); + } + + // Periodically spawn new obstacle + if (tick_counter_ % SPAWN_EVERY_TICKS == 0) { + auto type = (spawn_index_++ % 2 == 0) + ? CrawlObstacleType::Crawl1 + : CrawlObstacleType::Crawl2; + const int w = (type == CrawlObstacleType::Crawl1) + ? OBSTACLE_CRAWL1_FRAME_WIDTH + : OBSTACLE_CRAWL2_FRAME_WIDTH; + spawn_obstacle(type, VIEW_WIDTH - w - 1); + } + + // Draw & collision check + for (int i = 0; i < MAX_OBSTACLES; i++) { + if (!obstacle_pool[i].active) + continue; + + const char **frame = nullptr; + int height = 0; + if (obstacle_pool[i].type == CrawlObstacleType::Crawl1) { + frame = OBSTACLE_CRAWL1_FRAME; + height = OBSTACLE_CRAWL1_FRAME_HEIGHT; + } else { + frame = OBSTACLE_CRAWL2_FRAME; + height = OBSTACLE_CRAWL2_FRAME_HEIGHT; + } + + draw_clipped_obstacle(obstacle_pool[i], frame, height); + + // Collision check + if (check_collision(player_pos, player_movement, + obstacle_pool[i].data)) { + printf("\033[2J\033[H"); + printf("GAME OVER\r\n"); + return true; + } + } + + return false; + } + +private: + static constexpr int SPAWN_EVERY_TICKS = 35; + static constexpr int UPDATE_INTERVAL = 1; // move obstacles each frame + int tick_counter_; + int spawn_index_; + + static void draw_clipped_obstacle(const MovingObstacle &obs, + const char **frame, + int frame_height) { + int x = obs.data.x; + int y = obs.data.y; + int w = obs.data.width; + + for (int r = 0; r < frame_height; ++r) { + int left = x; + int right = x + w; + if (right <= 0 || left >= VIEW_WIDTH) + continue; + + int vis_left = clamp_min(left, 0); + int vis_right = clamp_max(right, VIEW_WIDTH); + int start = vis_left - left; + int count = vis_right - vis_left; + if (count <= 0) + continue; + + const char *row = frame[r] + start; + printf("\033[%d;%dH%.*s", y + r + 1, vis_left + 1, count, row); + } + } +}; diff --git a/semestralka1/src/render/loop.cpp b/semestralka1/src/render/loop.cpp index b7d42c5..410fbe2 100644 --- a/semestralka1/src/render/loop.cpp +++ b/semestralka1/src/render/loop.cpp @@ -7,10 +7,13 @@ #include "player.h" #include "../game/state.h" #include "../game/animation.h" +#include "../game/collision.h" #include "../hardware/uart.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 BufferedSerial serial_port; extern DigitalOut led; @@ -38,9 +41,14 @@ void render_loop(int speed) { int anim_tick_counter = 0; int tick_counter = 0; int player_speed = 6; + bool game_over = false; player_state.set_state(PlayerState::Run); - while (true) { + + spawn_obstacle(CrawlObstacleType::Crawl1, 40); + obstacle_pool[0].data.y = 8; + obstacle_pool[0].active = true; + while (!game_over) { tick_counter++; mover.update_position(player_speed, timing.get_ground_speed()); @@ -56,11 +64,9 @@ void render_loop(int speed) { if (uart_event == UartEvent::Triggered) { PlayerState current = player_state.get_state(); - // pressing UART trigger while walking/running -> Crawl1 if (current == PlayerState::Walk || current == PlayerState::Run) { player_state.start_crawl(PlayerState::Crawl1); } - // pressing again while crawling -> Crawl2 else if (current == PlayerState::Crawl1) { player_state.start_crawl(PlayerState::Crawl2); } @@ -84,6 +90,17 @@ void render_loop(int speed) { CharacterPosition draw_pos = get_aligned_frame_position(pos, frame.movement, frame.frame_index); draw_character(draw_pos.x, draw_pos.y, frame.movement, frame.frame_index); + for (int i = 0; i < MAX_OBSTACLES; i++) { + if (!obstacle_pool[i].active) + continue; + draw_obstacle(obstacle_pool[i].data.x, + obstacle_pool[i].data.y, + obstacle_pool[i].type); + } + + if (game_over) + break; + ThisThread::sleep_for(50ms); } diff --git a/semestralka1/src/render/obstacle.cpp b/semestralka1/src/render/obstacle.cpp new file mode 100644 index 0000000..806c737 --- /dev/null +++ b/semestralka1/src/render/obstacle.cpp @@ -0,0 +1,31 @@ +// src/render/obstacle.cpp +#include "obstacle.h" +#include "../assets/obstacle_crawl_frames.h" +#include + +// Draw a single obstacle ASCII sprite at (x, y) +void draw_obstacle(int x, int y, CrawlObstacleType type) { + const char **obstacle = nullptr; + int obstacle_height = 0; + + switch (type) { + case CrawlObstacleType::Crawl1: + obstacle = OBSTACLE_CRAWL1_FRAME; + obstacle_height = OBSTACLE_CRAWL1_FRAME_HEIGHT; + break; + + case CrawlObstacleType::Crawl2: + obstacle = OBSTACLE_CRAWL2_FRAME; + obstacle_height = OBSTACLE_CRAWL2_FRAME_HEIGHT; + break; + + default: + return; // in case of invalid type + } + + // Each obstacle row is printed using ANSI cursor positioning + for (int i = 0; i < obstacle_height; i++) { + printf("\033[%d;%dH%s", y + i + 1, x + 1, obstacle[i]); + } + fflush(stdout); +} diff --git a/semestralka1/src/render/obstacle.h b/semestralka1/src/render/obstacle.h new file mode 100644 index 0000000..feec541 --- /dev/null +++ b/semestralka1/src/render/obstacle.h @@ -0,0 +1,7 @@ +// src/render/obstacle.h +#pragma once + +#include "../assets/obstacle_crawl_frames.h" + +// Draw the obstacle ASCII block starting at given (x, y) +void draw_obstacle(int x, int y, CrawlObstacleType type);