Nimbin[12]?SDL & Graphics / mapcreator / commits / 941badf

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 propper death

Christian Immanuel · 2026-02-08 11:09 · 941badf0543dd7256a3fa14e858e83e6a577129e

 src/character/animation.cpp      |  20 ++++-
 src/character/animation.hpp      |   3 +
 src/character/character.cpp      | 176 ++++++++++++++++++++++++++++++++++---
 src/character/character.hpp      |  17 ++++
 src/character/message.cpp        |  19 +++-
 src/character/message.hpp        |   1 +
 src/defaults/shuffle_texture.hpp | 183 +++++++++++++++++++++++++++++++++++++++
 src/defaults/ui.cpp              |   6 +-
 src/defaults/ui.hpp              |   1 +
 src/game/state_playing.cpp       |   9 ++
 src/map/map.cpp                  |  40 ++++-----
 11 files changed, 433 insertions(+), 42 deletions(-)

diff --git a/src/character/animation.cpp b/src/character/animation.cpp
index 953abd0..52978cf 100644
--- a/src/character/animation.cpp
+++ b/src/character/animation.cpp
@@ -14,7 +14,8 @@ namespace Animation {
                             float frame_width,
                             float frame_height,
                             std::string audio_name_in) {
-        SDL_Texture* tex = IMG_LoadTexture(renderer, path.c_str());
+        SDL_Surface* surf = IMG_Load(path.c_str());
+        SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
         SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
         if (!tex) {
             std::cerr << "Failed to load texture: " << path << '\n';
@@ -33,6 +34,7 @@ namespace Animation {
 
         AnimationData anim;
         anim.texture = tex;
+        anim.surface = surf;
         anim.frames.reserve(frame_count);
 
         for (int i = 0; i < frame_count; ++i) {
@@ -58,11 +60,21 @@ namespace Animation {
         anim.timer += delta_time;
         if (anim.timer >= animation_frame_time) {
             anim.timer = 0.0f;
-            anim.current_frame = (anim.current_frame + 1) % anim.frames.size();
+
+            if (!anim.reverse) {
+                anim.current_frame =
+                    (anim.current_frame + 1) % anim.frames.size();
+            } else {
+                if (anim.current_frame == 0) {
+                    anim.current_frame = anim.frames.size() - 1;
+                } else {
+                    anim.current_frame--;
+                }
+            }
+
             if (anim.has_audio) {
-                if ( !audio->is_Playing(anim.audio_name.c_str()) ) {
+                if (!audio->is_Playing(anim.audio_name.c_str())) {
                     audio->Play(anim.audio_name.c_str());
-                    //std::cout << "PLAY START\n";
                 }
             }
         }
diff --git a/src/character/animation.hpp b/src/character/animation.hpp
index af51e33..3460058 100644
--- a/src/character/animation.hpp
+++ b/src/character/animation.hpp
@@ -17,12 +17,15 @@ class Game;
 
 struct AnimationData {
     SDL_Texture* texture;
+    SDL_Texture* txt_back;
+    SDL_Surface* surface = nullptr;
     std::vector<SDL_FRect> frames;
     size_t current_frame = 0;
     float frame_time = 0.1f;
     float timer = 0.0f;
     bool has_audio = false;
     std::string audio_name;
+    bool reverse = false;
 };
 
 struct CharacterAnimation {
diff --git a/src/character/character.cpp b/src/character/character.cpp
index 29269bc..21f48ff 100644
--- a/src/character/character.cpp
+++ b/src/character/character.cpp
@@ -53,6 +53,7 @@ Character::Init(SDL_Renderer* renderer) {
     collider.y = tile_size - collider.h - 16;
     collider_world.x = x + collider.x + collider.w * 0.5f;
     collider_world.y = y + collider.y + collider.h * 0.5f;
+    spawn_point = {collider_world.x, collider_world.y};
     arm_reach_radius = 6;
     collecting_logic.is_collecting = false;
 
@@ -65,7 +66,7 @@ Character::Init(SDL_Renderer* renderer) {
     sprintstart_logic = {0, 0.8, false};
 
     death_duration = 0;
-    death_duration_max = 0.4;
+    death_duration_max = 0.8;
 
     is_placing_build = false;
 
@@ -146,7 +147,8 @@ void Character::set_Status(const CharacterStatus& s) {
         Animation::audio->Stop(active_anim->audio_name.c_str());
     status = s;
     active_anim->timer = 0;
-    active_anim->current_frame = 0;
+    if (s != CharacterStatus::DEAD)
+        active_anim->current_frame = 0;
     Animation::animation_frame_time = active_anim->frame_time;
 }
 
@@ -254,6 +256,7 @@ Character::Draw(SDL_Renderer* renderer) {
 
     // Render Character
     const SDL_FRect& src = active_anim->frames[active_anim->current_frame];
+    std::cout << "FR: " << active_anim->current_frame<< std::endl;
 
     SDL_FRect dst {
             Scale(x + Map::offset.x),
@@ -262,7 +265,12 @@ Character::Draw(SDL_Renderer* renderer) {
             Scale(src.h)
     };
 
-    SDL_RenderTextureRotated(renderer, active_anim->texture, &src, &dst,
+    SDL_Texture* txt = active_anim->texture;
+
+    if (death_shuffler)
+        txt = SDL_CreateTextureFromSurface(renderer, death_shuffler->getSurface());
+
+    SDL_RenderTextureRotated(renderer, txt, &src, &dst,
             0.0, nullptr,
             flip ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE);
     if (collecting_logic.is_collecting) {
@@ -795,6 +803,7 @@ Character::drink_River() {
         stats.stamina -= stamina_per_second;
         if (stats.stamina <= 0) {
             stamina_StopReg();
+            messages->Add(MessageKey::PLAYER_EXHAUSTED);
         }
         return;
     }
@@ -846,6 +855,7 @@ Character::cultivate_Soil() {
     stats.stamina -= stamina_per_second;
     if (stats.stamina <= 0) {
         stamina_StopReg();
+        messages->Add(MessageKey::PLAYER_EXHAUSTED);
         cultivate_Stop();
     }
 
@@ -896,6 +906,7 @@ Character::plant_Vegetable() {
     stats.stamina -= stamina_per_second;
     if (stats.stamina <= 0) {
         stamina_StopReg();
+        messages->Add(MessageKey::PLAYER_EXHAUSTED);
         plant_Stop();
     }
 
@@ -1008,12 +1019,16 @@ Character::getting_Hit(float demage) {
 
 void
 Character::death() {
-    if (status == CharacterStatus::DEAD)
+    if (status == CharacterStatus::DEAD) {
+        if (death_shuffler)
+            Resurrect();
         return;
+    }
     death_duration += Default::delta_time;
     if (death_duration > death_duration_max) {
-        if (status != CharacterStatus::DEAD)
+        if (status != CharacterStatus::DEAD) {
             set_Status(CharacterStatus::DEAD);
+        }
     } else {
         if (status != CharacterStatus::DYING)
             set_Status(CharacterStatus::DYING);
@@ -1021,6 +1036,134 @@ Character::death() {
 }
 
 
+void
+Character::Resurrect() {
+    if (status != CharacterStatus::DEAD || !death_shuffler) {
+        return;
+    }
+
+    shuffle_timer += Default::delta_time;
+
+    constexpr float SHUFFLE_START_TIME = 1.6f;
+    constexpr float MOVE_START_TIME    = 2.1f;
+    constexpr float UNSHUFFLE_TIME     = 2.8f;
+    constexpr float FRAME_REDUCE_TIME  = 0.8f;
+    constexpr float TICK_INTERVAL      = 0.04f;
+
+    static float tick_accumulator = 0.0f;
+    tick_accumulator += Default::delta_time;
+    bool tick = false;
+    if (tick_accumulator >= TICK_INTERVAL) {
+        tick_accumulator -= TICK_INTERVAL;
+        tick = true;
+    }
+
+    auto distance = [](const SDL_FPoint& a, const SDL_FPoint& b) {
+        float dx = a.x - b.x;
+        float dy = a.y - b.y;
+        return SDL_sqrtf(dx * dx + dy * dy);
+    };
+
+    /* ---------------- PHASE 0: INITIAL DELAY ---------------- */
+    if (resurrect_phase == 0) {
+        if (shuffle_timer >= SHUFFLE_START_TIME) {
+            resurrect_phase = 1;
+        }
+        return;
+    }
+
+    /* ---------------- PHASE 1: SHUFFLE ---------------- */
+    if (resurrect_phase == 1) {
+        if (tick) {
+            death_shuffler->shufflePixels();
+        }
+
+        if (shuffle_timer >= MOVE_START_TIME) {
+            move_logic.move_to_position = spawn_point;
+            move_logic.automove = true;
+            death_position = collider_world;
+            resurrect_phase = 2;
+        }
+        return;
+    }
+
+    /* ---------------- PHASE 2: SHUFFLE + MOVE ---------------- */
+    if (resurrect_phase == 2) {
+        if (tick) {
+            death_shuffler->shufflePixels();
+        }
+
+        if (distance(collider_world, spawn_point) <= 2.0f) {
+            resurrect_phase = 3;
+            resurrection_timer = 0.0f;
+            move_logic.automove = false;
+        }
+        return;
+    }
+
+    /* ---------------- PHASE 3: UNSHUFFLE (2.8s) ---------------- */
+    if (resurrect_phase == 3) {
+        resurrection_timer += Default::delta_time;
+
+        if (tick) {
+            death_shuffler->unshuffleStep();
+        }
+
+        if (resurrection_timer >= UNSHUFFLE_TIME) {
+            resurrection_timer = 0.0f;
+            resurrection_start_frame = active_anim->current_frame;
+            resurrect_phase = 4;
+        }
+        return;
+    }
+
+    /* ---------------- PHASE 4: FRAME REDUCTION (0.8s) ---------------- */
+    if (resurrect_phase == 4) {
+        resurrection_timer += Default::delta_time;
+
+        float t = resurrection_timer / FRAME_REDUCE_TIME;
+        if (t > 1.0f) t = 1.0f;
+
+        active_anim->current_frame =
+            static_cast<int>(resurrection_start_frame * (1.0f - t));
+
+        if (resurrection_timer >= FRAME_REDUCE_TIME) {
+            resurrect_phase = 5;
+        }
+        return;
+    }
+
+    /* ---------------- PHASE 5: REVIVE ---------------- */
+    delete death_shuffler;
+    death_shuffler = nullptr;
+
+    resurrect_phase = 0;
+    shuffle_timer = 0.0f;
+    resurrection_timer = 0.0f;
+
+    Revive();
+}
+
+
+void
+Character::Revive() {
+    stats.health = stats.health_max;
+    stats.thirst = stats.thirst_max;
+    move_logic.automove = false;
+    set_Status(CharacterStatus::IDLE);
+    if (death_shuffler)
+        delete death_shuffler;
+
+    death_shuffler = nullptr;
+    resurrection_unshuffle_phase = false;
+    resurrection_unshuffle_timer = 0.0f;
+    shuffle_timer = 0.0f;
+    death_duration = 0.0f;
+    set_Status(CharacterStatus::IDLE);
+    std::cout << "REVIVED!!\n";
+}
+
+
 void
 set_AttackAnimation(AnimationData* &ad, WalkDirection& dir, Character* ch) {
     switch (static_cast<InventoryItem*>(ch->selected_item)->type) {
@@ -1107,6 +1250,7 @@ Character::handle_Animation() {
 
         default:
             active_anim_ref = &animation.idle[dir];
+            std::cout << "IDLEoo\n";
             break;
     }
 
@@ -1119,7 +1263,8 @@ Character::handle_Animation() {
             AnimationData& ad = *active_anim_ref;
 
             // lock last frame
-            if (!ad.frames.empty()) {
+            //if (!ad.frames.empty() && !ad.reverse) {
+            if (resurrect_start == 2) {
                 ad.current_frame = ad.frames.size() - 1;
             }
 
@@ -1461,8 +1606,10 @@ Character::handle_Input(Input& input, SDL_Rect win_size) {
         }
 
         stats.stamina -= stamina_drain;
-        if (stats.stamina <= 0)
+        if (stats.stamina <= 0) {
             stamina_StopReg();
+            messages->Add(MessageKey::PLAYER_EXHAUSTED);
+        }
         else {
             is_sprinting = true;
             if (!sprintstart_logic.is_starting && !sprintstart_logic.has_started) {
@@ -1544,13 +1691,13 @@ void Character::handle_Move() {
 
     moving = (dx != 0.0f || dy != 0.0f);
 
-    /*
     // --- Disable automove when manually moving ---
+    /*
     if (manual_move)
         move_logic.automove = false;
+        */
 
-    // --- Automatic movement logic ---
-    if (!manual_move && move_logic.automove) {
+    if (move_logic.automove) {
         float target_x = move_logic.move_to_position.x;
         float target_y = move_logic.move_to_position.y;
 
@@ -1569,8 +1716,15 @@ void Character::handle_Move() {
 
         dx /= distance;
         dy /= distance;
+        y += dy;
+        x += dx;
+        // --- Update world positions ---
+        collider_world.x = x + collider.x + collider.w * 0.5f;
+        collider_world.y = y + collider.y + collider.h * 0.5f;
+        tile_world.x = collider_world.x / Tile::tile_size;
+        tile_world.y = collider_world.y / Tile::tile_size;
+        return;
     }
-    */
 
     // --- Stop if neither auto nor manual movement active ---
     if (!moving) {
diff --git a/src/character/character.hpp b/src/character/character.hpp
index 1a91f2a..21e1f1f 100644
--- a/src/character/character.hpp
+++ b/src/character/character.hpp
@@ -20,6 +20,7 @@
 #include "building/building.hpp"
 #include "vegetables/vegetables.hpp"
 #include "bird/bird.hpp"
+#include "defaults/shuffle_texture.hpp"
 
 // Character stuff
 
@@ -161,8 +162,19 @@ class Character {
     CharacterSprintStartLogic sprintstart_logic;
     Bird* bird = nullptr;
 
+    PixelShuffler* death_shuffler;
+    float shuffle_timer    = 0.0f;
+    float shuffle_interval = 8.0f;
+
+
+    int resurrect_start = 0; // 1 and 2
+    int resurrection_start_frame = 0;
+    float resurrection_unshuffle_timer = 0.0f;
+    bool  resurrection_unshuffle_phase = false;
     float death_duration;
     float death_duration_max;
+    int   resurrect_phase = 0;   // 0..5
+    float resurrection_timer = 0.0f;
 
     int height;
     int width;
@@ -172,6 +184,8 @@ class Character {
     std::vector<SDL_Point>
                   tiles_in_reach;
 
+    SDL_FPoint death_position;
+
 
     CharacterMoveLogic move_logic;
 
@@ -188,6 +202,7 @@ class Character {
     CharacterStaminaRegeneration stamina_regen_logic;
     CharacterThirstDrainLogic    thirst_drain_logic;
     float x, y;
+    SDL_FPoint spawn_point;
     int level = 1;
 
     std::unique_ptr<Messages> messages;
@@ -255,6 +270,8 @@ class Character {
 
     void getting_Hit(float demage);
     void death();
+    void Resurrect();
+    void Revive();
 
     //bool is_CollidingWithObjects(const SDL_FRect& rect);
     bool is_Colliding(const SDL_FRect& rect);
diff --git a/src/character/message.cpp b/src/character/message.cpp
index 937ef1f..e7510bf 100644
--- a/src/character/message.cpp
+++ b/src/character/message.cpp
@@ -14,6 +14,7 @@ Messages::Messages() {
     messages_data = {
         {MessageKey::MOIN, {10, MessageType::CHARACTER, "I should collect some things.."}},
         {MessageKey::BUILDING_TOOL_REQUIRED, {5, MessageType::CHARACTER, "I need a building tool!"}},
+        {MessageKey::PLAYER_EXHAUSTED,       {5, MessageType::CHARACTER, "I am exhausted..."}},
         {MessageKey::DISCOVERED_RESOURCE,    {5, MessageType::CHARACTER, "Wow, thats new!!"}},
         {MessageKey::DISCOVERED_BUILDING,    {5, MessageType::CHARACTER, "Maybe i can build something with it.."}},
         {MessageKey::THIRST_20,     {10, MessageType::CHARACTER, "I should drink something.."}},
@@ -127,11 +128,14 @@ Messages::Render(SDL_Renderer* renderer, float center_x, float start_y) {
             // Render message
             SDL_Color col{255, 255, 255, 255};
             SDL_Surface* surf =
-                TTF_RenderText_Blended(Ui::font_tiny, msg.message.c_str(), msg.message.length(), col);
+                TTF_RenderText_Blended(Ui::font_message, msg.message.c_str(), msg.message.length(), col);
+            SDL_Surface* surf_bg =
+                TTF_RenderText_Blended(Ui::font_message, msg.message.c_str(), msg.message.length(), {126,126,126,164});
 
-            if (surf) {
-                SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
-                if (tex) {
+            if (surf && surf_bg) {
+                SDL_Texture* tex    = SDL_CreateTextureFromSurface(renderer, surf);
+                SDL_Texture* tex_bg = SDL_CreateTextureFromSurface(renderer, surf_bg);
+                if (tex && tex_bg) {
 
 
                     SDL_FRect dst{
@@ -141,10 +145,17 @@ Messages::Render(SDL_Renderer* renderer, float center_x, float start_y) {
                             (float)surf->h
                     };
 
+                    SDL_FRect dst_bg = dst;
+                    dst_bg.x -= 1;
+                    dst_bg.y -= 1;
+
                     SDL_RenderTexture(renderer, tex, nullptr, &dst);
+                    SDL_RenderTexture(renderer, tex_bg, nullptr, &dst_bg);
                     SDL_DestroyTexture(tex);
+                    SDL_DestroyTexture(tex_bg);
                 }
                 SDL_DestroySurface(surf);
+                SDL_DestroySurface(surf_bg);
             }
 
             start_y -= row_h;
diff --git a/src/character/message.hpp b/src/character/message.hpp
index 73113a9..f928b1c 100644
--- a/src/character/message.hpp
+++ b/src/character/message.hpp
@@ -26,6 +26,7 @@ struct Message_s {
 enum class MessageKey {
     MOIN,
     BUILDING_TOOL_REQUIRED,
+    PLAYER_EXHAUSTED,
     DISCOVERED_RESOURCE,
     DISCOVERED_BUILDING,
     THIRST_20,
diff --git a/src/defaults/shuffle_texture.hpp b/src/defaults/shuffle_texture.hpp
new file mode 100644
index 0000000..4ca1bb6
--- /dev/null
+++ b/src/defaults/shuffle_texture.hpp
@@ -0,0 +1,183 @@
+#pragma once
+#include <SDL3/SDL.h>
+#include <SDL3_image/SDL_image.h>
+#include <iostream>
+#include <vector>
+#include <random>
+#include <algorithm>
+
+class PixelShuffler {
+private:
+    SDL_Renderer* renderer;
+    SDL_Surface* surface;
+    SDL_Texture* texture;
+    // Store each shuffle step as a list of swaps
+    struct Swap {
+        int x1, y1, x2, y2;
+    };
+    std::vector<std::vector<Swap>> shuffleHistory; // Each shuffle creates a list of swaps
+    std::mt19937 rng;
+
+
+public:
+    PixelShuffler(SDL_Renderer* r, SDL_Surface* surf) : renderer(r), surface(surf), rng(std::random_device{}()) {
+    }
+
+    // Get pixel at position (x, y)
+    Uint32 getPixel(int x, int y) {
+        if (x < 0 || x >= surface->w || y < 0 || y >= surface->h) {
+            return 0;
+        }
+
+        Uint8* pixels = (Uint8*)surface->pixels;
+        int bpp = SDL_BYTESPERPIXEL(surface->format);
+        Uint8* pixel = pixels + y * surface->pitch + x * bpp;
+
+        switch (bpp) {
+            case 1:
+                return *pixel;
+            case 2:
+                return *(Uint16*)pixel;
+            case 3:
+                if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+                    return ((Uint32)pixel[0] << 16)
+                        | ((Uint32)pixel[1] << 8)
+                        |  (Uint32)pixel[2];
+                else
+                    return  (Uint32)pixel[0]
+                        | ((Uint32)pixel[1] << 8)
+                        | ((Uint32)pixel[2] << 16);
+            case 4:
+                return *(Uint32*)pixel;
+            default:
+                return 0;
+        }
+    }
+
+    // Set pixel at position (x, y)
+    void setPixel(int x, int y, Uint32 color) {
+        if (x < 0 || x >= surface->w || y < 0 || y >= surface->h) {
+            return;
+        }
+
+        Uint8* pixels = (Uint8*)surface->pixels;
+        int bpp = SDL_BYTESPERPIXEL(surface->format);
+        Uint8* pixel = pixels + y * surface->pitch + x * bpp;
+
+        switch (bpp) {
+            case 1:
+                *pixel = color;
+                break;
+            case 2:
+                *(Uint16*)pixel = color;
+                break;
+            case 3:
+                if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+                    pixel[0] = (color >> 16) & 0xff;
+                    pixel[1] = (color >> 8) & 0xff;
+                    pixel[2] = color & 0xff;
+                } else {
+                    pixel[0] = color & 0xff;
+                    pixel[1] = (color >> 8) & 0xff;
+                    pixel[2] = (color >> 16) & 0xff;
+                }
+                break;
+            case 4:
+                *(Uint32*)pixel = color;
+                break;
+        }
+    }
+
+    // Shuffle pixels by swapping each pixel with a random neighbor
+    void shufflePixels() {
+        SDL_LockSurface(surface);
+
+        int width = surface->w;
+        int height = surface->h;
+
+        std::vector<Swap> currentShuffleSwaps;
+
+        // For each pixel, swap with a random neighbor
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                // Get random neighbor direction (0=right, 1=down, 2=left, 3=up)
+                int direction = rng() % 4;
+                int nx = x, ny = y;
+
+                switch (direction) {
+                    case 0: nx = x + 1; break; // right
+                    case 1: ny = y + 1; break; // down
+                    case 2: nx = x - 1; break; // left
+                    case 3: ny = y - 1; break; // up
+                }
+
+                // Only swap if neighbor is valid
+                if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
+                    // Swap pixels
+                    Uint32 pixel1 = getPixel(x, y);
+                    Uint32 pixel2 = getPixel(nx, ny);
+                    setPixel(x, y, pixel2);
+                    setPixel(nx, ny, pixel1);
+
+                    // Record this swap
+                    currentShuffleSwaps.push_back({x, y, nx, ny});
+                }
+            }
+        }
+
+        // Add this shuffle to history
+        shuffleHistory.push_back(currentShuffleSwaps);
+
+        SDL_UnlockSurface(surface);
+        texture = SDL_CreateTextureFromSurface(renderer, getSurface());
+    }
+
+    // Unshuffle by reversing the last shuffle step
+    bool unshuffleStep() {
+        if (shuffleHistory.empty()) {
+            return false; // Nothing to unshuffle
+        }
+
+        SDL_LockSurface(surface);
+
+        // Get the last shuffle
+        std::vector<Swap>& lastShuffle = shuffleHistory.back();
+
+        // Reverse the swaps in reverse order
+        for (auto it = lastShuffle.rbegin(); it != lastShuffle.rend(); ++it) {
+            const Swap& swap = *it;
+
+            // Swap back
+            Uint32 pixel1 = getPixel(swap.x1, swap.y1);
+            Uint32 pixel2 = getPixel(swap.x2, swap.y2);
+            setPixel(swap.x1, swap.y1, pixel2);
+            setPixel(swap.x2, swap.y2, pixel1);
+        }
+
+        // Remove this shuffle from history
+        shuffleHistory.pop_back();
+
+        SDL_UnlockSurface(surface);
+        texture = SDL_CreateTextureFromSurface(renderer, getSurface());
+        return true;
+    }
+
+    // Check if there are shuffles to undo
+    bool canUnshuffle() const {
+        return !shuffleHistory.empty();
+    }
+
+    // Get number of shuffle steps in history
+    int getShuffleCount() const {
+        return shuffleHistory.size();
+    }
+
+    SDL_Surface* getSurface() { return surface; }
+
+
+    // Get the working texture to render
+    SDL_Texture* getTexture() {
+        return texture;
+    }
+};
+
diff --git a/src/defaults/ui.cpp b/src/defaults/ui.cpp
index 71d8654..8fbfc97 100644
--- a/src/defaults/ui.cpp
+++ b/src/defaults/ui.cpp
@@ -55,8 +55,9 @@ namespace Ui {
     TTF_Font* font_small = nullptr;
     TTF_Font* font_tiny  = nullptr;
     TTF_Font* font_menu  = nullptr;
-    TTF_Font* font_top_bar = nullptr;
+    TTF_Font* font_top_bar   = nullptr;
     TTF_Font* font_menu_bold = nullptr;
+    TTF_Font* font_message   = nullptr;
 
     float top_bar_height = 40;
 
@@ -72,8 +73,7 @@ namespace Ui {
         font_tiny  = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 12);
         font_menu  = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 18);
         font_menu_bold = TTF_OpenFont("fonts/DejaVuSansMono-Bold.ttf", 18);
-
-        font_top_bar = TTF_OpenFont("fonts/DejaVuSans-Bold.ttf", 22);
+        font_message   = TTF_OpenFont("fonts/DejaVuSansMono.ttf", 16);
 
         if (!font_big || !font_small) {
             std::cerr << "Failed to load font: " << SDL_GetError() << std::endl;
diff --git a/src/defaults/ui.hpp b/src/defaults/ui.hpp
index 6551411..608885c 100644
--- a/src/defaults/ui.hpp
+++ b/src/defaults/ui.hpp
@@ -18,6 +18,7 @@ namespace Ui {
     extern TTF_Font* font_tiny;
     extern TTF_Font* font_menu;
     extern TTF_Font* font_menu_bold;
+    extern TTF_Font* font_message;
 
     extern float top_bar_height;
 
diff --git a/src/game/state_playing.cpp b/src/game/state_playing.cpp
index 94b47e0..f60e0a3 100644
--- a/src/game/state_playing.cpp
+++ b/src/game/state_playing.cpp
@@ -7,6 +7,7 @@
 #include "game/game.hpp"
 #include "defaults/ui.hpp"
 #include "character/message.hpp"
+#include "defaults/shuffle_texture.hpp"
 
 StatePlaying::StatePlaying() { 
     std::cout << "CREATE STATE_PLAYING\n";
@@ -139,6 +140,9 @@ void StatePlaying::handle_Events(SDL_Event& event, Game* game) {
                         player->cultivation_logic.is_removing = !player->cultivation_logic.is_removing;
                     break;
                 }
+                case SDL_SCANCODE_F5:
+                    player->stats.health = 0;
+                    break;
                 case SDL_SCANCODE_F11: // FOR TESTING
                     Enemy::init_SpawnEnemy(0, WeaponType::SWORD, WeaponMaterial::WOOD, {150, 100}, {6,6,1,false,false,0,1.2});
                     return;
@@ -198,6 +202,11 @@ void StatePlaying::Update(Game* game) {
     // CHARACTER
     player->handle_Input(game->input, game->win_size);
     player->Update();
+    if (player->status == CharacterStatus::DEAD && player->death_shuffler == nullptr) {
+        player->death_shuffler = new PixelShuffler(game->renderer, player->active_anim->surface);
+        player->shuffle_timer = 0.0f;
+        return;
+    }
 
     // MESSAGES
     player->messages->Update();
diff --git a/src/map/map.cpp b/src/map/map.cpp
index e4c5aee..b2edf11 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -905,27 +905,27 @@ namespace Map {
 
 
 
-                    player_behind = false;
-                    int am_i_behind_an_object = object->is_InFrontOf({m_xf, m_yf},
-                                           {character_collider_x,
-                                            character_collider_y,
-                                            player.collider.w,
-                                            player.collider.h});
-                    if (am_i_behind_an_object) {
-                        if ( am_i_behind_an_object == 1 &&
-                             character_collider_y-player.height/3 > m_yf &&
-                             ( player.collider_world.x > m_xf ||
-                               player.collider_world.x < m_xf+object->dimension.x ) )
-                            player_behind = true;
-                        if (!character_drawn) {
-                            //std::cout << "DRAW BEHIND\n";
-                            player.Draw(renderer);
-                            character_drawn = true;
-                        }
+                player_behind = false;
+                int am_i_behind_an_object = object->is_InFrontOf({m_xf, m_yf},
+                        {character_collider_x,
+                        character_collider_y,
+                        player.collider.w,
+                        player.collider.h});
+                if (am_i_behind_an_object) {
+                    if ( am_i_behind_an_object == 1 &&
+                            character_collider_y-player.height/3 > m_yf &&
+                            ( player.collider_world.x > m_xf ||
+                              player.collider_world.x < m_xf+object->dimension.x ) )
+                        player_behind = true;
+                    if (!character_drawn) {
+                        //std::cout << "DRAW BEHIND\n";
+                        player.Draw(renderer);
+                        character_drawn = true;
                     }
-                    //std::cout <<  player_behind << std::endl;
-                    //std::cout << "CHA: " << character_collider_x << " " << character_collider_y << " " << player.collider.w << " " << player.collider.h << std::endl;
-                    //std::cout << "OJJ: " << m_xf << " " << m_yf << " " << m_point.x << " " << m_point.y << std::endl;
+                }
+                //std::cout <<  player_behind << std::endl;
+                //std::cout << "CHA: " << character_collider_x << " " << character_collider_y << " " << player.collider.w << " " << player.collider.h << std::endl;
+                //std::cout << "OJJ: " << m_xf << " " << m_yf << " " << m_point.x << " " << m_point.y << std::endl;