Nimbin[12]?SDL & Graphics / mapcreator / commits / 0a35a7c

mapcreator git · master

SDL3 2.5D game and engine using assets, with map editor

sdl3 c++ game engine map-editor cmake · first commit 2025-10-31 · last commit 2026-09-07 (1 month ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-spieleentwicklung/map_creator

C++ 99.3%
git clone https://git.christianimmanuel.de/sdl-graphics/mapcreator.gitwget https://git.christianimmanuel.de/sdl-graphics/mapcreator/archive/mapcreator.tar.gz

Fix enemy thread pool

Christian Immanuel · 2026-02-15 16:41 · 0a35a7cb5d0efd165096ccb4fd3989753bf8c1b3

 src/character/character.cpp |  10 +++-
 src/character/character.hpp |   4 +-
 src/character/enemy.cpp     | 141 +++++++++++++++++++++++++++++---------------
 src/character/enemy.hpp     |  27 ++++++++-
 src/inventory/inventory.hpp |   8 ++-
 src/inventory/item.cpp      |   6 +-
 6 files changed, 137 insertions(+), 59 deletions(-)

diff --git a/src/character/character.cpp b/src/character/character.cpp
index 524279e..d0aa87c 100644
--- a/src/character/character.cpp
+++ b/src/character/character.cpp
@@ -816,7 +816,7 @@ Character::attack_Enemy() {
     Enemy* enemy = Enemy::is_InEnemyReach(weaponrect);
 
     if (enemy && enemy->stats.health > 0)
-        enemy->getting_Hit(stats.demage);
+        enemy->getting_Hit(damage_Get());
 
     attacking_logic.has_attacked = true;
 }
@@ -1168,6 +1168,14 @@ Character::stamina_StopReg() {
     stamina_regen_logic.delay_is  = 0;
 }
 
+float
+Character::damage_Get() {
+    if (!using_Weapon())
+        return 0.0f;
+    std::cout << "DAMA " <<  static_cast<InventoryItem*>(selected_item)->damage << std::endl;
+    return static_cast<InventoryItem*>(selected_item)->damage;
+}
+
 
 void
 Character::thirst_Drain() {
diff --git a/src/character/character.hpp b/src/character/character.hpp
index 4d550d3..9f072c2 100644
--- a/src/character/character.hpp
+++ b/src/character/character.hpp
@@ -53,7 +53,7 @@ enum class CharacterStatus {
 
 struct CharacterMoveLogic {
     SDL_FPoint move_to_position;
-    bool automove;
+    bool automove = false;
 };
 
 struct CharacterCollectingLogic {
@@ -256,6 +256,8 @@ class Character {
     void  stamina_StartReg();
     void  stamina_StopReg();
 
+    float damage_Get();
+
     TileObject* find_ClosestObject(SDL_Point* tile_found, ObjectCategory category = ObjectCategory::NONE);
     void collect_Start(TileObject* map_object_p, SDL_Point tile_point);
     void collect_Object();
diff --git a/src/character/enemy.cpp b/src/character/enemy.cpp
index a445ea4..050ab78 100644
--- a/src/character/enemy.cpp
+++ b/src/character/enemy.cpp
@@ -14,6 +14,17 @@ SDL_FPoint Enemy::colider_default = {7, 7};
 EnemyAnimation Enemy::animation_all;
 size_t Enemy::tile_size = 64;
 
+// Persistent thread pool
+std::vector<SDL_Thread*> Enemy::workers;
+std::vector<WorkerData> Enemy::job_queue;
+std::mutex Enemy::queue_mutex;
+std::condition_variable Enemy::queue_cv;
+
+bool Enemy::stop_threads = false;
+
+int Enemy::jobs_remaining = 0;
+std::condition_variable Enemy::jobs_done_cv;
+
 Enemy::Enemy(WeaponType weapon_type, WeaponMaterial weapon_material, SDL_FPoint position, EnemyStats status)
     : weapon(weapon_type, weapon_material) {
         x = position.x;
@@ -73,6 +84,8 @@ Enemy::init_All(SDL_Renderer* renderer) {
     animation_all.death[WalkDirection::DOWN] = Animation::load(renderer, "Pixel_Crawler/Entities/Mobs/Skeleton Crew/Skeleton - Base/Death/Death-Sheet_processed.png", tile_size, tile_size, "None", false, false);
     animation_all.death[WalkDirection::SIDE] = Animation::load(renderer, "Pixel_Crawler/Entities/Mobs/Skeleton Crew/Skeleton - Base/Death/Death-Sheet_processed.png", tile_size, tile_size, "None", false, false);
     animation_all.death[WalkDirection::UP]   = Animation::load(renderer, "Pixel_Crawler/Entities/Mobs/Skeleton Crew/Skeleton - Base/Death/Death-Sheet_processed.png", tile_size, tile_size, "None", false, false);
+
+    init_Workers(10);
 }
 
 
@@ -295,11 +308,6 @@ Enemy::is_InEnemyReach(const SDL_FRect& rect) {
     return nullptr;
 }
 
-struct WorkerData {
-    Enemy* enemy;
-    const std::vector<bool>* grid;
-};
-
 /*
 static inline double ms_since(Uint64 start) {
     static const double freq = static_cast<double>(SDL_GetPerformanceFrequency());
@@ -307,72 +315,106 @@ static inline double ms_since(Uint64 start) {
 }
 */
 
-void Enemy::run_GetMovesThreads() {
-    //Uint64 t0 = SDL_GetPerformanceCounter();
+int EnemyWorkerThread(void*) {
+    std::cout << "WORK!\n";
+    while (true) {
+        std::unique_lock<std::mutex> lock(Enemy::queue_mutex);
+        Enemy::queue_cv.wait(lock, [] {
+            return Enemy::stop_threads || !Enemy::job_queue.empty();
+        });
 
-    std::vector<bool> grid = Map::tile_collission_map;
-    //std::cout << "grid copy: " << ms_since(t0) << " ms\n";
+        if (Enemy::stop_threads && Enemy::job_queue.empty())
+            return 0;
 
-    //Uint64 t1 = SDL_GetPerformanceCounter();
+        WorkerData job = Enemy::job_queue.back();
+        Enemy::job_queue.pop_back();
+        lock.unlock();
 
-    std::set<int> checks = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 32, 64, 128, 256};
-    for (Enemy* enemy : Enemy::enemies) {
-        if (enemy->moving_logic.move_type != EnemyMovingType::WALK ||
-            !checks.count(enemy->next_moves.size()))
-            continue;
+        job.enemy->set_NextMoves(*job.grid);
 
-        int tx = static_cast<int>(enemy->collider_world.x / Tile::tile_collider_size);
-        int ty = static_cast<int>(enemy->collider_world.y / Tile::tile_collider_size);
+        std::lock_guard<std::mutex> finish_lock(Enemy::queue_mutex);
+        if (--Enemy::jobs_remaining == 0)
+            Enemy::jobs_done_cv.notify_one();
+    }
+}
 
-        if (tx < 0 || ty < 0 ||
-            tx >= static_cast<int>(Map::size.x * Tile::tile_collider_size) ||
-            ty >= static_cast<int>(Map::size.y * Tile::tile_collider_size))
-            continue;
+void Enemy::init_Workers(int num_threads) {
+    stop_threads = false;
+    jobs_remaining = 0;
+    for (int i = 0; i < num_threads; ++i) {
+        SDL_Thread* th = SDL_CreateThread(EnemyWorkerThread, "EnemyWorker", nullptr);
+        workers.push_back(th);
+    }
+}
 
-        size_t id = static_cast<size_t>(ty * (Map::size.x * Tile::tile_collider_size) + tx);
-        grid[id] = true;
+void Enemy::shutdown_Workers() {
+    {
+        std::lock_guard<std::mutex> lock(queue_mutex);
+        stop_threads = true;
     }
+    queue_cv.notify_all();
 
-    //std::cout << "grid marking: " << ms_since(t1) << " ms\n";
-    //Uint64 t2 = SDL_GetPerformanceCounter();
+    for (SDL_Thread* th : workers) {
+        SDL_WaitThread(th, nullptr);
+    }
+    workers.clear();
+}
 
-    std::vector<WorkerData> jobdata;
-    jobdata.reserve(Enemy::enemies.size());
+void Enemy::run_GetMovesThreads()
+{
+    std::vector<bool> grid = Map::tile_collission_map;
 
-    std::vector<SDL_Thread*> workers;
-    workers.reserve(Enemy::enemies.size());
+    std::set<int> checks = {0,1,2,3,4,5,6,7,8,12,16,32,64,128,256};
 
+    // ---- First pass: mark grid exactly like old version ----
     for (Enemy* e : Enemy::enemies) {
-        if (e->point_in_reach)
+        if (e->moving_logic.move_type != EnemyMovingType::WALK ||
+            !checks.count(e->next_moves.size()))
             continue;
 
-        jobdata.push_back({ e, &grid });
+        int tx = static_cast<int>(e->collider_world.x / Tile::tile_collider_size);
+        int ty = static_cast<int>(e->collider_world.y / Tile::tile_collider_size);
 
-        SDL_Thread* th = SDL_CreateThread(
-            [](void* data) -> int {
-                auto* d = static_cast<WorkerData*>(data);
-                d->enemy->set_NextMoves(*d->grid);
-                return 0;
-            },
-            "EnemyWorker",
-            &jobdata.back()
-        );
+        if (tx < 0 || ty < 0 ||
+            tx >= static_cast<int>(Map::size.x * Tile::tile_collider_size) ||
+            ty >= static_cast<int>(Map::size.y * Tile::tile_collider_size))
+            continue;
 
-        workers.push_back(th);
+        size_t id = static_cast<size_t>(
+            ty * (Map::size.x * Tile::tile_collider_size) + tx);
+
+        grid[id] = true;
     }
 
-    //std::cout << "thread creation: " << ms_since(t2) << " ms\n";
-    //Uint64 t3 = SDL_GetPerformanceCounter();
+    auto grid_ptr = std::make_shared<std::vector<bool>>(std::move(grid));
 
-    for (SDL_Thread* th : workers)
-        SDL_WaitThread(th, nullptr);
+    // ---- Wait for previous frame to finish ----
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex);
+        jobs_done_cv.wait(lock, [] { return jobs_remaining == 0; });
+    }
 
-    //std::cout << "thread execution + join: " << ms_since(t3) << " ms\n";
-    //std::cout << "total: " << ms_since(t0) << " ms\n";
-}
+    // ---- Push jobs ----
+    {
+        std::lock_guard<std::mutex> lock(queue_mutex);
 
+        for (Enemy* e : Enemy::enemies) {
+            if (e->point_in_reach)
+                continue;
 
+            job_queue.push_back({ e, grid_ptr });
+            ++jobs_remaining;
+        }
+    }
+
+    queue_cv.notify_all();
 
+    // ---- Wait like old SDL_WaitThread loop ----
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex);
+        jobs_done_cv.wait(lock, [] { return jobs_remaining == 0; });
+    }
+}
 
 
 
@@ -385,6 +427,7 @@ void Enemy::Decide(Character& player) {
     //size_t dc_y = static_cast<size_t>(abs(d_y));
     EnemyMovingType r = (move_focus != EnemyMovingType::NONE) ? move_focus: static_cast<EnemyMovingType>(1 + (rand() % static_cast<int>(EnemyMovingType::LAST)-1));
 
+    r = EnemyMovingType::CHARACTER;
     set_MoveLogic(r, player);
     std::cout << "DEB " << (int) status << std::endl;
 }
@@ -989,6 +1032,7 @@ Enemy::Update(Character& player) {
     if (move_focus == EnemyMovingType::CHARACTER) {
         moving_logic.duration -= Default::delta_time;
         // std::cout << moving_logic.duration << std::endl;
+        std::cout << "NEXT MOVES: " << next_moves.size() << std::endl;
         if ((point_moved && moving_logic.duration <= 0) || moving_logic.move_type != EnemyMovingType::CHARACTER) {
             set_MoveLogic(moving_logic.move_type, player);
             return;
@@ -1087,6 +1131,7 @@ Enemy::attack_Stop() {
 void
 Enemy::getting_Hit(float demage) {
     // if (status != EnemyStatus::HIT) set_Status(EnemyStatus::HIT);
+    std::cout << "ENEM " << stats.health << " " << demage << std::endl;
     stats.health -= demage;
 
 #ifdef DEBUG_BUILD
diff --git a/src/character/enemy.hpp b/src/character/enemy.hpp
index a8b17ca..7cc893c 100644
--- a/src/character/enemy.hpp
+++ b/src/character/enemy.hpp
@@ -1,6 +1,8 @@
 #pragma once
 
 #include <algorithm>
+#include <mutex>
+#include <condition_variable>
 
 #include "animation.hpp"
 #include "objects/object.hpp"
@@ -77,6 +79,11 @@ struct EnemyMovingLogic {
     SDL_FPoint moving_distance = {0,0};
 };
 
+struct WorkerData {
+    Enemy* enemy;
+    std::shared_ptr<std::vector<bool>> grid; // use shared_ptr for safety
+};
+
 class Enemy {
     float  speed;
     float  run; // multiplier
@@ -145,6 +152,23 @@ class Enemy {
     SDL_Point tile_world;
     std::vector<SDL_Point> tiles_in_reach;
 
+    // START THREAD
+    static std::vector<SDL_Thread*> workers;
+    static std::vector<WorkerData> job_queue;
+    static std::mutex queue_mutex;
+    static std::condition_variable queue_cv;
+
+    static bool stop_threads;
+
+    // Job completion tracking
+    static int jobs_remaining;
+    static std::condition_variable jobs_done_cv;
+
+    static void init_Workers(int num_threads);
+    static void shutdown_Workers();
+    static void run_GetMovesThreads();
+    // END THREAD
+
 
     void
     Init();
@@ -167,9 +191,6 @@ class Enemy {
     static Enemy*
     is_InEnemyReach(const SDL_FRect& rect);
 
-    static void
-    run_GetMovesThreads();
-
     void
     Decide(Character& player);
 
diff --git a/src/inventory/inventory.hpp b/src/inventory/inventory.hpp
index e87b719..76810e0 100644
--- a/src/inventory/inventory.hpp
+++ b/src/inventory/inventory.hpp
@@ -33,9 +33,10 @@ struct Inventory_s {
 struct InventoryItem : Inventory_s {
     ItemType type = ItemType::HAND;
     int health_current = 0;
-    int health_max = 0;
-    float speed = 0;
-    int level = 0;
+    int health_max     = 0;
+    float speed        = 0;
+    int level          = 0;
+    float damage       = 0;
     InventoryItem(size_t d, ItemType t)
         : Inventory_s(d, InventoryObjectType::ITEM),
           type(t)
@@ -44,6 +45,7 @@ struct InventoryItem : Inventory_s {
         health_current  = info.base_health;
         health_max      = info.base_health;
         speed           = info.base_speed;
+        damage          = info.base_damage;
         level           = 1;
         equipement_slot = info.equipement_slot;
     }
diff --git a/src/inventory/item.cpp b/src/inventory/item.cpp
index f53eded..6d2c46c 100644
--- a/src/inventory/item.cpp
+++ b/src/inventory/item.cpp
@@ -58,7 +58,7 @@ namespace Item {
             ItemType::SWORD_WOOD,
             "Sword wood",
             { {ResourceType::WOOD, 6} },
-            1,      // base_damage
+            2,      // base_damage
             0,      // base_armor
             20,     // base_health
             0.8,    // base_speed
@@ -76,7 +76,7 @@ namespace Item {
             ItemType::SWORD_STONE,
             "Sword stone",
             { {ResourceType::WOOD, 2}, {ResourceType::STONE, 4} },
-            3,      // base_damage
+            4,      // base_damage
             0,      // base_armor
             30,     // base_health
             1.6,    // base_speed
@@ -94,7 +94,7 @@ namespace Item {
             ItemType::WOODAXE_STONE,
             "Wood Axe made of stone",
             { {ResourceType::WOOD, 3}, {ResourceType::STONE, 5} },
-            1,      // base_damage
+            2,      // base_damage
             0,      // base_armor
             30,     // base_health
             1.5,    // base_speed