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.gzsrc/building/building.cpp raw
#include "building/building.hpp"
#include "defaults/ui.hpp"
#include "map/map.hpp"
#include "building/research.hpp"
BuildingStatus
Building::construction_mode = BuildingStatus::CONSTRUCTING;
BuildingType
Building::selected_building_type = BuildingType::NONE;
int Building::draw_menu_curser_grid = -1;
int Building::draw_menu_curser_grid_active = -1;
void
Building::Draw(SDL_Renderer* renderer, SDL_FPoint position, bool is_transparent, int transparency) {
BuildingData_s* data = BuildingData::get_BuildingDataByType(building_type);
if (!data) return;
const bool is_deconstruct = (Building::construction_mode == BuildingStatus::DECONSTRUCT);
if (!is_deconstruct && (building_time <= 0.0f || data->building_duration <= 0.0f)) {
Object::Draw(renderer, position, is_transparent);
return;
}
float progress = 1.0f - (building_time / data->building_duration);
progress = std::clamp(progress, 0.0f, 1.0f);
int fill_r = 0, fill_g = 200, fill_b = 0;
if (is_deconstruct) {
progress = 1.0f - progress;
fill_r = 200; fill_g = 0; fill_b = 0;
}
if (!is_deconstruct)
transparency = 100.0f + (progress * 100.0f);
else
transparency = 200.0f - (progress * 100.0f);
const int tile = Tile::tile_size;
const float bar_w = (float)(dimension.x * tile) * 0.8f;
const float bar_h = (float)tile * 0.15f;
float bx = position.x + ((float)dimension.x * tile * 0.5f) - (bar_w * 0.5f);
float by = position.y - bar_h - 2.0f;
Object::Draw(renderer, position, true, transparency);
SDL_FRect bg{ Scale(bx + Map::offset.x), Scale(by + Map::offset.y), Scale(bar_w), Scale(bar_h) };
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_RenderFillRect(renderer, &bg);
float fill_w = bar_w * progress;
SDL_FRect fg{ Scale(bx + Map::offset.x), Scale(by + Map::offset.y), Scale(fill_w), Scale(bar_h) };
SDL_SetRenderDrawColor(renderer, fill_r, fill_g, fill_b, 220);
SDL_RenderFillRect(renderer, &fg);
// RESOURCE LIST
const float row_h = tile * 0.6f;
const float icon_sz = row_h;
const float pad = 2.0f;
const float gap_y = 2.0f;
float list_x = bx;
float list_y = by - row_h - 4.0f;
int rows = 0;
if (!is_deconstruct) {
for (const BuildingCost &rc : data->cost) {
int missing = rc.amount - cost_fulfilled[rc.type];
if (missing > 0) rows++;
}
} else {
for (const BuildingCost &rc : data->cost) {
int remaining = cost_fulfilled[rc.type];
if (remaining > 0) rows++;
}
}
if (rows == 0) return;
float total_h = rows * row_h + (rows - 1) * gap_y + 2;
SDL_FRect bg_all{
Scale(list_x + Map::offset.x),
Scale(list_y - (total_h - row_h) + Map::offset.y),
Scale(bar_w),
Scale(total_h)
};
SDL_SetRenderDrawColor(renderer, 0,0,0,transparency);
SDL_RenderFillRect(renderer, &bg_all);
float row_y = list_y;
auto render_row = [&](int value, ResourceType type) {
if (value <= 0) return;
auto it = Icon::resource_pos.find(type);
if (it == Icon::resource_pos.end()) return;
Icon::TilePos pos = it->second;
const float src_ts = 16.f;
SDL_FRect src{ pos.x * src_ts, pos.y * src_ts, src_ts, src_ts };
SDL_FRect dst{
Scale(list_x + pad + Map::offset.x),
Scale(row_y + pad - 4.0f + Map::offset.y),
Scale(icon_sz),
Scale(icon_sz)
};
SDL_RenderTexture(renderer, Icon::icons_texture, &src, &dst);
std::string t = std::to_string(value);
SDL_Surface* surf = TTF_RenderText_Blended(Ui::font_tiny, t.c_str(), t.length(), {255,255,255,255});
if (surf) {
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
if (tex) {
SDL_FRect r{
Scale(list_x + pad + icon_sz + 4.0f + Map::offset.x),
Scale(row_y + (row_h - surf->h) * 0.5f + 2.0f + Map::offset.y),
(float)surf->w,
(float)surf->h
};
SDL_RenderTexture(renderer, tex, nullptr, &r);
SDL_DestroyTexture(tex);
}
SDL_DestroySurface(surf);
}
row_y -= (row_h + gap_y);
};
// CONSTRUCTION
if (!is_deconstruct) {
for (const BuildingCost &rc : data->cost) {
int missing = rc.amount - cost_fulfilled[rc.type];
render_row(missing, rc.type); // rc.type is ResourceType
}
}
// DECONSTRUCTION
else {
std::vector<BuildingCost> cost_vec(data->cost.begin(), data->cost.end());
for (auto it = cost_vec.rbegin(); it != cost_vec.rend(); ++it) {
int remaining = cost_fulfilled[it->type];
render_row(remaining, it->type);
}
}
}
Building*
Building::get_BuildingById(size_t id) {
Object* obj = Object::get_ObjectById(id);
if (!obj) return nullptr;
return dynamic_cast<Building*>(obj);
}
bool
Building::Build(Inventory* inventory) {
BuildingData_s* data = BuildingData::get_BuildingDataByType(building_type);
if (!data || building_time <= 0.0f ) return false;
const float dt = Default::delta_time;
if (dt <= 0.0f) return false;
// Initialize cost_fulfilled if empty
if (cost_fulfilled.empty()) {
for (const BuildingCost& rc : data->cost)
cost_fulfilled[rc.type] = 0; // integer
}
// Compute remaining cost
std::unordered_map<ResourceType, float> remaining;
float total_required = 0.0f;
float total_remaining = 0.0f;
for (const BuildingCost& rc : data->cost) {
float rem = (float)rc.amount - (float)cost_fulfilled[rc.type];
if (rem < 0.0f) rem = 0.0f;
remaining[rc.type] = rem;
total_required += rc.amount;
total_remaining += rem;
}
if (total_remaining <= 0.0f) {
building_time = 0.0f;
return true;
}
// Compute per-second consumption rate
std::unordered_map<ResourceType, float> consumption_rate;
for (const BuildingCost& rc : data->cost)
consumption_rate[rc.type] = (float)rc.amount / data->building_duration;
// Desired consumption this tick
std::unordered_map<ResourceType, float> desired;
float redistribution_pool = 0.0f;
for (auto& kv : remaining) {
float want = consumption_rate[kv.first] * dt;
desired[kv.first] = std::min(want, kv.second);
}
// First pass: consume what is available
for (auto& kv : desired) {
ResourceType t = kv.first;
float want = kv.second;
float inv_have = (float)inventory->get_Resource(t);
float take = std::min(inv_have, want);
// Add fractionally
fractional_buffer[t] += take;
// Convert to integer only when possible
int consume_int = (int)fractional_buffer[t];
if (consume_int > 0) {
inventory->resources[t] -= consume_int;
cost_fulfilled[t] += consume_int;
fractional_buffer[t] -= consume_int;
}
// Anything not consumed goes to redistribution
if (take < want) {
redistribution_pool += want - take;
}
}
// Redistribution: proportional over remaining
if (redistribution_pool > 0.0f) {
float total_remaining_for_redistribution = 0.0f;
for (auto& kv : remaining)
total_remaining_for_redistribution += kv.second - desired[kv.first];
if (total_remaining_for_redistribution > 0.0f) {
for (auto& kv : remaining) {
float left = kv.second - desired[kv.first];
if (left <= 0.0f) continue;
float share = (left / total_remaining_for_redistribution) * redistribution_pool;
float inv_have = (float)inventory->get_Resource(kv.first);
float take = std::min(share, inv_have);
fractional_buffer[kv.first] += take;
int take_int = (int)fractional_buffer[kv.first];
if (take_int > 0) {
inventory->resources[kv.first] -= take_int;
cost_fulfilled[kv.first] += take_int;
fractional_buffer[kv.first] -= take_int;
}
}
}
}
// Recompute remaining for progress
total_remaining = 0.0f;
for (const BuildingCost& rc : data->cost) {
float rem = (float)rc.amount - (float)cost_fulfilled[rc.type];
if (rem < 0.0f) rem = 0.0f;
total_remaining += rem;
}
float progress = 1.0f - (total_remaining / total_required);
progress = std::clamp(progress, 0.0f, 1.0f);
building_time = data->building_duration * (1.0f - progress);
if (building_time <= 0.0f) {
building_time = 0.0f;
building_status = BuildingStatus::CONSTRUCTED;
return true;
}
return true;
}
bool
Building::Deconstruct(Inventory* inventory) {
BuildingData_s* data = BuildingData::get_BuildingDataByType(building_type);
if (!data)
return false;
if (building_time >= data->building_duration) {
std::cout << "DECONSTRUCTER!\n";
return true;
}
const float dt = Default::delta_time;
if (dt <= 0.0f) return false;
// Initialize cost_fulfilled if empty (unlikely)
if (cost_fulfilled.empty()) {
for (const BuildingCost& rc : data->cost)
cost_fulfilled[rc.type] = 0;
}
// Compute per-second deconstruction rate
std::unordered_map<ResourceType, float> deconstruct_rate;
for (const BuildingCost& rc : data->cost)
deconstruct_rate[rc.type] = (float)rc.amount / data->building_duration;
float total_remaining = 0.0f;
float total_required = 0.0f;
for (const BuildingCost& rc : data->cost) {
total_required += rc.amount;
total_remaining += rc.amount - cost_fulfilled[rc.type];
}
// Amount to return this tick
for (auto& kv : cost_fulfilled) {
ResourceType t = kv.first;
int used = kv.second;
if (used <= 0) continue;
float want = deconstruct_rate[t] * dt;
// Add fractionally
fractional_buffer[t] += want;
// Convert to integer only when possible
int give_back = std::min((int)fractional_buffer[t], used);
if (give_back > 0) {
inventory->resources[t] += give_back; // return to inventory
cost_fulfilled[t] -= give_back;
fractional_buffer[t] -= give_back;
}
}
// Recompute building progress
total_remaining = 0.0f;
for (const BuildingCost& rc : data->cost) {
total_remaining += rc.amount - cost_fulfilled[rc.type];
}
float progress = 1.0f - (total_remaining / total_required);
progress = std::clamp(progress, 0.0f, 1.0f);
building_time = data->building_duration * (1.0f - progress);
if (building_time > data->building_duration) {
building_time = data->building_duration;
std::cout << "DECONSTRUCTER!\n";
return true;
}
return false;
}
bool
Building::has_Menu() {
if ( static_cast<int>(category) >= static_cast<int>(BuildingType::CRAFTING_STONE_1) &&
static_cast<int>(category) <= static_cast<int>(BuildingType::RESEARCH_IRON_3))
return true;
return false;
}
CharacterMenuType
Building::open_Menu(Character* player) {
if (building_status != BuildingStatus::CONSTRUCTED)
return CharacterMenuType::NONE;
if (BuildingData::is_ResearchTable(building_type)) {
Research::research_buildings.clear();
Research::research_items.clear();
for (BuildingData_s data : BuildingData::building_data)
if (data.research_by == building_type && data.required_level <= player->level)
Research::research_buildings.push_back(data.type);
for (ItemStats data : Item::stats)
if (data.research_station == building_type && data.research_level <= static_cast<size_t>(player->level))
Research::research_items.push_back(data.type);
std::cout << "Open Research menu\n";
std::cout << "Build Items: " << Research::research_buildings.size() << std::endl;
std::cout << "Item Items: " << Research::research_items.size() << std::endl;
selected_building_type = building_type;
return CharacterMenuType::RESEARCH;
}
if (BuildingData::is_CraftingTable(building_type)) {
std::cout << "Open Crafting menu\n";
selected_building_type = building_type;
return CharacterMenuType::WORKBENCH;
}
return CharacterMenuType::NONE;
}
void
Building::close_Menu() {
std::cout << "Close Build menu\n";
Research::research_items.clear();
Research::research_buildings.clear();
}