// 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" 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*/) { draw_obstacle(obs.data.x, obs.data.y, obs.type); } };