#include "character/message.hpp" #include #include "defaults/ui.hpp" #include "map/map.hpp" #include "game/game.hpp" #include "inventory/inventory.hpp" #define TOP_RIGHT_ICON_BG 180, 180, 180 Messages::Messages() { messages.clear(); 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, "A new resource!"}}, {MessageKey::DISCOVERED_VEGETABLE, {5, MessageType::CHARACTER, "A new vegetable!"}}, {MessageKey::DISCOVERED_BUILDING, {5, MessageType::CHARACTER, "Maybe i can build something with it.."}}, {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 "}}, {MessageKey::ADD_VEGETABLE, {10, MessageType::VEGETABLE, "Vegetable "}}, {MessageKey::ADD_VEGETABLE_SEED, {10, MessageType::VEGETABLE_SEED, "Seed "}}, {MessageKey::NOTHING_TO_INTERACT, {4, MessageType::CHARACTER, "I cant find anything to interact with.."}}, }; } void Messages::Add(MessageKey key, void* type_data) { auto base = messages_data.find(key); if (base == messages_data.end()) return; Message_s msg = base->second; msg.type_data = type_data; if (msg.message_type == MessageType::INVENTORY) { size_t* id_ptr = static_cast(msg.type_data); std::cout << "ADD ID: " << *id_ptr << std::endl; messages.emplace(key, msg); return; } if (msg.message_type == MessageType::RESOURCE) { ResourceType new_type = *static_cast(msg.type_data); auto range = messages.equal_range(key); for (auto it = range.first; it != range.second; ) { if (it->second.message_type == MessageType::RESOURCE) { ResourceType old_type = *static_cast(it->second.type_data); if (old_type == new_type) { it = messages.erase(it); continue; } } ++it; } messages.emplace(key, msg); return; } if (msg.message_type == MessageType::VEGETABLE) { ResourceType new_type = *static_cast(msg.type_data); auto range = messages.equal_range(key); for (auto it = range.first; it != range.second; ) { if (it->second.message_type == MessageType::VEGETABLE) { ResourceType old_type = *static_cast(it->second.type_data); if (old_type == new_type) { it = messages.erase(it); continue; } } ++it; } messages.emplace(key, msg); return; } auto range = messages.equal_range(key); for (auto it = range.first; it != range.second; ) { it = messages.erase(it); } messages.emplace(key, msg); } void Messages::Update() { for (auto it = messages.begin(); it != messages.end(); ) { Message_s &msg = it->second; msg.duration -= Default::delta_time; if (msg.duration <= 0.0f) { /* if (msg.message_type == MessageType::VEGETABLE) delete static_cast(msg.type_data); else */ if (msg.message_type == MessageType::RESOURCE) delete static_cast(msg.type_data); else if (msg.message_type == MessageType::INVENTORY) delete static_cast(msg.type_data); else if (msg.message_type == MessageType::VEGETABLE) delete static_cast(msg.type_data); it = messages.erase(it); continue; } ++it; } } void Messages::Render(SDL_Renderer* renderer, float center_x, float start_y) { if (!messages.empty() && Ui::font_tiny) { const float row_h = 14.0f; // Stable iteration order std::vector keys; keys.reserve(messages.size()); for (const auto &kv : messages) keys.push_back(kv.first); for (size_t i = 0; i < keys.size(); ) { MessageKey key = keys[i]; auto it = messages.find(key); if (it == messages.end()) { ++i; continue; } Message_s &msg = it->second; if (msg.message_type == MessageType::INVENTORY || msg.message_type == MessageType::RESOURCE || msg.message_type == MessageType::VEGETABLE) { ++i; continue; } // Render message SDL_Color col{255, 255, 255, 255}; SDL_Surface* surf = 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 && 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{ Scale(center_x + Map::offset.x) - (surf->w * 0.5f), Scale(start_y + Map::offset.y + 22.0f), (float)surf->w, (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; ++i; } } } void Messages::render_InventoryMessages(Game* game, Inventory* inventory) { if (messages.empty() || !Ui::font_menu) return; const float bar_padding = 6.0f; const float bar_height_min = 48.0f; const float icon_bg_size = 48.0f; const float right_margin = 10.0f; const float spacing = 6.0f; // Collect inventory messages std::vector inv_msgs; inv_msgs.reserve(messages.size()); for (auto &kv : messages) { if ( kv.second.message_type == MessageType::INVENTORY || kv.second.message_type == MessageType::RESOURCE || kv.second.message_type == MessageType::VEGETABLE || kv.second.message_type == MessageType::VEGETABLE_SEED) inv_msgs.push_back(&kv.second); } if (inv_msgs.empty()) return; // Sort by remaining duration std::sort(inv_msgs.begin(), inv_msgs.end(), [](const Message_s* a, const Message_s* b){ return a->duration > b->duration; }); // Measure max text width float max_text_w = 0.0f; for (const Message_s* msg : inv_msgs) { std::string text ; if (msg->message_type == MessageType::INVENTORY) { size_t id = *static_cast(msg->type_data); // std::cout << "IDDD: " << id << " " << inventory->items.size() << " " << inventory->items.front()->id << std::endl; InventoryItem* item = inventory->get_ItemById(id); if (!item) continue; text = "Added item: " + Item::to_String(item->type); } else if (msg->message_type == MessageType::RESOURCE) { ResourceType* rt = static_cast(msg->type_data); ResourceType type = *rt; int amount = inventory->resources[type]; text = std::to_string(amount); } else if (msg->message_type == MessageType::VEGETABLE) { VegetableData::Type* vtype = static_cast(msg->type_data); auto it = inventory->vegetables.find(*vtype); int amount = 0; if (it != inventory->vegetables.end()) amount = it->second.count; text = std::to_string(amount); } else if (msg->message_type == MessageType::VEGETABLE_SEED) { VegetableData::Type* vtype = static_cast(msg->type_data); auto it = inventory->seeds.find(*vtype); int amount = 0; if (it != inventory->seeds.end()) amount = it->second.count; text = std::to_string(amount); } SDL_Color col{255,255,255,255}; SDL_Surface* surf = TTF_RenderText_Blended(Ui::font_menu, text.c_str(), text.length(), col); if (surf) { max_text_w = std::max(max_text_w, static_cast(surf->w)); SDL_DestroySurface(surf); } } float total_width = max_text_w + bar_padding * 2.0f + icon_bg_size + bar_padding; float bar_x = (float)game->win_size.w - total_width - right_margin; float y = 10.0f; // Render messages for (const Message_s* msg : inv_msgs) { InventoryItem* item = nullptr; ResourceType* rtype = nullptr; VegetableData::Type* vtype = nullptr; bool is_seed = false; std::string text ; if (msg->message_type == MessageType::INVENTORY) { size_t id = *static_cast(msg->type_data); item = inventory->get_ItemById(id); if (!item) continue; text = "Added item: " + Item::to_String(item->type); } else if (msg->message_type == MessageType::RESOURCE) { rtype = static_cast(msg->type_data); int amount = inventory->resources[*rtype]; text = std::to_string(amount); } else if (msg->message_type == MessageType::VEGETABLE) { vtype = static_cast(msg->type_data); auto it = inventory->vegetables.find(*vtype); int amount = 0; if (it != inventory->vegetables.end()) amount = it->second.count; text = std::to_string(amount); } else if (msg->message_type == MessageType::VEGETABLE_SEED) { is_seed = true; vtype = static_cast(msg->type_data); auto it = inventory->seeds.find(*vtype); int amount = 0; if (it != inventory->seeds.end()) amount = it->second.count; text = std::to_string(amount); } SDL_Color col{255,255,255,255}; SDL_Surface* surf = TTF_RenderText_Blended(Ui::font_menu, text.c_str(), text.length(), col); if (!surf) continue; SDL_Texture* tex = SDL_CreateTextureFromSurface(game->renderer, surf); if (!tex) { SDL_DestroySurface(surf); continue; } float text_w = surf->w; float text_h = surf->h; float bar_h = std::max(text_h + bar_padding * 2.0f, bar_height_min); SDL_FRect bar_rect{bar_x, y, total_width, bar_h}; // Bar background SDL_SetRenderDrawBlendMode(game->renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(game->renderer, 30, 30, 30, 170); SDL_RenderFillRect(game->renderer, &bar_rect); SDL_SetRenderDrawColor(game->renderer, 90, 90, 90, 210); SDL_RenderRect(game->renderer, &bar_rect); // Text SDL_FRect dst_text{ bar_x + bar_padding, y + bar_padding + bar_height_min/2 - (text_h*0.75f), text_w, text_h }; SDL_RenderTexture(game->renderer, tex, nullptr, &dst_text); SDL_DestroyTexture(tex); SDL_DestroySurface(surf); // Icon // Determine which payload we have // Now select the correct icon source bool draw_icon = false; SDL_FRect src; // ---- INVENTORY ITEM ---- if (item && item->type != ItemType::NONE) { auto it = Icon::item_pos.find(item->type); if (it != Icon::item_pos.end()) { src = SDL_FRect{ it->second.x * 16.0f, it->second.y * 16.0f, 16.0f, 16.0f }; draw_icon = true; } } // ---- RESOURCE ---- if (rtype && *rtype != ResourceType::NONE) { auto it = Icon::resource_pos.find(*rtype); if (it != Icon::resource_pos.end()) { src = SDL_FRect{ it->second.x * 16.0f, it->second.y * 16.0f, 16.0f, 16.0f }; draw_icon = true; } } // ---- VEGETABLE ---- if (vtype && *vtype != VegetableData::Type::NONE) { SDL_Point pos = VegetableData::get_IconTilePosByType(*vtype, is_seed); src = SDL_FRect{ pos.x * 16.0f, pos.y * 16.0f, 16.0f, 16.0f }; draw_icon = true; } // ---- DRAW IF AVAILABLE ---- if (draw_icon) { SDL_FRect icon_bg{ bar_x + total_width - icon_bg_size - bar_padding, y + (bar_h - icon_bg_size) * 0.5f, icon_bg_size, icon_bg_size }; SDL_SetRenderDrawColor(game->renderer, TOP_RIGHT_ICON_BG, 180); SDL_RenderFillRect(game->renderer, &icon_bg); SDL_SetRenderDrawColor(game->renderer, 0, 0, 0, 255); SDL_RenderRect(game->renderer, &icon_bg); SDL_FRect dst{ icon_bg.x, icon_bg.y, icon_bg_size, icon_bg_size }; SDL_RenderTexture(game->renderer, Icon::icons_texture, &src, &dst); } y += bar_h + spacing; } }