#include "building/building_data.hpp" #include #include "defaults/ui.hpp" #include "map/map.hpp" const int MENU_PADDING = 16; const int MENU_TOP_PADDING = 10 + Ui::top_bar_height; const int MENU_ITEM_SPACING = 6; const int MENU_BG_COLOR_R = 0; const int MENU_BG_COLOR_G = 0; const int MENU_BG_COLOR_B = 0; const int MENU_BG_COLOR_A = 182; const int WORKBENCH_ROW_HEIGHTS[6] = {4, 3, 4, 4, 3, 4}; namespace BuildingData { SDL_Texture* workbench_texture; ActiveBuild active_build; // Keep the order of BuildingType enum! std::vector building_data = { { BuildingType::NONE, "None", 0, 0, 0, BuildingType::NONE, {}, TileType::TILE_TYPE_LAST, 0, 0 }, // CRAFTING TABLE { BuildingType::CRAFTING_STONE_1, "Crafting table Stone", 6.0, 3, 200, BuildingType::RESEARCH_STONE_1, {{ResourceType::STONE, 6}, {ResourceType::WOOD, 4}, {ResourceType::CLAY, 2}}, TileType::TILE_BUILDING, 5, 0 }, { BuildingType::CRAFTING_STONE_2, "Crafting table Stone", 10.0, 3, 400, BuildingType::RESEARCH_STONE_2, {{ResourceType::STONE, 20}, {ResourceType::WOOD, 7}, {ResourceType::CLAY, 7}}, TileType::TILE_BUILDING, 5, 1 }, { BuildingType::CRAFTING_STONE_3, "Crafting table Stone", 14.0, 3, 600, BuildingType::RESEARCH_STONE_3, {{ResourceType::STONE, 30}, {ResourceType::WOOD, 12}, {ResourceType::CLAY, 12}, {ResourceType::IRON, 4}}, TileType::TILE_BUILDING, 5, 2 }, { BuildingType::CRAFTING_WOOD_1, "Crafting table Wood", 6.0, 3, 1, BuildingType::RESEARCH_WOOD_1, {{ResourceType::WOOD, 8}, {ResourceType::CLAY, 2}}, TileType::TILE_BUILDING, 5, 4 }, { BuildingType::CRAFTING_WOOD_2, "Crafting table Wood", 10.0, 3, 300, BuildingType::RESEARCH_WOOD_2, {{ResourceType::WOOD, 20}, {ResourceType::CLAY, 10}, {ResourceType::STONE, 4}}, TileType::TILE_BUILDING, 5, 13 }, { BuildingType::CRAFTING_WOOD_3, "Crafting table Wood", 14.0, 3, 500, BuildingType::RESEARCH_WOOD_3, {{ResourceType::WOOD, 20}, {ResourceType::CLAY, 10}, {ResourceType::STONE, 4}, {ResourceType::IRON, 4}}, TileType::TILE_BUILDING, 5, 14 }, { BuildingType::CRAFTING_IRON_1, "Crafting table Iron", 6.0, 3, 400, BuildingType::RESEARCH_IRON_1, {{ResourceType::IRON, 10}, {ResourceType::WOOD, 4}}, TileType::TILE_BUILDING, 5, 5 }, { BuildingType::CRAFTING_IRON_2, "Crafting table Iron", 10.0, 3, 600, BuildingType::RESEARCH_IRON_2, {{ResourceType::IRON, 20}, {ResourceType::WOOD, 10}, {ResourceType::STONE, 4}}, TileType::TILE_BUILDING, 5, 12 }, { BuildingType::CRAFTING_IRON_3, "Crafting table Iron", 14.0, 3, 800, BuildingType::RESEARCH_IRON_3, {{ResourceType::IRON, 20}, {ResourceType::WOOD, 10}, {ResourceType::STONE, 4}, {ResourceType::CLAY, 4}}, TileType::TILE_BUILDING, 5, 16 }, // RESEARCH TABLE { BuildingType::RESEARCH_STONE_1, "Research table Stone", 6.0, 3, 200, BuildingType::RESEARCH_WOOD_1, {{ResourceType::STONE, 8}}, TileType::TILE_BUILDING, 5, 6 }, { BuildingType::RESEARCH_STONE_2, "Research table Stone", 10.0, 3, 400, BuildingType::RESEARCH_STONE_1, {{ResourceType::STONE, 14}, {ResourceType::WOOD, 4}, {ResourceType::CLAY, 4}}, TileType::TILE_BUILDING, 5, 11 }, { BuildingType::RESEARCH_STONE_3, "Research table Stone", 14.0, 3, 600, BuildingType::RESEARCH_STONE_2, {{ResourceType::STONE, 20}, {ResourceType::WOOD, 6}, {ResourceType::CLAY, 4}, {ResourceType::IRON, 4}}, TileType::TILE_BUILDING, 5, 3 }, { BuildingType::RESEARCH_WOOD_1, "Research table Wood", 6.0, 3, 1, BuildingType::NONE, {{ResourceType::WOOD, 8}, {ResourceType::CLAY, 4}}, TileType::TILE_BUILDING, 5, 7 }, { BuildingType::RESEARCH_WOOD_2, "Research table Wood", 10.0, 3, 300, BuildingType::RESEARCH_WOOD_2, {{ResourceType::WOOD, 14}, {ResourceType::CLAY, 4}, {ResourceType::STONE, 4}}, TileType::TILE_BUILDING, 5, 10 }, { BuildingType::RESEARCH_WOOD_3, "Research table Wood", 14.0, 3, 500, BuildingType::RESEARCH_WOOD_3, {{ResourceType::WOOD, 20}, {ResourceType::CLAY, 6}, {ResourceType::STONE, 4}, {ResourceType::IRON, 4}}, TileType::TILE_BUILDING, 5, 15 }, { BuildingType::RESEARCH_IRON_1, "Research table Iron", 6.0, 3, 400, BuildingType::RESEARCH_STONE_1, {{ResourceType::IRON, 8}, {ResourceType::WOOD, 4}}, TileType::TILE_BUILDING, 5, 8 }, { BuildingType::RESEARCH_IRON_2, "Research table Iron", 10.0, 3, 600, BuildingType::RESEARCH_IRON_1, {{ResourceType::IRON, 14}, {ResourceType::WOOD, 3}, {ResourceType::STONE, 4}}, TileType::TILE_BUILDING, 5, 9 }, { BuildingType::RESEARCH_IRON_3, "Research table Iron", 14.0, 3, 800, BuildingType::RESEARCH_IRON_2, {{ResourceType::IRON, 20}, {ResourceType::WOOD, 6}, {ResourceType::STONE, 4}, {ResourceType::CLAY, 4}}, TileType::TILE_BUILDING, 5, 17 }, }; bool Init(SDL_Renderer* renderer) { workbench_texture = IMG_LoadTexture(renderer, "Pixel_Crawler/Environment/Structures/Stations/Workbench/Workbench.png"); if (!workbench_texture) { std::cerr << "Failed to load workbench_texture!\n"; return false; } return true; } int get_WorkbenchLevel(BuildingType type) { switch (type) { case BuildingType::CRAFTING_STONE_1: case BuildingType::RESEARCH_STONE_1: case BuildingType::CRAFTING_WOOD_1: case BuildingType::RESEARCH_WOOD_1: case BuildingType::CRAFTING_IRON_1: case BuildingType::RESEARCH_IRON_1: return 1; case BuildingType::CRAFTING_STONE_2: case BuildingType::RESEARCH_STONE_2: case BuildingType::CRAFTING_WOOD_2: case BuildingType::RESEARCH_WOOD_2: case BuildingType::CRAFTING_IRON_2: case BuildingType::RESEARCH_IRON_2: return 2; case BuildingType::CRAFTING_STONE_3: case BuildingType::RESEARCH_STONE_3: case BuildingType::CRAFTING_WOOD_3: case BuildingType::RESEARCH_WOOD_3: case BuildingType::CRAFTING_IRON_3: case BuildingType::RESEARCH_IRON_3: return 3; default: return -1; } } BuildingData_s* get_BuildingDataByType(BuildingType type) { for (auto& b : building_data) if (b.type == type) return &b; return nullptr; } std::vector get_CraftingTable() { std::vector result; for (auto& b : building_data) if (is_CraftingTable(b.type)) result.push_back(&b); return result; } std::vector get_ResearchTable() { std::vector result; for (auto& b : building_data) if (is_ResearchTable(b.type)) result.push_back(&b); return result; } void draw_WorkbenchMenu(SDL_Renderer* renderer, SDL_Rect win_size, std::vector& enabled_buildings) { const int tile = Tile::tile_size; const int tile_scale = 2; std::vector research = BuildingData::get_ResearchTable(); std::vector crafting = BuildingData::get_CraftingTable(); struct Cell { bool enabled; float w, h; float sx, sy; }; Cell cells[6][3]; size_t active_id = 100; // PRECOMPUTE EVERYTHING for (size_t row = 0; row < 6; ++row) { for (size_t col = 0; col < 3; ++col) { BuildingData_s* data = (row < 3) ? research[row * 3 + col] : crafting[(row - 3) * 3 + col]; Cell& c = cells[row][col]; c.enabled = enabled_buildings[static_cast(data->type)]; if (!c.enabled) continue; if (data->type == active_build.type) active_id = row * 3 + col; TilesetObject obj = Tile::get_ObjectFromTileSet( data->tileset_id, data->tileset_object_id, data->tileset_type ); c.sx = static_cast(obj.position.x * tile); c.sy = static_cast(obj.position.y * tile); c.w = static_cast(obj.position.w * tile); c.h = static_cast(obj.position.h * tile); } } // COMPUTE MENU WIDTH size_t max_w = 0; for (size_t row = 0; row < 6; ++row) { size_t row_w = 0; for (size_t col = 0; col < 3; ++col) { const Cell& c = cells[row][col]; if (!c.enabled) continue; row_w += (size_t)(c.w * tile_scale); if (col < 2) row_w += MENU_ITEM_SPACING; } if (row_w > max_w) max_w = row_w; } if (max_w == 0) max_w = 2 * tile + MENU_ITEM_SPACING; const float MENU_W = MENU_PADDING * 2 + (float)max_w; const float menu_x = (float)win_size.w - MENU_W; const float MENU_H = (float)win_size.h; // BACKGROUND SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, MENU_BG_COLOR_R, MENU_BG_COLOR_G, MENU_BG_COLOR_B, MENU_BG_COLOR_A ); SDL_FRect bg = { menu_x, 0, MENU_W, MENU_H }; SDL_RenderFillRect(renderer, &bg); // DRAW float cy = MENU_PADDING + MENU_TOP_PADDING; for (size_t row = 0; row < 6; ++row) { bool has_enabled = false; float cx = menu_x + MENU_PADDING; for (size_t col = 0; col < 3; ++col) { const Cell& c = cells[row][col]; if (!c.enabled) continue; has_enabled = true; SDL_FRect src = { c.sx, c.sy, c.w, c.h }; SDL_FRect dst = { cx, cy, c.w * tile_scale, c.h * tile_scale }; // higlight cell if (active_id == row * 3 + col) { SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 255, 255, 0, 100); SDL_RenderRect(renderer, &dst); SDL_SetRenderDrawColor(renderer, 255, 255, 0, 40); SDL_RenderFillRect(renderer, &dst); } SDL_RenderTexture(renderer, BuildingData::workbench_texture, &src, &dst); cx += c.w * tile_scale + MENU_ITEM_SPACING; } if (has_enabled) { cy += WORKBENCH_ROW_HEIGHTS[row] * tile * tile_scale + MENU_ITEM_SPACING; } } } BuildingType menu_DetectWorkbenchClick(const Input& input, SDL_Rect win_size, std::vector& enabled_buildings) { const int tile = Tile::tile_size; const int tile_scale = 2; std::vector research = BuildingData::get_ResearchTable(); std::vector crafting = BuildingData::get_CraftingTable(); struct Cell { BuildingData_s* data; float x, y, w, h; }; Cell cells[6][3]; size_t max_w = 0; // PRECOMPUTE CELLS float cy = MENU_PADDING + MENU_TOP_PADDING; for (size_t row = 0; row < 6; ++row) { float cx = MENU_PADDING; bool has_enabled = false; for (size_t col = 0; col < 3; ++col) { BuildingData_s* data = (row < 3) ? research[row * 3 + col] : crafting[(row - 3) * 3 + col]; bool is_enabled = enabled_buildings[static_cast(data->type)]; if (!is_enabled) { cells[row][col] = {nullptr, 0, 0, 0, 0}; continue; } TilesetObject obj = Tile::get_ObjectFromTileSet( data->tileset_id, data->tileset_object_id, data->tileset_type ); float w = static_cast(obj.position.w * tile * tile_scale); float h = static_cast(obj.position.h * tile * tile_scale); cells[row][col] = {data, cx, cy, w, h}; cx += w + MENU_ITEM_SPACING; has_enabled = true; } if (has_enabled) cy += WORKBENCH_ROW_HEIGHTS[row] * tile * tile_scale + MENU_ITEM_SPACING; if (cx - MENU_ITEM_SPACING > max_w) max_w = static_cast(cx - MENU_ITEM_SPACING); } if (max_w == 0) max_w = 2 * tile *tile_scale + MENU_ITEM_SPACING; const float menu_w = MENU_PADDING * 2 + static_cast(max_w); const float menu_x = static_cast(win_size.w) - menu_w; const float menu_y = 0.0f; SDL_FPoint mouse = input.mouse_state_raw; // check if mouse is inside menu if (mouse.x < menu_x || mouse.x > menu_x + menu_w || mouse.y < menu_y || mouse.y > menu_y + win_size.h) return BuildingType::NONE; // detect clicked cell for (size_t row = 0; row < 6; ++row) { for (size_t col = 0; col < 3; ++col) { Cell& c = cells[row][col]; if (!c.data) continue; float x0 = menu_x + c.x + MENU_PADDING; float y0 = menu_y + c.y; if (mouse.x >= x0 && mouse.x <= x0 + c.w && mouse.y >= y0 && mouse.y <= y0 + c.h) { return c.data->type; } } } return BuildingType::NONE; } bool menu_Hovering(const Input& input, SDL_Rect win_size, std::vector& enabled_buildings) { const int tile = Tile::tile_size; std::vector research = BuildingData::get_ResearchTable(); std::vector crafting = BuildingData::get_CraftingTable(); size_t max_w = 0; for (size_t row = 0; row < 6; ++row) { float cx = MENU_PADDING; for (size_t col = 0; col < 3; ++col) { BuildingData_s* data = (row < 3) ? research[row * 3 + col] : crafting[(row - 3) * 3 + col]; bool is_enabled = enabled_buildings[static_cast(data->type)]; if (!is_enabled) continue; TilesetObject obj = Tile::get_ObjectFromTileSet( data->tileset_id, data->tileset_object_id, data->tileset_type ); float w = static_cast(obj.position.w * tile); cx += w + MENU_ITEM_SPACING; } if (cx - MENU_ITEM_SPACING > max_w) max_w = static_cast(cx - MENU_ITEM_SPACING); } if (max_w == 0) max_w = 2 * tile + MENU_ITEM_SPACING; const float menu_w = MENU_PADDING * 2 + static_cast(max_w); const float menu_x = static_cast(win_size.w) - menu_w; const float menu_y = 0.0f; const float menu_h = (float)win_size.h; SDL_FPoint mouse = input.mouse_state_raw; return mouse.x >= menu_x && mouse.x <= menu_x + menu_w && mouse.y >= menu_y && mouse.y <= menu_y + menu_h; } bool is_BuildingCollidingWithObject() { SDL_Point position = active_build.tile_position; TilesetObject building = active_build.object; std::vector collision_map = Tile::get_ObjectCollisionsFromTileset(building.tileset_info.set_id, building.tileset_info.object_id, Tile::get_Type(building.tileset_info.set_category)); int start_cx = std::floor(position.x * Tile::tile_size / float(Tile::tile_collider_size)); int end_cx = std::ceil(((position.x + building.position.w * Tile::tile_size) / float(Tile::tile_collider_size))); int start_cy = std::floor(position.y * Tile::tile_size / float(Tile::tile_collider_size)); int end_cy = std::ceil(((position.y + building.position.h * Tile::tile_size ) / float(Tile::tile_collider_size))); int map_width_in_colliders = Map::size.x * (Tile::tile_size / Tile::tile_collider_size); // For each map tile collider cell size_t i = 0; for (int cy = start_cy; cy < end_cy; ++cy) { for (int cx = start_cx; cx < end_cx; ++cx, i++) { size_t index = static_cast(cy * map_width_in_colliders + cx); if (index >= Map::tile_collission_map.size()) continue; if (collision_map[i] && Map::tile_collission_map[index]) return true; } } // for each object collider cell for (int x = 0; x < building.position.w; x++) { for (int y = 0; y < building.position.h; y++) { SDL_Point p = {position.x+x, position.y+y}; if ( Tile::get_FloorType(p) != TileTypeFloors::MEADOW && Tile::get_FloorType(p) != TileTypeFloors::BRICKS ) return true; size_t map_id = static_cast(p.y * Map::size.x + x); if (Map::tile_map[map_id].overlays.size() > 0) return true; std::vector objects = Map::get_ObjectsFromMapTile(p); std::vector c_b = Tile::get_CollissionsOfTileFromObject(building, {x, y}); for (TileObject& o : objects) { Object* oo = Object::get_ObjectById(o.object_id); // oo->print_CollisionMap(); std::vector c_o = oo->get_CollissionsOfTile(o.position); for (size_t i = 0; i < c_o.size(); i++) if (c_o[i] && c_b[i]) return true; } } } return false; } bool snap_BuildPosition(float in_x, float in_y) { float tile = Tile::tile_size; float w = active_build.object.position.w * tile; float h = active_build.object.position.h * tile; float dst_x = in_x - w * 0.5f; float dst_y = in_y - h; float snapped_x = std::round(dst_x / tile) * tile; float snapped_y = std::floor(dst_y / tile) * tile; while (snapped_y + tile > in_y) { snapped_y -= tile; } active_build.tile_position = Map::get_TileFromXy({snapped_x, snapped_y}); if (active_build.tile_position.x >= Map::size.x || active_build.tile_position.y >= Map::size.y) return false; active_build.world_position = {snapped_x, snapped_y}; return true; } void update_ActiveBuildData(float pos_x, float pos_y) { if (!snap_BuildPosition(pos_x, pos_y)) return; active_build.is_colliding = is_BuildingCollidingWithObject(); } void draw_SelectedWorkbenchAt(SDL_Renderer* renderer) { BuildingType t = active_build.type; if (t == BuildingType::NONE) return; float snapped_x = active_build.world_position.x; float snapped_y = active_build.world_position.y; SDL_FRect dst = { Scale(snapped_x + Map::offset.x), Scale(snapped_y + Map::offset.y), Scale(active_build.object.position.w*static_cast(Tile::tile_size)), Scale(active_build.object.position.h*static_cast(Tile::tile_size)) }; // -------- RED BACKGROUND WHEN COLLIDING ---------- if (active_build.is_colliding) { SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(renderer, 255, 0, 0, 40); SDL_RenderFillRect(renderer, &dst); } // -------------------------------------------------- // Draw the workbench sprite on top SDL_FRect src = { static_cast(active_build.object.position.x*static_cast(Tile::tile_size)), static_cast(active_build.object.position.y*static_cast(Tile::tile_size)), static_cast(active_build.object.position.w*static_cast(Tile::tile_size)), static_cast(active_build.object.position.h*static_cast(Tile::tile_size)) }; SDL_SetTextureAlphaMod(BuildingData::workbench_texture, active_build.is_colliding ? 80 : 160); SDL_RenderTexture(renderer, BuildingData::workbench_texture, &src, &dst); SDL_SetTextureAlphaMod(BuildingData::workbench_texture, 255); } }