mapcreator git · master
SDL3 2.5D game and engine using assets, with map editor
C++ 99.3%git clone https://git.christianimmanuel.de/sdl-graphics/mapcreator.gitwget https://git.christianimmanuel.de/sdl-graphics/mapcreator/archive/mapcreator.tar.gzAdd Consume food
src/character/animation.hpp | 1 + src/character/character.cpp | 254 +++++++++++++++++++++++++++++++++--------- src/character/character.hpp | 56 +++++++--- src/character/enemy.cpp | 6 +- src/character/message.cpp | 1 + src/character/message.hpp | 1 + src/defaults/ui.cpp | 199 ++++++++++++++++++++++++++++----- src/defaults/ui.hpp | 2 +- src/game/state_playing.cpp | 4 +- src/inventory/inventory.cpp | 12 +- src/inventory/inventory.hpp | 9 +- src/vegetables/vegetables.cpp | 14 +-- src/vegetables/vegetables.hpp | 10 ++ 13 files changed, 461 insertions(+), 108 deletions(-) diff --git a/src/character/animation.hpp b/src/character/animation.hpp index df3bf2a..693b0a3 100644 --- a/src/character/animation.hpp +++ b/src/character/animation.hpp @@ -44,6 +44,7 @@ struct CharacterAnimation { std::unordered_map<WalkDirection, AnimationData> craft; std::unordered_map<WalkDirection, AnimationData> block; std::unordered_map<WalkDirection, AnimationData> drinking_river; + std::unordered_map<WalkDirection, AnimationData> eating; }; namespace Animation { diff --git a/src/character/character.cpp b/src/character/character.cpp index 2369794..76da0a5 100644 --- a/src/character/character.cpp +++ b/src/character/character.cpp @@ -58,16 +58,17 @@ Character::Init(SDL_Renderer* renderer) { arm_reach_radius = 6; collecting_logic.is_collecting = false; - stats = { 10, 10, 10, 10, 50, 100, 1}; + stats = { 25, 0, 25, 0, 50, 100, 2}; attacking_logic = { 0.0f, nullptr, AttackingLogicStatus_e::NONE, false, nullptr }; - consuming_logic = { 0.0f, 3.0f, false, nullptr }; + consuming_logic = { 0.0f, 3.0f, false, false, nullptr }; cultivation_logic = { 0.0f, 3.0f, false, false, {0,0} }; planting_logic = { 0.0f, 3.0f, false, {0,0}, VegetableData::Type::NONE, false}; sprintstart_logic = {0, 0.8, false}; death_duration = 0; death_duration_max = 0.8; + death_shuffler = nullptr; is_placing_build = false; @@ -135,6 +136,10 @@ Character::Init(SDL_Renderer* renderer) { animation.drinking_river[WalkDirection::SIDE] = Animation::load(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Collect_Base/Collect_Side-Sheet.png", tile_size, tile_size, "character.drink", false, false); animation.drinking_river[WalkDirection::UP] = Animation::load(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Collect_Base/Collect_Up-Sheet.png", tile_size, tile_size, "character.drink", false, false); + animation.eating[WalkDirection::DOWN] = Animation::load(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Collect_Base/Collect_Down-Sheet.png", tile_size, tile_size, "character.eat", false, false); + animation.eating[WalkDirection::SIDE] = Animation::load(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Collect_Base/Collect_Side-Sheet.png", tile_size, tile_size, "character.eat", false, false); + animation.eating[WalkDirection::UP] = Animation::load(renderer, "Pixel_Crawler/Entities/Characters/Body_A/Animations/Collect_Base/Collect_Up-Sheet.png", tile_size, tile_size, "character.eat", false, false); + Animation::audio->RegisterSound("character.dead", "music/character.dead.wav"); handle_Animation(); @@ -671,6 +676,48 @@ Character::interact_WithBuilding(Building* building, Input& input) { } +bool +Character::is_StatsBonusAlready(InventoryObjectType object_type, uint32_t type) { + for (CharacterStatsBonus& b : stats_bonus) + if (b.object_type == object_type && b.type == type) + return true; + + return false; +} + +void +Character::add_ToStatsBonus(InventoryObjectType object_type, + uint32_t type, + ConsumingStatsBonus sb) { + for (auto& entry : stats_bonus) { + if (entry.object_type == object_type && + entry.type == type) + { + entry.stats = sb; + return; + } + } + + if (stats_bonus.size() >= 2) { + std::cout << "I am overfed\n"; + return; + } + + stats_bonus.push_back({object_type, type, sb.duration, sb}); +} + +void +Character::reduce_StatsBonus() { + for (auto it = stats_bonus.begin(); it != stats_bonus.end(); ) { + it->duration -= Default::delta_time; + + if (it->duration <= 0.0f) + it = stats_bonus.erase(it); + else + ++it; + } +} + void Character::attack_Start() { std::cout << "Attack Start\n"; @@ -802,6 +849,49 @@ Character::building_Stop() { is_placing_build = false; } + +void +Character::eat_Start() { + std::cout << "Eating Start\n"; + if ( stats_bonus.size() >= 2 && + !is_StatsBonusAlready(selected_item->object_type, static_cast<uint32_t>(static_cast<InventoryVegetable*>(selected_item)->type))) + return; + + messages->Add(MessageKey::EAT_APPRECIATION); + set_Status(CharacterStatus::EATING); + handle_Animation(); + consuming_logic.duration = 0.0; + consuming_logic.duration_max = 3.0f; + consuming_logic.is_consuming = true; + consuming_logic.is_drinking = false; +} + +void +Character::eat_Something() { + // std::cout << "Eating ...\n"; + if (consuming_logic.duration < consuming_logic.duration_max) { + consuming_logic.duration += Default::delta_time; + float stamina_per_second = 0.01f / consuming_logic.duration_max; + stamina_Reduce(stamina_per_second); + if (stamina_Get() <= 0) { + stamina_StopReg(); + messages->Add(MessageKey::PLAYER_EXHAUSTED); + consume_Stop(); + } + return; + } + + std::cout << "EATING COMPLETE\n"; + //InventoryVegetable + if (selected_item->object_type == InventoryObjectType::VEGETABLE) { + InventoryVegetable* it = static_cast<InventoryVegetable*>(selected_item); + inventory->remove_Vegetable(it->type, 1); + add_ToStatsBonus(InventoryObjectType::VEGETABLE, static_cast<uint32_t>(it->type), it->stats_bonus); + } + + consume_Stop(); +} + void Character::drink_Start() { std::cout << "Drinking Start\n"; @@ -812,22 +902,23 @@ Character::drink_Start() { handle_Animation(); consuming_logic.duration = 0.0; consuming_logic.is_consuming = true; + consuming_logic.is_drinking = true; } void Character::drink_River() { // std::cout << "Drinking ...\n"; if (consuming_logic.duration < consuming_logic.duration_max) { - if (stats.stamina <= 0 || stats.thirst >= stats.thirst_max) { - drink_Stop(); + if (stamina_Get() <= 0 || stats.thirst >= stats.thirst_max) { + consume_Stop(); return; } consuming_logic.duration += Default::delta_time; float amount_per_second = 25.0f / consuming_logic.duration_max; stats.thirst += amount_per_second * Default::delta_time; float stamina_per_second = 0.01f / consuming_logic.duration_max; - stats.stamina -= stamina_per_second; - if (stats.stamina <= 0) { + stamina_Reduce(stamina_per_second); + if (stamina_Get() <= 0) { stamina_StopReg(); messages->Add(MessageKey::PLAYER_EXHAUSTED); } @@ -836,14 +927,14 @@ Character::drink_River() { if (stats.thirst > stats.thirst_max) stats.thirst = stats.thirst_max; - stats.health += 2; - if (stats.health > stats.health_max) - stats.health = stats.health_max; - drink_Stop(); + + health_Increase(2); + + consume_Stop(); } void -Character::drink_Stop() { - std::cout << "Drinking Stopped\n"; +Character::consume_Stop() { + std::cout << "Consuming Stopped\n"; consuming_logic.duration = 0.0; consuming_logic.is_consuming = false; set_Status(CharacterStatus::IDLE); @@ -856,7 +947,7 @@ Character::cultivate_Start() { std::cout << "FT: " << floor_type << " " << Map::get_IdFromTilePoint(tile_world) << std::endl; if (!Tile::floor_props[floor_type].can_buid_on) return; - if (stats.stamina <= 0) + if (stamina_Get() <= 0) return; if ( Farm::soil_IsMaptile(tile_world) && @@ -871,15 +962,15 @@ Character::cultivate_Start() { } void Character::cultivate_Soil() { - if (stats.stamina <= 0) { + if (stamina_Get() <= 0) { cultivate_Stop(); return; } cultivation_logic.duration += Default::delta_time; float stamina_per_second = 0.06f / cultivation_logic.duration_max; - stats.stamina -= stamina_per_second; - if (stats.stamina <= 0) { + stamina_Reduce(stamina_per_second); + if (stamina_Get() <= 0) { stamina_StopReg(); messages->Add(MessageKey::PLAYER_EXHAUSTED); cultivate_Stop(); @@ -904,7 +995,7 @@ Character::cultivate_Stop() { void Character::plant_Start() { std::cout << "Plant Start\n"; - if (stats.stamina <= 0) + if (stamina_Get() <= 0) return; if (!Farm::soil_IsMaptile(tile_world)) return; @@ -924,15 +1015,15 @@ Character::plant_Start() { void Character::plant_Vegetable() { - if (stats.stamina <= 0) { + if (stamina_Get() <= 0) { plant_Stop(); return; } planting_logic.duration += Default::delta_time; float stamina_per_second = 0.06f / planting_logic.duration_max; - stats.stamina -= stamina_per_second; - if (stats.stamina <= 0) { + stamina_Reduce(stamina_per_second); + if (stamina_Get() <= 0) { stamina_StopReg(); messages->Add(MessageKey::PLAYER_EXHAUSTED); plant_Stop(); @@ -963,9 +1054,63 @@ Character::plant_Stop() { } +float +Character::health_Get() { + return health_Max() - stats.health_reduced; +} +float +Character::health_Max() { + return stats.health_max + health_MaxBonus(); +} +float +Character::health_MaxBonus() { + float h = 0.0f; + for (auto& e : stats_bonus) { + h += e.stats.health; + } + return h; +} +void +Character::health_Reduce(float amount) { + stats.health_reduced += amount; +} +void +Character::health_Increase(float amount) { + stats.health_reduced -= amount; + if (stats.health_reduced < 0.0f) + stats.health_reduced = 0.0; +} + +float +Character::stamina_Get() { + return stamina_Max() - stats.stamina_reduced; +} +float +Character::stamina_Max() { + return stats.stamina_max + stamina_MaxBonus(); +} +float +Character::stamina_MaxBonus() { + float s = 0.0f; + for (auto& e : stats_bonus) { + s += e.stats.stamina; + } + return s; +} +void +Character::stamina_Reduce(float amount) { + stats.stamina_reduced += amount; +} +void +Character::stamina_Increase(float amount) { + stats.stamina_reduced -= amount; + if (stats.stamina_reduced < 0.0f) + stats.stamina_reduced = 0.0; +} + void Character::stamina_StartReg() { - if (stats.stamina >= stats.stamina_max) + if (stamina_Get() >= stamina_Max()) return; if (!stamina_regen_logic.is_regenerating) { @@ -977,8 +1122,8 @@ Character::stamina_StartReg() { if (status == CharacterStatus::WALK) { amount_per_second *= 0.5; } - stats.stamina += amount_per_second * Default::delta_time; - if (stats.stamina >= stats.stamina_max) + stamina_Increase(amount_per_second * Default::delta_time); + if (stamina_Get() >= stamina_Max()) stamina_StopReg(); } @@ -988,8 +1133,6 @@ void Character::stamina_StopReg() { stamina_regen_logic.is_regenerating = false; stamina_regen_logic.delay_is = 0; - if (stats.stamina < 0) - stats.stamina = 0; } @@ -1000,10 +1143,7 @@ Character::thirst_Drain() { if (stats.thirst <= 0) { float amount_per_second = 1; - stats.health -= amount_per_second * Default::delta_time; - if (stats.health <= 0) { - stats.health = 0; - } + health_Reduce(amount_per_second * Default::delta_time); return; } else { thirst_drain_logic.send_message_0 = false; @@ -1035,16 +1175,12 @@ Character::getting_Hit(float demage) { if (attacking_logic.is_attacking == AttackingLogicStatus_e::NONE) set_Status(CharacterStatus::HIT); collecting_logic.is_collecting = false; - if ((attacking_logic.is_attacking == AttackingLogicStatus_e::BLOCKING_RUNNING && stats.stamina >= block_staming)) - stats.stamina -= block_staming; + if ((attacking_logic.is_attacking == AttackingLogicStatus_e::BLOCKING_RUNNING && stamina_Get() >= block_staming)) + stamina_Reduce(block_staming); else - stats.health -= demage; - - std::cout << "Char: Getting hit! " << stats.health << std::endl; + health_Reduce(demage); - if (stats.health <= 0) { - stats.health = 0; - } + std::cout << "Char: Getting hit! " << std::endl; } void @@ -1185,7 +1321,7 @@ Character::Resurrect() { void Character::Revive() { - stats.health = stats.health_max; + stats.health_reduced = 0.0f; stats.thirst = stats.thirst_max; move_logic.automove = false; set_Status(CharacterStatus::IDLE); @@ -1286,6 +1422,10 @@ Character::handle_Animation() { active_anim_ref = &animation.drinking_river[dir]; break; + case CharacterStatus::EATING: + active_anim_ref = &animation.eating[dir]; + break; + default: active_anim_ref = &animation.idle[dir]; break; @@ -1338,7 +1478,7 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { */ thirst_Drain(); - if (stats.health == 0) { + if (health_Get() <= 0.0f) { death(); return; } @@ -1422,16 +1562,13 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { if (inventory->selected_inventory.vegetable != VegetableData::Type::NONE) { InventoryVegetable& iv = inventory->vegetables.at(inventory->selected_inventory.vegetable); setted_hotbar = inventory->set_HotbarItem(Inventory::selected_hotbar, &iv); - std::cout << "NOOO \n"; //inventory->Equip(it); } else if (inventory->selected_inventory.seed != VegetableData::Type::NONE) { InventoryVegetable& iv = inventory->seeds.at(inventory->selected_inventory.seed); - std::cout << "JEEE " << iv.is_seed << "\n"; setted_hotbar = inventory->set_HotbarItem(Inventory::selected_hotbar, &iv); //inventory->Equip(it); } else { - std::cout << "FUCK \n"; setted_hotbar = inventory->set_HotbarItem(Inventory::selected_hotbar, inventory->selected_inventory.item); if (setted_hotbar) { if (sel_item) inventory->Equip(sel_item); @@ -1439,6 +1576,9 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { } if (setted_hotbar) { + if (consuming_logic.is_consuming && !consuming_logic.is_drinking) + consume_Stop(); + selected_item = inventory->get_SelectedHotbarItem(); sel_item = nullptr; if (selected_item->object_type == InventoryObjectType::ITEM) { @@ -1584,14 +1724,18 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { // LEFT CLICK if (input.click_left) { if (sel_item) { + // START CULTIVATE if ( sel_item->type == ItemType::CULTIVATOR && status != CharacterStatus::CULTIVATING) cultivate_Start(); else + // START BUILD if (sel_item->type == ItemType::BUILDING_TOOL) { build_Start(); return; - } else if (using_Weapon() && + } else + // START ATTACK + if (using_Weapon() && ( attacking_logic.is_attacking == AttackingLogicStatus_e::NONE || attacking_logic.is_attacking == AttackingLogicStatus_e::BLOCKING_RUNNING)) { collecting_logic.is_collecting = false; @@ -1599,7 +1743,8 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { return; } } else { - if (!planting_logic.is_planting) { + if (!planting_logic.is_planting && + Farm::soil_IsMaptile(tile_world)) { switch (selected_item->object_type) { case InventoryObjectType::VEGETABLE: case InventoryObjectType::SEED: @@ -1607,6 +1752,9 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { return; default: break; } + } else + if (!consuming_logic.is_consuming && selected_item->object_type == InventoryObjectType::VEGETABLE) { + eat_Start(); } } } else @@ -1626,7 +1774,10 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { if (consuming_logic.is_consuming) { - drink_River(); + if (consuming_logic.is_drinking) + drink_River(); + else + eat_Something(); return; } @@ -1640,7 +1791,7 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { collect_Stop(); } - if (input.pressed_shift && stats.stamina > 0 && + if (input.pressed_shift && stamina_Get() > 0 && (status == CharacterStatus::WALK || status == CharacterStatus::RUN)) { // START SPRINT @@ -1662,8 +1813,8 @@ Character::handle_Input(Input& input, SDL_Rect win_size) { } } - stats.stamina -= stamina_drain; - if (stats.stamina <= 0) { + stamina_Reduce(stamina_drain); + if (stamina_Get() <= 0) { stamina_StopReg(); messages->Add(MessageKey::PLAYER_EXHAUSTED); } @@ -1796,11 +1947,10 @@ void Character::handle_Move() { } - if (collecting_logic.is_collecting) { + if (collecting_logic.is_collecting) collect_Stop(); - } if (consuming_logic.is_consuming) - drink_Stop(); + consume_Stop(); attacking_logic.is_attacking = AttackingLogicStatus_e::NONE; @@ -1882,6 +2032,8 @@ void Character::Update() { //bird->set_Position({x,y}); bird->Update(Default::delta_time, collider_world); + reduce_StatsBonus(); + if (status == CharacterStatus::HIT && active_anim->current_frame == 3) { set_Status(CharacterStatus::IDLE); } diff --git a/src/character/character.hpp b/src/character/character.hpp index e471761..4d550d3 100644 --- a/src/character/character.hpp +++ b/src/character/character.hpp @@ -45,6 +45,7 @@ enum class CharacterStatus { CRAFT, BLOCK, DRINK_RIVER, + EATING, CULTIVATING, PLANTING, }; @@ -85,6 +86,7 @@ struct CharacterConsumingLogic { float duration; float duration_max; bool is_consuming; + bool is_drinking; Resource* resource; }; @@ -106,21 +108,21 @@ struct CharacterPlantingLogic { }; struct CharacterStats { - float health; - float health_max; - float stamina; - float stamina_max; - float thirst; - float thirst_max; - - float demage; + float health_max; + float health_reduced; + float stamina_max; + float stamina_reduced; + float thirst; + float thirst_max; + + float demage; }; struct CharacterStaminaRegeneration { bool is_regenerating = false; float delay_max = 2.8; float delay_is = 0; - float regen_speed = 1; + float regen_speed = 2.4; }; struct CharacterThirstDrainLogic { @@ -136,6 +138,13 @@ struct CharacterSprintStartLogic { bool has_started = false; }; +struct CharacterStatsBonus { + InventoryObjectType object_type; + uint32_t type; + float duration; + ConsumingStatsBonus stats; +}; + class Character { //////////////////////////////////////// /// Character @@ -163,6 +172,8 @@ class Character { CharacterSprintStartLogic sprintstart_logic; Bird* bird = nullptr; + std::vector<CharacterStatsBonus> stats_bonus; + PixelShuffler* death_shuffler; float shuffle_timer = 0.0f; float shuffle_interval = 8.0f; @@ -230,6 +241,21 @@ class Character { void update_Animation(AnimationData& anim, float delta_time); void Draw(SDL_Renderer* renderer); + + float health_Get(); + float health_Max(); + float health_MaxBonus(); + void health_Reduce(float amount); + void health_Increase(float amount); + + float stamina_Get(); + float stamina_Max(); + float stamina_MaxBonus(); + void stamina_Reduce(float amount); + void stamina_Increase(float amount); + void stamina_StartReg(); + void stamina_StopReg(); + 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(); @@ -251,7 +277,11 @@ class Character { void drink_Start(); void drink_River(); - void drink_Stop(); + + void eat_Start(); + void eat_Something(); + + void consume_Stop(); void cultivate_Start(); void cultivate_Soil(); @@ -261,13 +291,13 @@ class Character { void plant_Vegetable(); void plant_Stop(); - void stamina_StartReg(); - void stamina_StopReg(); - void thirst_Drain(); void interact_WithBuilding(Building* building, Input& input); + bool is_StatsBonusAlready(InventoryObjectType object_type, uint32_t type); + void add_ToStatsBonus(InventoryObjectType object_type, uint32_t type, ConsumingStatsBonus sb); + void reduce_StatsBonus(); void getting_Hit(float demage); void death(); diff --git a/src/character/enemy.cpp b/src/character/enemy.cpp index 17e0df6..a445ea4 100644 --- a/src/character/enemy.cpp +++ b/src/character/enemy.cpp @@ -758,7 +758,7 @@ void Enemy::init_SpawnEnemy(double delay_to_spawn_in_sec, WeaponType weapon_type = WeaponType::SWORD, WeaponMaterial weapon_material = WeaponMaterial::WOOD, SDL_FPoint position = {100,100}, - EnemyStats status = {6,6,1,false,false,0,1.2}) { + EnemyStats status = {16,16,2,false,false,0,1.2}) { double spawn_date = delay_to_spawn_in_sec + Default::get_TimeSinceStartInSec(); Enemy::enemies_to_spawn.push_back({spawn_date, @@ -881,7 +881,7 @@ Enemy::set_MoveLogic(EnemyMovingType& type, Character& player) { break; } case EnemyMovingType::CHARACTER: { - bool is_player_alive = player.stats.health > 0; + bool is_player_alive = player.health_Get() > 0; if (!is_player_alive) { moving_logic.move_type = EnemyMovingType::NONE; break; @@ -1078,7 +1078,7 @@ Enemy::attack_Stop() { stats.has_attacked = false; if (moving_logic.move_type == EnemyMovingType::CHARACTER) { - if (static_cast<Character*>(moving_logic.object)->stats.health <= 0) + if (static_cast<Character*>(moving_logic.object)->health_Get() <= 0) moving_logic.move_type = EnemyMovingType::NONE; } diff --git a/src/character/message.cpp b/src/character/message.cpp index 2eabcf7..897dc3f 100644 --- a/src/character/message.cpp +++ b/src/character/message.cpp @@ -20,6 +20,7 @@ Messages::Messages() { {MessageKey::THIRST_20, {10, MessageType::CHARACTER, "I should drink something.."}}, {MessageKey::THIRST_0, {10, MessageType::CHARACTER, "No water no life.."}}, {MessageKey::WATER_APPRECIATION, {5, MessageType::CHARACTER, "Hmmm water"}}, + {MessageKey::EAT_APPRECIATION, {5, MessageType::CHARACTER, "Omnjomnjom"}}, {MessageKey::CRAFTED_ITEM, {10, MessageType::CHARACTER, "Whatever i can do with that.."}}, {MessageKey::ADD_INVENTORY, {10, MessageType::INVENTORY, "Added item: "}}, {MessageKey::ADD_RESOURCE, {10, MessageType::RESOURCE, "Resource "}}, diff --git a/src/character/message.hpp b/src/character/message.hpp index bc3656d..311dc50 100644 --- a/src/character/message.hpp +++ b/src/character/message.hpp @@ -32,6 +32,7 @@ enum class MessageKey { THIRST_20, THIRST_0, WATER_APPRECIATION, + EAT_APPRECIATION, CRAFTED_ITEM, ADD_INVENTORY, ADD_RESOURCE, diff --git a/src/defaults/ui.cpp b/src/defaults/ui.cpp index 0e14ad6..0a08b44 100644 --- a/src/defaults/ui.cpp +++ b/src/defaults/ui.cpp @@ -253,7 +253,8 @@ namespace Ui { SDL_DestroySurface(surface_text); } - void draw_BottomLeftBars(SDL_Renderer* renderer, SDL_Rect win_size, const Character& player) { + void + draw_BottomLeftBars(SDL_Renderer* renderer, SDL_Rect win_size, Character& player) { const float health_w = 20.0f; const float energy_w = health_w * 0.5f; const float thirst_w = health_w * 0.4f; @@ -263,48 +264,194 @@ namespace Ui { float start_x = 20.0f; float start_y = static_cast<float>(win_size.h) - bar_h - 20.0f; - // Clamp helper auto clamp01 = [](float v) { return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v); }; - float energy_ratio = clamp01(player.stats.stamina / player.stats.stamina_max); - float health_ratio = clamp01(player.stats.health / player.stats.health_max); - float thirst_ratio = clamp01(player.stats.thirst / player.stats.thirst_max); + float thirst_max = player.stats.thirst_max; + if (thirst_max <= 0.0f) thirst_max = 1.0f; - // Thirst bar + float thirst_ratio = clamp01(player.stats.thirst / thirst_max); - // Backgrounds (dark) - SDL_FRect health_bg = { start_x + energy_w + spacing, start_y, health_w, bar_h }; - SDL_FRect energy_bg = { start_x, start_y, energy_w, bar_h }; - SDL_FRect thirst_bg = { health_bg.x + health_w + spacing, start_y, thirst_w, bar_h }; + // ---------------- HEALTH ---------------- - SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); - SDL_RenderFillRect(renderer, &energy_bg); - SDL_RenderFillRect(renderer, &health_bg); - SDL_RenderFillRect(renderer, &thirst_bg); + float health_max = player.health_Max(); + float health_bonus = player.health_MaxBonus(); + float health_base_max = health_max - health_bonus; + if (health_base_max < 0.0f) health_base_max = 0.0f; - // Filled portions (fill from bottom up) - SDL_FRect health_fill = { health_bg.x, start_y + bar_h * (1.0f - health_ratio), health_w, bar_h * health_ratio }; - SDL_FRect energy_fill = { energy_bg.x, start_y + bar_h * (1.0f - energy_ratio), energy_w, bar_h * energy_ratio }; - SDL_FRect thirst_fill = { thirst_bg.x, start_y + bar_h * (1.0f - thirst_ratio), thirst_w, bar_h * thirst_ratio }; - // std::cout << thirst_ratio << " " << player.stats.thirst << " " << player.stats.thirst_max << std::endl; + float health_capacity_ratio = clamp01(health_max / thirst_max); + float health_bar_max_h = bar_h * health_capacity_ratio; + float health_base_capacity_ratio = + health_max > 0.0f ? clamp01(health_base_max / health_max) : 0.0f; - SDL_SetRenderDrawColor(renderer, 200, 40, 40, 255); - SDL_RenderFillRect(renderer, &health_fill); + float health_base_cap_h = health_bar_max_h * health_base_capacity_ratio; + float health_bonus_cap_h = health_bar_max_h - health_base_cap_h; - SDL_SetRenderDrawColor(renderer, 200, 200, 40, 255); - SDL_RenderFillRect(renderer, &energy_fill); + float health_val = player.health_Get(); + + float health_base_ratio = + health_base_max > 0.0f ? clamp01(health_val / health_base_max) : 0.0f; + + float health_bonus_ratio = + health_bonus > 0.0f + ? clamp01((health_val - health_base_max) / health_bonus) + : 0.0f; + + float health_bottom_y = start_y + (bar_h - health_bar_max_h); + + // ---------------- STAMINA ---------------- + + float stamina_max = player.stamina_Max(); + float stamina_bonus = player.stamina_MaxBonus(); + float stamina_base_max = stamina_max - stamina_bonus; + if (stamina_base_max < 0.0f) stamina_base_max = 0.0f; + + float stamina_capacity_ratio = clamp01(stamina_max / thirst_max); + float stamina_bar_max_h = bar_h * stamina_capacity_ratio; + + float stamina_base_capacity_ratio = + stamina_max > 0.0f ? clamp01(stamina_base_max / stamina_max) : 0.0f; + + float stamina_base_cap_h = stamina_bar_max_h * stamina_base_capacity_ratio; + float stamina_bonus_cap_h = stamina_bar_max_h - stamina_base_cap_h; + + float stamina_val = player.stamina_Get(); + + float stamina_base_ratio = + stamina_base_max > 0.0f ? clamp01(stamina_val / stamina_base_max) : 0.0f; + + float stamina_bonus_ratio = + stamina_bonus > 0.0f + ? clamp01((stamina_val - stamina_base_max) / stamina_bonus) + : 0.0f; + + float stamina_bottom_y = start_y + (bar_h - stamina_bar_max_h); + + // ---------------- RECTS ---------------- + + SDL_FRect thirst_bg = { start_x, start_y, thirst_w, bar_h }; + SDL_FRect energy_bg = { + thirst_bg.x + thirst_w + spacing, + stamina_bottom_y, + energy_w, + stamina_bar_max_h + }; + SDL_FRect health_bg = { + energy_bg.x + energy_w + spacing, + health_bottom_y, + health_w, + health_bar_max_h + }; + // ---------------- BACKGROUND ---------------- + + // thirst background + SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); + SDL_RenderFillRect(renderer, &thirst_bg); + + // stamina background base + SDL_FRect stamina_bg_base = { + energy_bg.x, + stamina_bottom_y + stamina_bonus_cap_h, + energy_w, + stamina_base_cap_h + }; + SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); + SDL_RenderFillRect(renderer, &stamina_bg_base); + + // stamina background bonus (slightly lighter) + SDL_FRect stamina_bg_bonus = { + energy_bg.x, + stamina_bottom_y, + energy_w, + stamina_bonus_cap_h + }; + SDL_SetRenderDrawColor(renderer, 45, 45, 45, 255); + SDL_RenderFillRect(renderer, &stamina_bg_bonus); + + // health background base + SDL_FRect health_bg_base = { + health_bg.x, + health_bottom_y + health_bonus_cap_h, + health_w, + health_base_cap_h + }; + SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); + SDL_RenderFillRect(renderer, &health_bg_base); + + // health background bonus (slightly lighter) + SDL_FRect health_bg_bonus = { + health_bg.x, + health_bottom_y, + health_w, + health_bonus_cap_h + }; + SDL_SetRenderDrawColor(renderer, 45, 45, 45, 255); + SDL_RenderFillRect(renderer, &health_bg_bonus); + + // ---------------- FOREGROUND ---------------- + + // health base fill + SDL_FRect health_fill_base = { + health_bg.x, + health_bottom_y + health_bonus_cap_h + + health_base_cap_h * (1.0f - health_base_ratio), + health_w, + health_base_cap_h * health_base_ratio + }; + SDL_SetRenderDrawColor(renderer, 200, 40, 40, 255); + SDL_RenderFillRect(renderer, &health_fill_base); + + // health bonus fill (lighter red) + SDL_FRect health_fill_bonus = { + health_bg.x, + health_bottom_y + + health_bonus_cap_h * (1.0f - health_bonus_ratio), + health_w, + health_bonus_cap_h * health_bonus_ratio + }; + SDL_SetRenderDrawColor(renderer, 255, 100, 100, 255); + SDL_RenderFillRect(renderer, &health_fill_bonus); + + // stamina base fill + SDL_FRect stamina_fill_base = { + energy_bg.x, + stamina_bottom_y + stamina_bonus_cap_h + + stamina_base_cap_h * (1.0f - stamina_base_ratio), + energy_w, + stamina_base_cap_h * stamina_base_ratio + }; + SDL_SetRenderDrawColor(renderer, 200, 200, 40, 255); + SDL_RenderFillRect(renderer, &stamina_fill_base); + + // stamina bonus fill (lighter yellow) + SDL_FRect stamina_fill_bonus = { + energy_bg.x, + stamina_bottom_y + + stamina_bonus_cap_h * (1.0f - stamina_bonus_ratio), + energy_w, + stamina_bonus_cap_h * stamina_bonus_ratio + }; + SDL_SetRenderDrawColor(renderer, 255, 255, 120, 255); + SDL_RenderFillRect(renderer, &stamina_fill_bonus); + + // thirst fill + SDL_FRect thirst_fill = { + thirst_bg.x, + start_y + bar_h * (1.0f - thirst_ratio), + thirst_w, + bar_h * thirst_ratio + }; SDL_SetRenderDrawColor(renderer, 40, 120, 200, 255); SDL_RenderFillRect(renderer, &thirst_fill); - // borders + // ---------------- BORDERS ---------------- + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderRect(renderer, &thirst_bg); SDL_RenderRect(renderer, &energy_bg); SDL_RenderRect(renderer, &health_bg); - SDL_RenderRect(renderer, &thirst_bg); - } } diff --git a/src/defaults/ui.hpp b/src/defaults/ui.hpp index 608885c..f9ef969 100644 --- a/src/defaults/ui.hpp +++ b/src/defaults/ui.hpp @@ -32,5 +32,5 @@ namespace Ui { void draw_TopBarTileInfo(SDL_Renderer* renderer, SDL_Rect win_size); - void draw_BottomLeftBars(SDL_Renderer* renderer, SDL_Rect win_size, const Character& player); + void draw_BottomLeftBars(SDL_Renderer* renderer, SDL_Rect win_size, Character& player); } diff --git a/src/game/state_playing.cpp b/src/game/state_playing.cpp index fa27642..4102f13 100644 --- a/src/game/state_playing.cpp +++ b/src/game/state_playing.cpp @@ -158,10 +158,10 @@ void StatePlaying::handle_Events(SDL_Event& event, Game* game) { break; } case SDL_SCANCODE_F5: - player->stats.health = 0; + player->health_Reduce(100000000); 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}); + Enemy::init_SpawnEnemy(0, WeaponType::SWORD, WeaponMaterial::WOOD, {150, 100}, {16,16,2,false,false,0,1.2}); return; default: break; diff --git a/src/inventory/inventory.cpp b/src/inventory/inventory.cpp index f50b850..1e7438c 100644 --- a/src/inventory/inventory.cpp +++ b/src/inventory/inventory.cpp @@ -105,8 +105,12 @@ Inventory::remove_Vegetable(VegetableData::Type type, size_t amount) { std::cout << "TSCHÖÖ VEG\n"; it->second.count -= amount; - if (it->second.count == 0) + if (it->second.count == 0) { vegetables.erase(it); + bool setted_hotbar = set_HotbarItem(Inventory::selected_hotbar, Inventory::hotbar_hands); + if (setted_hotbar) + Equip(Inventory::hotbar_hands); + } return true; } @@ -139,8 +143,12 @@ Inventory::remove_Seed(VegetableData::Type type, size_t amount) { return false; it->second.count -= amount; - if (it->second.count == 0) + if (it->second.count == 0) { seeds.erase(it); + bool setted_hotbar = set_HotbarItem(Inventory::selected_hotbar, Inventory::hotbar_hands); + if (setted_hotbar) + Equip(Inventory::hotbar_hands); + } return true; } diff --git a/src/inventory/inventory.hpp b/src/inventory/inventory.hpp index e1d7cda..e87b719 100644 --- a/src/inventory/inventory.hpp +++ b/src/inventory/inventory.hpp @@ -12,6 +12,7 @@ #include "defaults/icons.hpp" #include "vegetables/vegetables.hpp" + enum class InventoryObjectType : uint8_t { NONE, ITEM, @@ -52,10 +53,12 @@ struct InventoryVegetable : Inventory_s { size_t count = 0; bool is_seed = false; VegetableData::Type type; + const ConsumingStatsBonus& stats_bonus; InventoryVegetable(size_t d, VegetableData::Type t, bool s) - : Inventory_s(d, s ? InventoryObjectType::SEED : InventoryObjectType::VEGETABLE), is_seed(s), type(t) - { - } + : Inventory_s(d, s ? InventoryObjectType::SEED : InventoryObjectType::VEGETABLE), + is_seed(s), type(t), + stats_bonus(VegetableData::crop_defs[static_cast<uint32_t>(t)].stats_bonus) + { } }; diff --git a/src/vegetables/vegetables.cpp b/src/vegetables/vegetables.cpp index 42e34cb..2f20cb1 100644 --- a/src/vegetables/vegetables.cpp +++ b/src/vegetables/vegetables.cpp @@ -8,13 +8,13 @@ namespace VegetableData { const CropDefinition crop_defs[(int)Type::COUNT] = { // STAGE: 0 1 2 3 4 - { "",0,0,0,0,0,0,0,0,0,0,0,false }, - { "carrot", 2,1, 5,1, 6,1, 7,1, 23,0, (5-2+1)+1, false}, - { "raddish", 2,3, 5,3, 6,3, 7,3, 23,1, (5-2+1)+1, false}, - { "cabbage", 2,5, 5,5, 6,5, 7,5, 23,2, (5-2+1)+1, false}, - { "salad", 2,7, 5,7, 6,7, 7,7, 23,3, (5-2+1)+1, false}, - { "broccoli", 2,9, 5,9, 6,9, 7,9, 23,4, (5-2+1)+1, false}, - { "onion", 2,11,5,11,6,11,7,11,23,5, (5-2+1)+1, false} + { "",0,0,0,0,0,0,0,0,0,0,0,false , { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "carrot", 2,1, 5,1, 6,1, 7,1, 23,0, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "raddish", 2,3, 5,3, 6,3, 7,3, 23,1, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "cabbage", 2,5, 5,5, 6,5, 7,5, 23,2, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "salad", 2,7, 5,7, 6,7, 7,7, 23,3, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "broccoli", 2,9, 5,9, 6,9, 7,9, 23,4, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } }, + { "onion", 2,11,5,11,6,11,7,11,23,5, (5-2+1)+1, false, { 5.0f, 5.0f, 0.0f, 0.0f, 300.0f } } }; const CropDefinition* get_DataByType(Type type) { diff --git a/src/vegetables/vegetables.hpp b/src/vegetables/vegetables.hpp index c83aa42..5031a97 100644 --- a/src/vegetables/vegetables.hpp +++ b/src/vegetables/vegetables.hpp @@ -10,6 +10,15 @@ enum class EquipmentSlot; class Vegetable; + +struct ConsumingStatsBonus { + float health = 0.0f; + float stamina = 0.0f; + float thirst = 0.0f; + float demage = 0.0f; + float duration = 0.0f; +}; + namespace VegetableData { extern std::vector<Vegetable*> all_vegetables; @@ -38,6 +47,7 @@ namespace VegetableData { int icon_tile_y; size_t total_stages; // auto-calculated = (grown-start)+1 + rotten bool is_seed; + ConsumingStatsBonus stats_bonus; };