Nimbin[12]?SDL & Graphics / mapcreator / commits / ef86efc

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

Add bird somehow landing, taking off, following player etc

Christian Immanuel · 2025-12-16 18:26 · ef86efc595d872388215432db7dd3fb3b7d97788

 src/bird/bird.cpp           | 360 +++++++++++++++++++++++++++++++++++++-------
 src/bird/bird.hpp           |  68 +++++++--
 src/character/character.cpp |   6 +-
 src/game/state_playing.cpp  |   1 -
 4 files changed, 364 insertions(+), 71 deletions(-)

diff --git a/src/bird/bird.cpp b/src/bird/bird.cpp
index 812400f..88ca72d 100644
--- a/src/bird/bird.cpp
+++ b/src/bird/bird.cpp
@@ -4,136 +4,388 @@
 #include <SDL3_image/SDL_image.h>
 #include <cstdint>
 #include <iostream>
+#include <algorithm>
+#include <cmath>
 
 #include "defaults/defaults.hpp"
+#include "map/map.hpp"
+
+/*
+static constexpr float TAKEOFF_TIME = 1.8f;
+static constexpr float LANDING_TIME = 1.8f;
+*/
+static constexpr float FLIGHT_HEIGHT = 80.0f;  // Consistent flying altitude
 
 SDL_Texture* Bird::texture;
 
-Bird::Bird(int x, int y)
+Bird::Bird(float x, float y)
     : pos_x(x),
-      pos_y(y) {
-    set_State(BirdState::IDLE);
+      pos_y(y),
+      target_pos{x, y},
+      decision_timer(0.0) {
+    set_State(BirdState::WALKING, 2.4);
 }
 
-bool
-Bird::Init(SDL_Renderer* renderer) {
+bool Bird::Init(SDL_Renderer* renderer) {
     texture = IMG_LoadTexture(renderer, "Bird/Spritesheet/Bird Spritesheet.png");
     SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
     return texture != nullptr;
-
 }
 
-void
-Bird::Update(double delta_ms) {
-    animation_timer += delta_ms;
+void Bird::Update(double delta_s, const SDL_FPoint& player_pos) {
+    std::cout << (int)state << " ISTART " << is_starting_to_fly << " ILAND " << is_landing << " XY: "  << pos_x << " " << pos_y << " DC " << decision_timer << " STT: " << state_timer << std::endl;
+    state_timer += delta_s;
+    decision_timer -= delta_s;
 
-    std::cout << "FT: " << current_frame << " " << animation_timer << std::endl;
-    if (animation_timer >= frame_duration_ms) {
-        animation_timer = 0;
-        current_frame += frame_step;
 
-        if (current_frame >= frame_count || current_frame < 0) {
-            if (state == BirdState::START_TO_FLY) {
-                set_State(BirdState::FLYING);
-                return;
-            }
+    // Make decisions when idle on ground
+    //if (landing_cooldown <= 0.0 && state == BirdState::IDLE && on_ground && decision_timer >= 2.0) {
+    if ( decision_timer <= 0 &&
+         state != BirdState::FLYING &&
+         state != BirdState::WALKING) {
+        Decide(player_pos);
+    }
 
-            if (state == BirdState::STOP_FLYING) {
-                set_State(BirdState::IDLE);
-                return;
+    /*
+    // Handle eating state
+    if (state == BirdState::EATING) {
+        eating_timer += delta_s;
+        if (eating_timer >= 3.0) {
+            set_State(BirdState::START_TO_FLY, 0.0);
+            PickFlyTarget(player_pos);
+            return;
+        }
+    }
+    */
+
+    // Determine movement speed
+    float speed = 0.0f;
+    if (state == BirdState::FLYING)
+        speed = FLY_SPEED;
+    else if (state == BirdState::WALKING)
+        speed = WALK_SPEED;
+
+    // Calculate distance to target
+    float dx = target_pos.x - pos_x;
+    float dy = target_pos.y - pos_y;
+    float dist = sqrtf(dx * dx + dy * dy);
+
+    // Handle flying movement
+    if (state == BirdState::FLYING && !is_landing && !is_starting_to_fly) {
+        if (dist > 2.0f) {
+            pos_x += (dx / dist) * speed * delta_s;
+            pos_y += (dy / dist) * speed * delta_s;
+            
+            // Update facing direction
+            if (fabsf(dx) > 0.01f) {
+                facing_left = (dx < 0.0f);
             }
+        } else {
+            // Reached target - begin landing
+            std::cout << "BIRD INIT LANDING!\n";
+            is_landing = true;
+            landing_progress = 0.0f;
+            landing_start_x = pos_x;
+            landing_start_y = pos_y;
+            landing_direction = facing_left ? -1.0f : 1.0f;  // Store current flight direction
+            landing_prev_x_offset = 0.0f;
+            landing_prev_y_offset = 0.0f;
+        }
+    }
+    // Handle walking movement
+    else
+    if (state == BirdState::WALKING) {
+        std::cout << "DIST: " << dist << " SP " << speed << std::endl;
+        if (dist > 0.4) {
+            float move = speed * delta_s;
+            if (move > dist) move = dist;
+            pos_x += (dx / dist) * move;
+            pos_y += (dy / dist) * move;
 
-            current_frame = 0;
+            if (fabsf(dx) > 0.01f) facing_left = (dx < 0.0f);
+        } else {
+            set_State(BirdState::IDLE, 0.0);
+            return;
+        }
+    }
+    else
+    if (is_starting_to_fly) {
+
+        float overall_dx = target_pos.x - pos_x;
+        takeoff_direction = (overall_dx >= 0.0f) ? 1.0f : -1.0f;
+
+        float TAKEOFF_SPEED = 35.0f;
+
+        takeoff_progress += TAKEOFF_SPEED * delta_s;
+
+        float t = takeoff_progress / FLIGHT_HEIGHT;
+
+        // Bias circle to start opposite target
+        float circle_direction = -takeoff_direction;
+
+        float angle, x_offset;
+        if (t < 0.5f) {
+            angle = (t / 0.5f) * (M_PI / 2.0f);
+            x_offset = sinf(angle) * 30.0f * circle_direction;
+        } else {
+            angle = ((t - 0.5f) / 0.5f) * (M_PI / 2.0f);
+            x_offset = (1.0f + sinf(angle)) * 30.0f * circle_direction;
         }
 
+        float full_angle = t * M_PI;
+        float y_offset = -sinf(full_angle) * 20.0f;
+
+        // Delta
+        float x_offset_delta = x_offset - takeoff_prev_x_offset;
+        takeoff_prev_x_offset = x_offset;
+        float y_offset_delta = -((takeoff_progress + y_offset) - takeoff_prev_y_offset);
+        takeoff_prev_y_offset = takeoff_progress + y_offset;
+
+        std::cout << "FACIN A: " << facing_left << std::endl;
+        facing_left = ( pos_x+x_offset_delta - pos_x < 0.0f);
+        std::cout << "FACIN B: " << facing_left << std::endl;
+
+        pos_x += x_offset_delta;
+        pos_y += y_offset_delta;
+
+        // If finished takeoff, align for straight flight
+        if (takeoff_progress >= FLIGHT_HEIGHT) {
+            is_starting_to_fly = false;
+
+            // Reset offsets
+            takeoff_prev_x_offset = 0.0f;
+            takeoff_prev_y_offset = 0.0f;
+
+            facing_left = (target_pos.x - pos_x < 0.0f);
+        }
+
+        std::cout << "DEL: " << x_offset_delta << " " << y_offset_delta << std::endl;
+    }
+    // Handle landing animation with circular arc (position-based)
+    else if (is_landing) {
+
+        landing_progress += speed * delta_s;
+
+        if (landing_progress >= FLIGHT_HEIGHT) {
+        // if (fabs(target_pos.y - landing_start_y) >= FLIGHT_HEIGHT+1) {
+            std::cout << "STOP FLYING! START LANDING" << std::endl;
+            set_State(BirdState::STOP_FLYING, 0.0);
+            return;
+        }
+
+        float t = landing_progress / FLIGHT_HEIGHT;
+
+        float angle;
+        float x_offset;
+        if (t < 0.5f) {
+            angle = (t / 0.5f) * (M_PI / 2.0f);
+            x_offset = sinf(angle) * 30.0f * landing_direction;
+        } else {
+            angle = ((t - 0.5f) / 0.5f) * (M_PI / 2.0f);
+            x_offset = (1.0f - sinf(angle)) * 30.0f * landing_direction;
+        }
+
+        float full_angle = t * M_PI;
+        float y_offset = -sinf(full_angle) * 20.0f;
+
+        float x_offset_delta = x_offset - landing_prev_x_offset;
+        landing_prev_x_offset = x_offset;
+        float y_offset_delta = (landing_progress + y_offset) - landing_prev_y_offset;
+        landing_prev_y_offset = landing_progress + y_offset;
+
+        float dx = pos_x + x_offset_delta - pos_x;
+        if (fabsf(dx) > 0.01f) {
+            facing_left = (dx < 0.0f);
+        }
+        pos_x += x_offset_delta;
+        pos_y += y_offset_delta;
+    }
+
+    // Update animation frames
+    animation_timer += delta_s;
+    if (animation_timer >= frame_duration_seconds) {
+        animation_timer = 0.0;
+        current_frame += frame_step;
+        
+        if (reverse_animation) {
+            if (current_frame < 0)
+                current_frame = 0;
+        } else {
+            if (current_frame >= frame_count)
+                current_frame = 0;
+        }
+        
         src_rect.x = current_frame * tile_size;
     }
+    
 }
 
-void
-Bird::Render(SDL_Renderer* renderer) {
+void Bird::Render(SDL_Renderer* renderer) {
     SDL_FRect dst;
-    dst.x = Scale(static_cast<float>(pos_x));
-    dst.y = Scale(static_cast<float>(pos_y));
+    dst.x = Scale(pos_x + Map::offset.x);
+    dst.y = Scale(pos_y + Map::offset.y);
     dst.w = Scale(tile_size);
     dst.h = Scale(tile_size);
 
-    SDL_RenderTexture(renderer, texture, &src_rect, &dst);
+    SDL_FlipMode flip = facing_left ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL;
+    SDL_RenderTextureRotated(renderer, texture, &src_rect, &dst, 0.0, nullptr, flip);
 }
 
-void
-Bird::set_Position(SDL_FPoint point) {
+void Bird::set_Position(SDL_FPoint point) {
     pos_x = point.x;
     pos_y = point.y;
 }
 
-void
-Bird::set_State(BirdState new_state) {
+void Bird::set_State(BirdState new_state, double dec_timer) {
+
+    decision_timer = dec_timer;
+
     if (state == new_state)
         return;
 
     state = new_state;
-    std::cout << "SET BIRD STATE\n";
-    animation_timer = 0;
-
+    state_timer = 0.0f;
+    transition_timer = 0.0f;
+    animation_timer = 0.0;
     reverse_animation = false;
     frame_step = 1;
 
     switch (state) {
         case BirdState::IDLE:
             current_frame = 0;
-            set_Animation(0, 2, 0.05);
+            set_Animation(0, 2, 0.25);
             break;
 
         case BirdState::FLYING:
             current_frame = 0;
-            set_Animation(1, 8, 0.08);
+            set_Animation(1, 8, 0.06);
             break;
 
         case BirdState::WALKING:
             current_frame = 0;
-            set_Animation(2, 3, 0.02);
+            set_Animation(2, 3, 0.15);
             break;
 
         case BirdState::EATING:
             current_frame = 0;
-            set_Animation(3, 3, 0.03);
+            //eating_timer = 0.0;
+            set_Animation(3, 3, 0.15);
             break;
 
-        case BirdState::START_TO_FLY:
+        case BirdState::START_TO_FLY: {
             current_frame = 0;
-            set_Animation(4, 8, 0.01);
+            is_starting_to_fly = true;
+            set_Animation(4, 8, 0.06);
+            decision_timer = 8*0.06;
+            takeoff_progress = 0.0f;
+            takeoff_prev_x_offset = 0.0f;
+            takeoff_prev_y_offset = 0.0f;
             break;
-
-        case BirdState::STOP_FLYING:
-            set_Animation(4, 8, 0.01);
-            reverse_animation = true;
+        }
+        case BirdState::STOP_FLYING: {
             frame_step = -1;
-            current_frame = frame_count - 1;
+            reverse_animation = true;
+            set_Animation(4, 8, 0.06);
+            decision_timer = 8*0.06;
+            is_landing = false;
+            takeoff_direction = facing_left ? -1.0f : 1.0f;
             break;
-
-        case BirdState::ATTACK:
+        }
+        case BirdState::ATTACK: {
             current_frame = 0;
             set_Animation(1, 8, 0.06);
             break;
+        }
     }
 
     src_rect.x = current_frame * tile_size;
 }
 
-BirdState
-Bird::get_State() const {
+BirdState Bird::get_State() const {
     return state;
 }
 
-
-void
-Bird::set_Animation(int row, int frames, double frame_time) {
+void Bird::set_Animation(int row, int frames, double frame_time) {
     frame_count = frames;
-    frame_duration_ms = frame_time;
+    frame_duration_seconds = frame_time;
 
-    src_rect.x = 0;
+    src_rect.x = current_frame * tile_size;
     src_rect.y = row * tile_size;
     src_rect.w = tile_size;
     src_rect.h = tile_size;
 }
+
+void Bird::Decide(const SDL_FPoint& player_pos) {
+
+    // Calculate distance to player
+    float dx = pos_x - player_pos.x;
+    float dy = pos_y - player_pos.y;
+    float dist_sq = dx * dx + dy * dy;
+
+    // If too far, fly closer
+    if ( dist_sq > MAX_RADIUS * MAX_RADIUS &&
+         state != BirdState::FLYING &&
+         state != BirdState::STOP_FLYING &&
+         state != BirdState::START_TO_FLY) {
+        std::cout << "BIRD TARGET TOO FAR\n";
+        set_State(BirdState::START_TO_FLY, 0.0);
+        PickFlyTarget(player_pos);
+        return;
+    }
+    if (state == BirdState::START_TO_FLY) {
+        set_State(BirdState::FLYING, 0.0);
+        return;
+    }
+
+    // Random behavior when close to player
+    int   choice   = rand() % 100;
+    float duration = rand() %  80 * 0.1f + 1.1f;
+
+    if (state == BirdState::STOP_FLYING) {
+        set_State(BirdState::IDLE, duration);
+        return;
+    }
+
+    if (choice < 25) {
+        set_State(BirdState::EATING, duration);
+    }
+    else if (choice < 50) {
+        set_State(BirdState::WALKING, duration);
+        PickGroundTarget(player_pos);
+    }
+    else if (choice < 75) {
+        set_State(BirdState::IDLE, duration);
+    }
+    else {
+        set_State(BirdState::START_TO_FLY, 0.0);
+        PickFlyTarget(player_pos);
+    }
+}
+
+void Bird::PickFlyTarget(const SDL_FPoint& player_pos) {
+    const float MIN_DIST = MAX_RADIUS; // minimum distance from current position
+
+    float angle, radius, dx, dy, dist_sq;
+    do {
+        angle = (rand() % 628) / 100.0f;  // 0 to ~2*PI
+        radius = MIN_RADIUS + (rand() % static_cast<int>(MAX_RADIUS - MIN_RADIUS));
+
+        target_pos.x = player_pos.x + cosf(angle) * radius;
+        target_pos.y = player_pos.y - FLIGHT_HEIGHT + sinf(angle) * (radius * 0.3f);
+
+        // Clamp within map bounds
+        target_pos.x = std::clamp(target_pos.x, 0.0f, static_cast<float>(Map::size.x * Tile::tile_size));
+        target_pos.y = std::clamp(target_pos.y, 0.0f, static_cast<float>(Map::size.y * Tile::tile_size));
+
+        dx = target_pos.x - pos_x;
+        dy = target_pos.y - pos_y;
+        dist_sq = dx * dx + dy * dy;
+
+    } while (dist_sq < MIN_DIST * MIN_DIST);  // ensure minimum distance
+}
+
+void Bird::PickGroundTarget(const SDL_FPoint& player_pos) {
+    float offset = static_cast<float>((rand() % 64) - 32);  // -32 to +32
+    target_pos.x = player_pos.x + offset;
+    target_pos.y = player_pos.y;
+}
diff --git a/src/bird/bird.hpp b/src/bird/bird.hpp
index ed26f86..c683fe3 100644
--- a/src/bird/bird.hpp
+++ b/src/bird/bird.hpp
@@ -1,31 +1,39 @@
 #pragma once
 
+static constexpr float MIN_RADIUS = 124.f;
+static constexpr float MAX_RADIUS = 164.f;
+static constexpr float FLY_SPEED  =  42.f;
+static constexpr float WALK_SPEED =  10.f;
+
 
 enum class BirdState {
-    IDLE,
-    FLYING,
-    WALKING,
-    EATING,
-    START_TO_FLY,
-    STOP_FLYING,
-    ATTACK
+    IDLE,       // 0
+    FLYING,     // 1
+    WALKING,    // 2
+    EATING,     // 3
+    START_TO_FLY,   // 4
+    STOP_FLYING,    // 5
+    ATTACK      // 6
 };
 
 #include <SDL3/SDL.h>
 #include <cstdint>
 
-class Bird
-{
+class Bird {
 public:
-    Bird(int x, int y);
+    Bird(float x, float y);
 
     static bool Init(SDL_Renderer*);
 
-    void Update(double delta_ms);
+    void Update(double delta_s, const SDL_FPoint& player_pos);
+    void Decide(const SDL_FPoint& player_pos);
+    void PickFlyTarget(const SDL_FPoint& player_pos);
+    void PickGroundTarget(const SDL_FPoint& player_pos);
+
 
     void Render(SDL_Renderer* renderer);
 
-    void set_State(BirdState new_state);
+    void set_State(BirdState new_state, double decision_timer);
     void set_Position(SDL_FPoint);
 
     BirdState get_State() const;
@@ -38,6 +46,40 @@ private:
     float pos_x = 0;
     float pos_y = 0;
 
+    SDL_FPoint velocity{0.f, 0.f};
+    SDL_FPoint target_pos{0.f, 0.f};
+
+    double state_timer      = 0.0;
+    double decision_timer   = 0.0;
+    double eating_timer     = 0.0;
+
+    bool is_landing  = false;
+    bool is_starting_to_fly = false;
+    float landing_progress = 0.0f;
+    float takeoff_progress = 0.0f;
+    bool facing_left = true;
+    float vertical_offset = 0.0f;
+    float transition_timer = 0.0f;
+
+    // Bird class private members
+    float landing_start_y = 0.0f;   // store Y when landing starts
+
+    float landing_start_x = 0.0f;       // Store X when landing starts
+    float landing_direction = 1.0f;      // Store flight direction for landing arc
+    float landing_prev_x_offset = 0.0f;
+    float landing_prev_y_offset = 0.0f;
+
+
+    float takeoff_direction = 1.0f;      // Store flight direction for takeoff arc
+    float takeoff_prev_x_offset = 0.0f;
+    float takeoff_prev_y_offset = 0.0f;
+
+
+
+
+    bool reached_target = false;
+    bool init_start_to_fly = false;
+
     static constexpr int tile_size = 16;
 
     BirdState state = BirdState::IDLE;
@@ -48,6 +90,6 @@ private:
     bool reverse_animation = false;
     int frame_step = 1;
 
-    double frame_duration_ms = 200;
+    double frame_duration_seconds = 200;
     double animation_timer = 0;
 };
diff --git a/src/character/character.cpp b/src/character/character.cpp
index 8aea8a9..0127f60 100644
--- a/src/character/character.cpp
+++ b/src/character/character.cpp
@@ -70,7 +70,7 @@ Character::Init(SDL_Renderer* renderer) {
     selected_item = inventory->hotbar_hands;
 
     bird = new Bird(x, y);
-    bird->set_State(BirdState::FLYING);
+    bird->set_State(BirdState::WALKING, 1.4);
 
 
     // load_MapTiles animations
@@ -1614,8 +1614,8 @@ void Character::handle_Move() {
 
 
 void Character::Update() {
-    bird->set_Position({x,y});
-    bird->Update(Default::delta_time);
+    //bird->set_Position({x,y});
+    bird->Update(Default::delta_time, collider_world);
 
     if ( is_placing_build)
         BuildingData::update_ActiveBuildData(collider_world.x, collider_world.y - collider.h);
diff --git a/src/game/state_playing.cpp b/src/game/state_playing.cpp
index 83c62f5..ab7cf88 100644
--- a/src/game/state_playing.cpp
+++ b/src/game/state_playing.cpp
@@ -7,7 +7,6 @@
 #include "game/game.hpp"
 #include "defaults/ui.hpp"
 #include "character/message.hpp"
-#include "bird/bird.hpp"
 
 StatePlaying::StatePlaying() { 
     std::cout << "CREATE STATE_PLAYING\n";