add item spawner

This commit is contained in:
2025-12-01 10:38:44 -05:00
parent 49751ea09e
commit d6dff2a539
6 changed files with 221 additions and 79 deletions

132
main.cpp
View File

@@ -5,6 +5,8 @@
#include <raylib.h>
#include <thread>
#include "rng.h"
#include "spawner.h"
#include "types.h"
float screen_width = 800;
@@ -24,26 +26,10 @@ float tear_radius = 10.0f;
double last_fired = 0;
double fire_rate = .5;
std::mutex enemy_mutex;
float enemy_max_speed = 3.0;
float enemy_radius = 25.0;
struct Rng {
static std::random_device dev;
static std::mt19937 rng;
static float generate() {
return static_cast<float>(rng()) / static_cast<float>(std::mt19937::max());
}
static float generate(const uint32_t max) {
return generate() * static_cast<float>(max);
}
static float generate(const int min, const int max) {
return generate() * (static_cast<float>(max) - static_cast<float>(min)) + static_cast<float>(min);
}
};
float item_radius = 10.0;
std::random_device Rng::dev{};
std::mt19937 Rng::rng{dev()};
@@ -91,50 +77,6 @@ void Player::move(const float delta_x, const float delta_y) {
}
}
template <typename T, size_t N>
struct Spawner {
std::mutex mutex;
std::condition_variable cv;
std::atomic<bool> running;
std::atomic<bool> paused;
std::optional<std::thread> timer;
long long rate_secs;
T values[N];
explicit Spawner(const long long rate_secs)
: rate_secs(rate_secs) {
}
virtual void spawn() {}
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(mutex);
spawn();
}
auto next = start + std::chrono::seconds(rate_secs);
std::unique_lock lock{mutex};
cv.wait_until(lock, next, [&] { return !running.load(); });
}
});
}
virtual ~Spawner() {
running.store(false);
cv.notify_all();
if (timer) {
timer.value().join();
}
}
};
struct EnemySpawner final : Spawner<Enemy, 100> {
explicit EnemySpawner(const long long rate_secs)
: Spawner<Enemy, 100>(rate_secs) {
@@ -172,6 +114,30 @@ struct EnemySpawner final : Spawner<Enemy, 100> {
EnemySpawner enemy_spawner{2};
struct ItemSpawner final : Spawner<Item, 100> {
explicit ItemSpawner(const long long rate_secs) : Spawner<Item, 100>(rate_secs) {
}
void spawn() override {
const auto item_type = static_cast<ItemType>(Rng::generate(ItemType::ITEM_TYPE_COUNT));
const auto x = Rng::generate(static_cast<uint32_t>(screen_width));
const auto y = Rng::generate(static_cast<uint32_t>(screen_height));
for (auto &item: this->values) {
if (item.active) continue;
item.active = true;
item.type = item_type;
item.center = Vector2{x, y};
item.radius = item_radius;
break;
}
}
};
ItemSpawner item_spawner{5};
void spawn_tear(const Vector2 center, const Direction direction) {
for (auto &[tear_center, tear_direction, tear_active, starting_center]: tears) {
if (!tear_active) {
@@ -194,13 +160,13 @@ void update_tears() {
}
}
for (auto &enemy: enemy_spawner.values) {
enemy_spawner.for_each([&center, &active](auto &enemy) {
if (enemy.alive && CheckCollisionCircles(center, tear_radius, enemy.center, enemy.radius)) {
active = false;
enemy.alive = false;
score++;
}
}
});
switch (direction) {
case UP:
@@ -230,6 +196,7 @@ int main() {
enemy_spawner.start();
item_spawner.start();
player.width = 50;
player.height = 50;
@@ -296,26 +263,33 @@ int main() {
}
}
{
std::lock_guard enemy_guard{enemy_mutex};
for (auto &[center, radius, speed, alive]: enemy_spawner.values) {
if (alive) {
center = Vector2MoveTowards(center, player.center(), speed);
DrawCircleV(center, radius, RED);
enemy_spawner.for_each([now](auto &enemy) {
if (enemy.alive) {
enemy.center = Vector2MoveTowards(enemy.center, player.center(), enemy.speed);
DrawCircleV(enemy.center, enemy.radius, RED);
if (center.x < 0 || screen_width < center.x ||
center.y < 0 || screen_height < center.y) {
alive = false;
}
if (enemy.center.x < 0 || screen_width < enemy.center.x ||
enemy.center.y < 0 || screen_height < enemy.center.y) {
enemy.alive = false;
}
if (CheckCollisionCircleRec(center, radius, player.rect()) &&
player.last_hit + player_invulnerability < now) {
player.lives--;
player.last_hit = now;
}
if (CheckCollisionCircleRec(enemy.center, enemy.radius, player.rect()) &&
player.last_hit + player_invulnerability < now) {
player.lives--;
player.last_hit = now;
}
}
}
});
item_spawner.for_each([](auto &item) {
if (!item.active) return;
DrawCircleV(item.center, item.radius, item.color());
if (CheckCollisionCircleRec(item.center, item.radius, player.rect())) {
item.active = false;
}
});
std::format_to(std::back_inserter(score_text_buffer), "Score: {}", score);
DrawText(score_text_buffer.c_str(), 50, static_cast<int>(screen_height) - 100, 20, BLACK);