#include #include #include #include #include #include "types.h" auto screen_width = 800; auto screen_height = 600; Rectangle walls[] = { {.x = 100, .y = 100, .width = 5, .height = 100} }; Player player; auto player_speed = 5.0f; Tear tears[100] = {}; float tear_speed = 10; float tear_range = 500; float tear_radius = 10.0f; double last_fired = 0; double fire_rate = .5; std::random_device dev; std::mt19937 rng(dev()); std::mutex enemy_mutex; float enemy_max_speed = 5.0; float enemy_radius = 50.0; Enemy enemies[100] = {}; Vector2 Player::center() const noexcept { return {.x = this->x + this->width / 2, .y = this->y + this->height / 2}; } void Player::move(const float delta_x, const float delta_y) { auto x = std::min( std::max(this->x + delta_x, 0.0f), static_cast(screen_width) - this->width); auto y = std::min( std::max(this->y + delta_y, 0.0f), static_cast(screen_height) - this->height); bool collided = false; const Rectangle next_position = { .x = x, .y = y, .width = this->width, .height = this->height }; for (const auto wall: walls) { if (CheckCollisionRecs(wall, next_position)) { collided = true; break; } } if (collided) { } else { this->x = x; this->y = y; } } struct EnemySpawner { std::mutex m; std::condition_variable cv; std::atomic running; std::atomic paused; std::thread timer; long long rate_secs; explicit EnemySpawner(const long long rate_secs) : rate_secs(rate_secs) { } void start() { running.store(true); timer = std::thread([&] { while (running.load()) { const auto start = std::chrono::steady_clock::now(); if (!paused.load()) { std::lock_guard lock(enemy_mutex); const auto num = static_cast(rng()) / static_cast(std::mt19937::max()); const auto dir = static_cast(rng()) / static_cast(std::mt19937::max()); for (auto &[center, radius, moving, alive]: enemies) { if (alive) continue; alive = true; radius = enemy_radius; if (num < 0.25) { center = {.x = static_cast(screen_width) / 2, .y = 0}; moving = {.x = (dir - 1) * 2, .y = 1}; } else if (0.25 <= num && num < 0.5) { center = {.x = static_cast(screen_width) / 2, .y = static_cast(screen_height)}; moving = {.x = (dir - 1) * 2, .y = -1}; } else if (0.5 <= num && num < 0.75) { center = {.x = 0, .y = static_cast(screen_height) / 2}; moving = {.x = 1, .y = (dir - 1) * 2}; } else { center = {.x = static_cast(screen_width), .y = static_cast(screen_height) / 2}; moving = {.x = -1, .y = (dir - 1) * 2}; } const auto speed_ratio = static_cast(rng()) / static_cast(std::mt19937::max()); const auto speed = speed_ratio * enemy_max_speed; moving = Vector2Scale(moving, speed); break; } } auto next = start + std::chrono::seconds(rate_secs); std::unique_lock lock{m}; cv.wait_until(lock, next, [&] { return !running.load(); }); } }); } ~EnemySpawner() { running.store(false); cv.notify_all(); timer.join(); } }; void spawn_tear(const Vector2 center, const Direction direction) { for (auto &[tear_center, tear_direction, tear_active, starting_center]: tears) { if (!tear_active) { tear_center = center; tear_direction = direction; tear_active = true; starting_center = center; break; } } } void update_tears() { for (auto &[center, direction, active, starting_center]: tears) { if (active) { for (const auto wall: walls) { if (CheckCollisionCircleRec(center, tear_radius, wall)) { active = false; break; } } switch (direction) { case UP: center.y -= tear_speed; break; case DOWN: center.y += tear_speed; break; case LEFT: center.x -= tear_speed; break; case RIGHT: center.x += tear_speed; break; } if (tear_range < Vector2Distance(center, starting_center)) { active = false; } } } } int main() { InitWindow(800, 600, "Isaac++"); SetTargetFPS(60); EnemySpawner spawner{3}; spawner.start(); player.x = 10; player.y = 10; player.width = 50; player.height = 50; while (!WindowShouldClose()) { if (IsWindowResized()) { screen_height = GetScreenHeight(); screen_width = GetScreenWidth(); } float delta_x = 0; float delta_y = 0; if (IsKeyDown(KEY_W)) delta_y -= player_speed; if (IsKeyDown(KEY_S)) delta_y += player_speed; if (IsKeyDown(KEY_A)) delta_x -= player_speed; if (IsKeyDown(KEY_D)) delta_x += player_speed; player.move(delta_x, delta_y); std::optional tear_direction; if (IsKeyDown(KEY_LEFT)) { tear_direction = LEFT; } if (IsKeyDown(KEY_UP)) { tear_direction = UP; } if (IsKeyDown(KEY_RIGHT)) { tear_direction = RIGHT; } if (IsKeyDown(KEY_DOWN)) { tear_direction = DOWN; } if (const auto now = GetTime(); last_fired + fire_rate < now && tear_direction.has_value()) { last_fired = now; spawn_tear(player.center(), tear_direction.value()); } update_tears(); BeginDrawing(); ClearBackground(RAYWHITE); DrawRectangleRec(player, BLUE); for (const auto wall: walls) { DrawRectangleRec(wall, GREEN); } for (const auto tear: tears) { if (tear.active) { DrawCircleV(tear.center, tear_radius, SKYBLUE); } } for (auto& enemy: enemies) { if (enemy.alive) { enemy.center = Vector2Add(enemy.center, enemy.moving); DrawCircleV(enemy.center, enemy.radius, RED); } } EndDrawing(); } CloseWindow(); return 0; }