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_data.cpp raw
#include "building/building_data.hpp"
#include <SDL3_image/SDL_image.h>
#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<BuildingData_s> 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<BuildingData_s*>
get_CraftingTable() {
std::vector<BuildingData_s*> result;
for (auto& b : building_data)
if (is_CraftingTable(b.type))
result.push_back(&b);
return result;
}
std::vector<BuildingData_s*>
get_ResearchTable() {
std::vector<BuildingData_s*> 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<bool>& enabled_buildings) {
const int tile = Tile::tile_size;
const int tile_scale = 2;
std::vector<BuildingData_s*> research = BuildingData::get_ResearchTable();
std::vector<BuildingData_s*> 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<size_t>(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<float>(obj.position.x * tile);
c.sy = static_cast<float>(obj.position.y * tile);
c.w = static_cast<float>(obj.position.w * tile);
c.h = static_cast<float>(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<bool>& enabled_buildings) {
const int tile = Tile::tile_size;
const int tile_scale = 2;
std::vector<BuildingData_s*> research = BuildingData::get_ResearchTable();
std::vector<BuildingData_s*> 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<size_t>(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<float>(obj.position.w * tile * tile_scale);
float h = static_cast<float>(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<size_t>(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<float>(max_w);
const float menu_x = static_cast<float>(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<bool>& enabled_buildings) {
const int tile = Tile::tile_size;
std::vector<BuildingData_s*> research = BuildingData::get_ResearchTable();
std::vector<BuildingData_s*> 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<size_t>(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<float>(obj.position.w * tile);
cx += w + MENU_ITEM_SPACING;
}
if (cx - MENU_ITEM_SPACING > max_w)
max_w = static_cast<size_t>(cx - MENU_ITEM_SPACING);
}
if (max_w == 0)
max_w = 2 * tile + MENU_ITEM_SPACING;
const float menu_w = MENU_PADDING * 2 + static_cast<float>(max_w);
const float menu_x = static_cast<float>(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<bool>
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<size_t>(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<size_t>(p.y * Map::size.x + x);
if (Map::tile_map[map_id].overlays.size() > 0)
return true;
std::vector<TileObject> objects = Map::get_ObjectsFromMapTile(p);
std::vector<bool> 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<bool> 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<int>(Tile::tile_size)),
Scale(active_build.object.position.h*static_cast<int>(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<float>(active_build.object.position.x*static_cast<int>(Tile::tile_size)),
static_cast<float>(active_build.object.position.y*static_cast<int>(Tile::tile_size)),
static_cast<float>(active_build.object.position.w*static_cast<int>(Tile::tile_size)),
static_cast<float>(active_build.object.position.h*static_cast<int>(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);
}
}