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/map/map.cpp raw
#include "map/map.hpp"
#include <cstddef>
#include <iostream>
#include <queue>
#include <set>
#include "building/building_data.hpp"
#include "resource/resource.hpp"
#include "building/building.hpp"
#include "game/game.hpp"
#include "farm/farm.hpp"
#include "bird/bird.hpp"
namespace Map {
// save_MapTiles
std::string save_path = "saves/save.map";
std::string save_collission_path = "saves/save.collissions";
bool setting_offset = false;
SDL_Point offset_to_set = {0, 0};
SDL_Point offset = {0, 0};
SDL_Point size = {160, 100}; // map size in tiles...
SDL_Point boundaries = {63, 39}; // JEee fucked up the size,
// to much work to resize (i have an outer cage which fucks the path finding)
std::vector<TileMap> tile_map;
std::vector<bool> tile_collission_map;
int frame_water = 0;
float global_frame_time = 0.0f;
float global_frame_time_max = 0.6f;
int frame_meadow = 0;
float frame_meadow_time = 0.0f;
float frame_meadow_time_max = 0.6f;
void
Init() {
tile_map.resize(static_cast<size_t>(Map::size.x * Map::size.y));
for (int y = 0; y < Map::size.y; ++y) {
for (int x = 0; x < Map::size.x; ++x) {
bool is_border =
(x == 0) || (y == 0) ||
(x == Map::size.x - 1) ||
(y == Map::size.y - 1);
size_t index = static_cast<size_t>(y * Map::size.x + x);
tile_map[index].background = { 0, SIZE_MAX, TILE_GROUND, {is_border ? 17 : 0, 0} };
//tile_map[index].overlay = { SIZE_MAX, SIZE_MAX, TILE_OVERLAY_CHARACTER, {0, 0} };
}
}
}
void
init_Collider() {
tile_collission_map.clear();
// init map_colliders
const size_t cells_per_tile_map = (Tile::tile_size / Tile::tile_collider_size)*(Tile::tile_size / Tile::tile_collider_size);
for (size_t i = 0; i < tile_map.size(); i++) {
for (size_t j = 0; j < cells_per_tile_map; j++)
tile_collission_map.push_back(false);
}
}
void
Reset() {
tile_map.clear();
Init();
init_Collider();
}
void
set_CollisionForTile(const TileMap &tile_map_item,
size_t map_tile_x,
size_t map_tile_y) {
const size_t cells_per_tile = Tile::tile_size / Tile::tile_collider_size;
const size_t map_width_cells = static_cast<size_t>(Map::size.x) * cells_per_tile;
const Tile_s &tile = tile_map_item.background;
const SDL_Point position = tile.set_position;
const TileSet &tileset = Tile::get_TilesetFromMapTile(tile);
const size_t ts_width = static_cast<size_t>(tileset.size.x) * cells_per_tile;
size_t map_cell_origin_x = map_tile_x * cells_per_tile;
size_t map_cell_origin_y = map_tile_y * cells_per_tile;
for (size_t y = 0; y < cells_per_tile; ++y) {
for (size_t x = 0; x < cells_per_tile; ++x) {
size_t ts_index =
(static_cast<size_t>(position.y) * cells_per_tile + y) * ts_width +
(static_cast<size_t>(position.x) * cells_per_tile + x);
if (ts_index >= tileset.collission_map.size())
continue;
size_t map_x = map_cell_origin_x + x;
size_t map_y = map_cell_origin_y + y;
size_t map_index = map_y * map_width_cells + map_x;
if (map_index < tile_collission_map.size())
tile_collission_map[map_index] = tileset.collission_map[ts_index];
}
}
}
void
set_CollissionFromTileset() {
for (int tile_index = 0; tile_index < static_cast<int>(tile_map.size()); ++tile_index) {
int map_tile_x = tile_index % Map::size.x;
int map_tile_y = tile_index / Map::size.x;
set_TileCollissionForObjects({map_tile_x, map_tile_y});
}
}
void
set_ObjectCollisionOnTilemap(size_t object_id, SDL_Point map_tile) {
Object* object = Object::get_ObjectById(object_id);
object->print_CollisionMap();
const std::vector<bool>& collision_map = object->get_CollisionMap();
const size_t scale = Tile::tile_size / Tile::tile_collider_size;
const size_t obj_cols = static_cast<size_t>(object->dimension.x) * scale;
const size_t obj_rows = static_cast<size_t>(object->dimension.y) * scale;
std::cout << "DIM: " << object->dimension.x << " " << object->dimension.y << std::endl;
std::cout << "COL: " << obj_cols << " " << obj_rows << std::endl;
const size_t base_x = static_cast<size_t>(map_tile.x) * scale;
const size_t base_y = static_cast<size_t>(map_tile.y) * scale;
std::cout << "BAS: " << base_x << " " << base_y << std::endl;
const size_t map_cols = static_cast<size_t>(Map::size.x) * scale;
for (size_t oy = 0; oy < obj_rows; ++oy) {
for (size_t ox = 0; ox < obj_cols; ++ox) {
const size_t obj_idx = oy * obj_cols + ox;
if (collision_map[obj_idx]) {
const size_t mx = base_x + ox;
const size_t my = base_y + oy;
const size_t map_idx = my * map_cols + mx;
std::cout << "COL: " << mx << " " << my << " " << map_idx << std::endl;
Map::tile_collission_map[map_idx] = true;
}
}
}
}
void
set_TileCollissionForObjects(SDL_Point map_tile) {
const size_t tile_index = static_cast<size_t>(map_tile.y * Map::size.x + map_tile.x);
const size_t scale = Tile::tile_size / Tile::tile_collider_size;
const size_t start_y = static_cast<size_t>(map_tile.y)* scale;
const size_t start_x = static_cast<size_t>(map_tile.x)* scale;
const size_t map_cols = static_cast<size_t>(Map::size.x) * scale;
const TileMap &tile_map_item = tile_map[tile_index];
// reset collisions for this tile
set_CollisionForTile(tile_map_item, static_cast<size_t>(map_tile.x), static_cast<size_t>(map_tile.y));
// merge collisions from all objects on the tile
for (const TileObject& to : tile_map[tile_index].objects) {
Object* object = Object::get_ObjectById(to.object_id);
const std::vector<bool> collision_map =
object->get_CollissionsOfTile(to.position);
for (size_t oy = 0; oy < scale; ++oy) {
for (size_t ox = 0; ox < scale; ++ox) {
const size_t tile_cell = oy * scale + ox;
if (collision_map[tile_cell]) {
const size_t map_idx =
(start_y + oy) * map_cols + (start_x + ox);
Map::tile_collission_map[map_idx] = true;
}
}
}
}
}
void
save_TileCollision(const std::string &path) {
std::ofstream out(path, std::ios::binary);
if (!out) {
std::cerr << "Failed to open file for writing: " << path << '\n';
return;
}
out.write(reinterpret_cast<const char*>(&Map::size.x), sizeof(Map::size.x));
out.write(reinterpret_cast<const char*>(&Map::size.y), sizeof(Map::size.y));
// Write the size first
size_t size = tile_collission_map.size();
out.write(reinterpret_cast<const char*>(&size), sizeof(size));
// Write each bool as a byte
for (bool value : tile_collission_map) {
uint8_t byte = value ? 1 : 0;
out.write(reinterpret_cast<const char*>(&byte), sizeof(byte));
}
std::cout << "Saved " << size << " collision cells to " << path << '\n';
}
void
load_TileCollision(const std::string &path) {
std::ifstream in(path, std::ios::binary);
if (!in) {
std::cerr << "Failed to open file for reading: " << path << '\n';
return;
}
int map_tilesize_x;
int map_tilesize_y;
in.read(reinterpret_cast<char*>(&map_tilesize_x), sizeof(map_tilesize_x));
in.read(reinterpret_cast<char*>(&map_tilesize_y), sizeof(map_tilesize_y));
std::cout << "WUU " << map_tilesize_x << " " << map_tilesize_y<< std::endl;
// Read the size
size_t size = 0;
in.read(reinterpret_cast<char*>(&size), sizeof(size));
if (!in) {
std::cerr << "Failed to read size from file: " << path << '\n';
return;
}
// Resize and read data
std::vector<bool> temp(size);
const size_t w = static_cast<size_t>(Map::size.x);
const size_t h = static_cast<size_t>(Map::size.y);
const size_t m = Tile::tile_size/Tile::tile_collider_size;
Map::tile_collission_map.assign(w * h * m * m, false);
for (size_t i = 0; i < size; ++i) {
uint8_t byte = 0;
in.read(reinterpret_cast<char*>(&byte), sizeof(byte));
temp[i] = (byte != 0);
size_t x = i% static_cast<size_t>(map_tilesize_x);
size_t y = i/ static_cast<size_t>(map_tilesize_x);
Map::tile_collission_map[y*static_cast<size_t>(Map::size.x)+x] = temp[i];
}
if (!in) {
std::cerr << "File corrupted or incomplete: " << path << '\n';
return;
}
//Map::tile_collission_map = std::move(temp);
std::cout << "Loaded " << size << " collision cells from " << path << '\n';
}
void
save_MapTiles(const std::vector<TileMap>& tile_map, const std::string& path) {
std::ofstream file(path, std::ios::binary);
if (!file.is_open()) return;
size_t map_count = tile_map.size();
file.write(reinterpret_cast<const char*>(&map_count), sizeof(map_count));
size_t id = 0;
for (const auto& map : tile_map) {
// --- background ---
size_t map_tile_x = id % static_cast<size_t>(Map::size.x);
size_t map_tile_y = id / static_cast<size_t>(Map::size.x);
file.write(reinterpret_cast<const char*>(&map_tile_x), sizeof(map_tile_x));
file.write(reinterpret_cast<const char*>(&map_tile_y), sizeof(map_tile_y));
file.write(reinterpret_cast<const char*>(&map.background.set_id), sizeof(map.background.set_id));
file.write(reinterpret_cast<const char*>(&map.background.object_id), sizeof(map.background.object_id));
file.write(reinterpret_cast<const char*>(&map.background.set_category), sizeof(map.background.set_category));
file.write(reinterpret_cast<const char*>(&map.background.set_position), sizeof(map.background.set_position));
// --- overlays ---
const std::size_t overlay_count = map.overlays.size();
file.write(reinterpret_cast<const char*>(&overlay_count), sizeof(overlay_count));
for (const Tile_s& ov : map.overlays) {
file.write(reinterpret_cast<const char*>(&ov.set_id), sizeof(ov.set_id));
file.write(reinterpret_cast<const char*>(&ov.object_id), sizeof(ov.object_id));
file.write(reinterpret_cast<const char*>(&ov.set_category), sizeof(ov.set_category));
file.write(reinterpret_cast<const char*>(&ov.set_position), sizeof(ov.set_position));
}
// --- objects ---
size_t obj_count = map.objects.size();
file.write(reinterpret_cast<const char*>(&obj_count), sizeof(obj_count));
for (const auto& obj : map.objects) {
Object* o = Object::get_ObjectById(obj.object_id);
// TilesetObject
file.write(reinterpret_cast<const char*>(&obj.position.x), sizeof(obj.position.x));
file.write(reinterpret_cast<const char*>(&obj.position.y), sizeof(obj.position.y));
file.write(reinterpret_cast<const char*>(&o->dimension.x), sizeof(o->dimension.x));
file.write(reinterpret_cast<const char*>(&o->dimension.y), sizeof(o->dimension.y));
file.write(reinterpret_cast<const char*>(&obj.draw_from_this_tile), sizeof(obj.draw_from_this_tile));
auto object_type = static_cast<int>(o->object_type);
auto category = static_cast<int>(o->category);
file.write(reinterpret_cast<const char*>(&object_type), sizeof(object_type));
file.write(reinterpret_cast<const char*>(&category), sizeof(category));
// im getting tile_type on load
int tileset_type = static_cast<int>(o->tileset_type);
file.write(reinterpret_cast<const char*>(&tileset_type), sizeof(tileset_type));
file.write(reinterpret_cast<const char*>(&o->tileset_id), sizeof(o->tileset_id));
file.write(reinterpret_cast<const char*>(&o->tileset_object_id), sizeof(o->tileset_object_id));
file.write(reinterpret_cast<const char*>(&o->z_index), sizeof(o->z_index));
if (o->category == ObjectCategory::BUILDING) {
Building* b = static_cast<Building*>(o);
auto type = static_cast<int>(b->building_type);
auto status = static_cast<int>(b->building_status);
file.write(reinterpret_cast<const char*>(&type), sizeof(type));
file.write(reinterpret_cast<const char*>(&status), sizeof(status));
file.write(reinterpret_cast<const char*>(&b->building_time),
sizeof(b->building_time));
size_t cost_count = b->cost_fulfilled.size();
file.write(reinterpret_cast<const char*>(&cost_count), sizeof(cost_count));
for (auto& p : b->cost_fulfilled) {
auto r = static_cast<int>(p.first);
file.write(reinterpret_cast<const char*>(&r), sizeof(r));
file.write(reinterpret_cast<const char*>(&p.second),
sizeof(p.second));
}
size_t frac_count = b->fractional_buffer.size();
file.write(reinterpret_cast<const char*>(&frac_count), sizeof(frac_count));
for (auto& p : b->fractional_buffer) {
auto r = static_cast<int>(p.first);
file.write(reinterpret_cast<const char*>(&r), sizeof(r));
file.write(reinterpret_cast<const char*>(&p.second),
sizeof(p.second));
}
}
else if (o->category == ObjectCategory::FARM_CROP) {
Vegetable* v = static_cast<Vegetable*>(o);
auto type = static_cast<int>(v->type);
file.write(reinterpret_cast<const char*>(&type), sizeof(type));
file.write(reinterpret_cast<const char*>(&v->is_seed),
sizeof(v->is_seed));
file.write(reinterpret_cast<const char*>(&v->quantity),
sizeof(v->quantity));
file.write(reinterpret_cast<const char*>(&v->growth_stage),
sizeof(v->growth_stage));
file.write(reinterpret_cast<const char*>(&v->next_update_sec),
sizeof(v->next_update_sec));
file.write(reinterpret_cast<const char*>(&v->collecting_duration_max),
sizeof(v->collecting_duration_max));
file.write(reinterpret_cast<const char*>(&v->tileset_object_x),
sizeof(v->tileset_object_x));
file.write(reinterpret_cast<const char*>(&v->tileset_object_y),
sizeof(v->tileset_object_y));
auto slot = static_cast<int>(v->equipement_slot);
file.write(reinterpret_cast<const char*>(&slot), sizeof(slot));
}
}
id++;
}
}
std::vector<TileMap>
load_MapTiles(const std::string& path) {
std::vector<TileMap> tile_map;
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) return tile_map;
size_t map_count = 0;
file.read(reinterpret_cast<char*>(&map_count), sizeof(map_count));
tile_map.resize(static_cast<size_t>(Map::size.y*Map::size.x));
for (size_t id = 0; id < map_count; id++) {
//int x = i % static_cast<size_t>(Map::size.x);
//int y = i / static_cast<size_t>(Map::size.x);
size_t x;
size_t y;
file.read(reinterpret_cast<char*>(&x), sizeof(x));
file.read(reinterpret_cast<char*>(&y), sizeof(y));
auto& map = tile_map[y*static_cast<size_t>(Map::size.x)+x];
// --- background ---
file.read(reinterpret_cast<char*>(&map.background.set_id), sizeof(map.background.set_id));
file.read(reinterpret_cast<char*>(&map.background.object_id), sizeof(map.background.object_id));
file.read(reinterpret_cast<char*>(&map.background.set_category), sizeof(map.background.set_category));
file.read(reinterpret_cast<char*>(&map.background.set_position), sizeof(map.background.set_position));
// --- overlays ---
std::size_t overlay_count = 0;
file.read(reinterpret_cast<char*>(&overlay_count), sizeof(overlay_count));
map.overlays.clear();
map.overlays.resize(overlay_count);
for (std::size_t i = 0; i < overlay_count; ++i) {
Tile_s& ov = map.overlays[i];
file.read(reinterpret_cast<char*>(&ov.set_id), sizeof(ov.set_id));
file.read(reinterpret_cast<char*>(&ov.object_id), sizeof(ov.object_id));
file.read(reinterpret_cast<char*>(&ov.set_category), sizeof(ov.set_category));
file.read(reinterpret_cast<char*>(&ov.set_position), sizeof(ov.set_position));
}
TileSet& tileset = Tile::get_TilesetFromMapTile(map.background);
map.background.object_id = static_cast<size_t>(map.background.set_position.y*tileset.size.x + map.background.set_position.x);
map.background.set_category = Tile::tilesets[TILE_BACKGROUND][map.background.set_id].category;
// std::cout << "SCM: " << map.background.set_id << " " << map.background.object_id<< std::endl;
// --- objects ---
size_t obj_count = 0;
file.read(reinterpret_cast<char*>(&obj_count), sizeof(obj_count));
map.objects.resize(obj_count);
for (auto& map_object : map.objects) {
TilesetObject obj;
// TilesetObject
file.read(reinterpret_cast<char*>(&obj.position.x), sizeof(obj.position.x));
file.read(reinterpret_cast<char*>(&obj.position.y), sizeof(obj.position.y));
file.read(reinterpret_cast<char*>(&obj.position.w), sizeof(obj.position.w));
file.read(reinterpret_cast<char*>(&obj.position.h), sizeof(obj.position.h));
file.read(reinterpret_cast<char*>(&obj.draw_from_this_tile),
sizeof(obj.draw_from_this_tile));
int object_type;
file.read(reinterpret_cast<char*>(&object_type), sizeof(object_type));
int cat_i;
file.read(reinterpret_cast<char*>(&cat_i), sizeof(cat_i));
ObjectCategory category = static_cast<ObjectCategory>(cat_i);
int tt;
file.read(reinterpret_cast<char*>(&tt), sizeof(tt));
TileType tileset_type = static_cast<TileType>(tt);
file.read(reinterpret_cast<char*>(&obj.tileset_info.set_id),
sizeof(obj.tileset_info.set_id));
file.read(reinterpret_cast<char*>(&obj.tileset_info.object_id),
sizeof(obj.tileset_info.object_id));
file.read(reinterpret_cast<char*>(&obj.z_index), sizeof(obj.z_index));
// SET DATA TO REAL OBJECT AND MAP OBJECT
SDL_Point wp = {static_cast<int>(x)-obj.position.x, static_cast<int>(y)-obj.position.y};
Object* o = Object::get_ObjectByWorldPositionAndTilesetObjectID(wp, obj.tileset_info.object_id);
Object* on = nullptr;
SDL_Point dim = {obj.position.w, obj.position.h};
switch (category) {
case ObjectCategory::STATIC: {
on = new Static(
static_cast<ObjectType>(object_type),
ObjectCategory::STATIC,
tileset_type,
obj.tileset_info.set_id,
obj.tileset_info.object_id,
wp,
dim,
obj.z_index);
break;
}
case ObjectCategory::RESOURCE: {
Resource_s r = Tile::get_ResourceFromTileSet(
obj.tileset_info.set_id,
obj.tileset_info.object_id);
on = new Resource(
static_cast<ObjectType>(object_type),
ObjectCategory::RESOURCE,
tileset_type,
obj.tileset_info.set_id,
obj.tileset_info.object_id,
wp,
dim,
obj.z_index,
r.resource,
r.quantity,
r.collecting_methode,
r.collecting_duration);
break;
}
case ObjectCategory::BUILDING: {
int bt_i;
file.read(reinterpret_cast<char*>(&bt_i), sizeof(bt_i));
BuildingType bt = static_cast<BuildingType>(bt_i);
int status_i;
file.read(reinterpret_cast<char*>(&status_i), sizeof(status_i));
BuildingStatus status = static_cast<BuildingStatus>(status_i);
float time;
file.read(reinterpret_cast<char*>(&time), sizeof(time));
size_t cost_count;
file.read(reinterpret_cast<char*>(&cost_count), sizeof(cost_count));
std::unordered_map<ResourceType, int> cost_f;
for (size_t i = 0; i < cost_count; ++i) {
int r, v;
file.read(reinterpret_cast<char*>(&r), sizeof(r));
file.read(reinterpret_cast<char*>(&v), sizeof(v));
cost_f[static_cast<ResourceType>(r)] = v;
}
size_t frac_count;
file.read(reinterpret_cast<char*>(&frac_count), sizeof(frac_count));
std::unordered_map<ResourceType, float> frac_b;
for (size_t i = 0; i < frac_count; ++i) {
int r;
float v;
file.read(reinterpret_cast<char*>(&r), sizeof(r));
file.read(reinterpret_cast<char*>(&v), sizeof(v));
frac_b[static_cast<ResourceType>(r)] = v;
}
on = new Building(
static_cast<ObjectType>(object_type),
ObjectCategory::BUILDING,
tileset_type,
obj.tileset_info.set_id,
obj.tileset_info.object_id,
wp,
dim,
obj.z_index,
bt);
Building* b = static_cast<Building*>(on);
b->building_status = status;
b->building_time = time;
b->cost_fulfilled = std::move(cost_f);
b->fractional_buffer = std::move(frac_b);
break;
}
case ObjectCategory::FARM_CROP: {
int type_i;
bool is_seed;
file.read(reinterpret_cast<char*>(&type_i), sizeof(type_i));
file.read(reinterpret_cast<char*>(&is_seed), sizeof(is_seed));
Vegetable* v = new Vegetable(static_cast<VegetableData::Type>(type_i), wp, is_seed);
file.read(reinterpret_cast<char*>(&v->quantity), sizeof(v->quantity));
file.read(reinterpret_cast<char*>(&v->growth_stage),
sizeof(v->growth_stage));
file.read(reinterpret_cast<char*>(&v->next_update_sec),
sizeof(v->next_update_sec));
file.read(reinterpret_cast<char*>(&v->collecting_duration_max),
sizeof(v->collecting_duration_max));
file.read(reinterpret_cast<char*>(&v->tileset_object_x),
sizeof(v->tileset_object_x));
file.read(reinterpret_cast<char*>(&v->tileset_object_y),
sizeof(v->tileset_object_y));
int slot;
file.read(reinterpret_cast<char*>(&slot), sizeof(slot));
v->equipement_slot = static_cast<EquipmentSlot>(slot);
on = v;
break;
}
default:
break;
}
if (!o && on) {
Object::objects.push_back(on);
o = on;
}
/*
std::cout << "OP: " << obj.position.x << " " << obj.position.y << " ID " << o->id << std::endl;
std::cout << "WP " << wp.x << " " << wp.y << std::endl;
std::cout << "DM: " << obj.position.w << " " << obj.position.h << " ID " << obj.tileset_info.object_id << std::endl;
*/
map_object = {{obj.position.x, obj.position.y}, obj.draw_from_this_tile, o ? o->id : 0};
}
}
return tile_map;
}
inline bool
isInView(float x, float y, float w, float h, const SDL_Rect &win) {
return !(x + w < 0 ||
y + h < 0 ||
x > win.w ||
y > win.h);
}
inline int
waveFrame( float t, int x_i, int y_i, int frame_count, float speed = 1.8f) {
float wave =
sinf(t * speed + x_i * 0.4f + y_i * 0.6f) +
sinf(t * speed * 0.66f - x_i * 0.7f + y_i * 0.3f) * 0.5f;
wave *= (1.0f / 1.5f);
float scaled = (wave + 1.0f) * (frame_count * 0.5f);
int frame = (int)scaled;
if (frame < 0) frame = 0;
if (frame >= frame_count) frame = frame_count - 1;
return frame;
}
void
draw_MapTiles(Game* game) {
// std::cout << "FL " << Map::offset.x << " " << Map::offset.y << " " << Default::game_scale << std::endl;
SDL_Renderer* renderer = game->renderer;
Character& player = *game->state_playing->player;
SDL_Rect win_size = game->win_size;
float view_x0 = -Map::offset.x;
float view_y0 = -Map::offset.y;
float view_x1 = view_x0 + win_size.w / Scale(1.f);
float view_y1 = view_y0 + win_size.h / Scale(1.f);
int size = Tile::get_Size();
global_frame_time += Default::delta_time;
/*
if (global_frame_time >= global_frame_time_max) {
frame_water = frame_water > 0 ? frame_water-1 : 3;
global_frame_time = 0.0f;
if (frame_water == 0)
global_frame_time_max = 0.3f + ((float)rand() / (float)RAND_MAX) * 1.2f;
//std::cout << "global_frame_time_max: " << global_frame_time_max << std::endl;
}
*/
frame_meadow_time += Default::delta_time;
if (frame_meadow_time >= frame_meadow_time_max) {
frame_meadow++;
frame_meadow %= 3;
frame_meadow_time = 0.0f;
if (frame_meadow == 0)
frame_meadow_time_max = 1.4f + ((float)rand() / (float)RAND_MAX) * 2.3f;
//std::cout << "frame_meadow_time_max: " << frame_meadow_time_max << std::endl;
}
for (size_t i = 0; i < Map::tile_map.size(); ++i) {
// START draw background tiles
Tile_s& tile = Map::tile_map[i].background;
int x_i = i % static_cast<size_t>(Map::size.x);
int y_i = i / static_cast<size_t>(Map::size.x);
float world_x = x_i * size;
float world_y = y_i * size;
float world_x2 = world_x + size;
float world_y2 = world_y + size;
// CULL TILE IF OUTSIDE CAMERA
if (world_x2 < view_x0 || world_x > view_x1 ||
world_y2 < view_y0 || world_y > view_y1)
continue;
float xf = world_x + Map::offset.x;
float yf = world_y + Map::offset.y;
SDL_FRect dst {
Scale(xf),
Scale(yf),
Scale(size),
Scale(size)
};
SDL_Point set_position = tile.set_position;
if (tile.set_category == TileCategory::TILE_WATER && set_position.y < 5) {
float local_speed = 1.8f * (0.8f + 0.4f * sinf(x_i*0.3f + y_i*0.5f));
int frame = waveFrame(global_frame_time, x_i, y_i, 4, local_speed);
set_position.x += frame * 6;
}
else if (tile.set_category == TileCategory::TILE_GROUND && set_position.y == 10) {
float local_speed = 1.5f * (0.85f + 0.3f * sinf(x_i*0.25f + y_i*0.35f));
int frame = waveFrame(global_frame_time, x_i, y_i, 3, local_speed);
set_position.x += frame;
}
SDL_FRect src {
static_cast<float>(set_position.x) * Tile::tile_size,
static_cast<float>(set_position.y) * Tile::tile_size,
(float)Tile::tile_size,
(float)Tile::tile_size
};
SDL_RenderTexture(renderer,
Tile::tilesets[TILE_BACKGROUND][tile.set_id].texture,
&src,
&dst);
for (Tile_s& overlay : Map::tile_map[i].overlays) {
if (overlay.set_category == TileCategory::TILE_OVERLAY_FARM) {
Farm::soil_Render(renderer, {xf, yf}, overlay.set_position);
}
}
// END draw background tiles
}
draw_ObjectTiles(renderer, player, win_size);
// if (Default::game_state == GAME_PLAY) return;
if (Default::modify_state == MODIFY_COLLISSION) {
size_t selection_size = Tile::get_SelectionSize(Default::modify_state);
const float cell_size = static_cast<float>(selection_size);
size_t map_width = static_cast<size_t>(Map::size.x)*(Tile::tile_size/Tile::tile_collider_size);
size_t map_height = static_cast<size_t>(Map::size.y)*(Tile::tile_size/Tile::tile_collider_size);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 100);
for (size_t y = 0; y < map_height; ++y) {
for (size_t x = 0; x < map_width; ++x) {
size_t index = y * map_width + x;
if (!Map::tile_collission_map[index])
continue;
SDL_FRect cell_rect = {
Scale(x * cell_size + Map::offset.x),
Scale(y * cell_size + Map::offset.y),
Scale(cell_size),
Scale(cell_size)
};
// draw cell outline
SDL_RenderRect(renderer, &cell_rect);
// draw cross
SDL_RenderLine(renderer,
cell_rect.x, cell_rect.y,
cell_rect.x + cell_rect.w, cell_rect.y + cell_rect.h);
SDL_RenderLine(renderer,
cell_rect.x + cell_rect.w, cell_rect.y,
cell_rect.x, cell_rect.y + cell_rect.h);
}
}
}
}
void draw_SelectionBorder(SDL_Renderer* renderer, Input& input) {
// START render selecting border
bool cursor_over_tile = Tile::hovering_set;
if (cursor_over_tile) return;
int selection_size = Tile::get_SelectionSize(Default::modify_state);
SDL_FRect tile_rect {
Scale(input.mouse_tile_position.x * selection_size + Map::offset.x),
Scale(input.mouse_tile_position.y * selection_size + Map::offset.y),
Scale(selection_size) * Tile::selection_tile_dimension.x,
Scale(selection_size) * Tile::selection_tile_dimension.y
};
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderRect(renderer, &tile_rect);
// END render selecting border
}
void
draw_ObjectTiles(SDL_Renderer* renderer, Character& player, SDL_Rect win_size) {
int size = Tile::get_Size();
std::vector<bool> enemies_drawn(Enemy::enemies.size(), false);
// draw objects and character
bool character_drawn = false;
bool player_behind = false;
float character_collider_x = player.collider_world.x; // - player.collider.w*0.5;
float character_collider_y = player.collider_world.y; // - player.collider.h*0.5;
for (Bird* bird : Bird::birds)
bird->drawn = false;
for (size_t i = 0; i < Map::tile_map.size(); ++i) {
TileMap& tilemap = Map::tile_map[i];
int x_i = i % static_cast<size_t>(Map::size.x);
int y_i = i / static_cast<size_t>(Map::size.x);
float xf = (x_i * size);
float yf = (y_i * size);
SDL_FPoint xy_point = {xf+size, yf+size};
// Render Character if no object is hiding it
player_behind = false;
for (TileObject& o : tilemap.objects) {
Object* object = Object::get_ObjectById(o.object_id);
// o = relative position,
// so can be 0 0, 0 1, 1 0, 1 1 if object size is 2 2
#ifdef DEBUG_BUILD
// START DEBUG RECT
SDL_FRect dbg = {
Scale(xf + Map::offset.x),
Scale(yf + Map::offset.y),
Scale(size),
Scale(size)};
SDL_FRect dbg_rect = dbg;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderRect(renderer, &dbg_rect);
// START DEBUG RECT
#endif
//if (o.position.x != 0 || o.position.y != o.position.h-1) continue;
// real object 0,0 start in map pixel
float m_xf = xf - o.position.x*size;
float m_yf = yf - o.position.y*size;
float width = static_cast<int>(object->dimension.x) * size;
float height = static_cast<int>(object->dimension.y) * size;
SDL_FPoint m_point = {m_xf+width, m_yf+height};
SDL_FRect obj_bb {
Scale(m_xf + Map::offset.x),
Scale(m_yf + Map::offset.y),
Scale(width),
Scale(height)
};
if (!isInView(obj_bb.x, obj_bb.y, obj_bb.w, obj_bb.h, win_size))
continue;
//if (!o.draw_from_this_tile) continue;
size_t j = 0;
//std::cout << "PS "<< o.position.x<< " " <<o.position.y <<std::endl;
for (Enemy* enemy : Enemy::enemies) {
SDL_FPoint enemy_bb {
static_cast<float>(enemy->collider_world.x),
static_cast<float>(enemy->collider_world.y),
};
if (!isInView(enemy_bb.x, enemy_bb.y, enemy->collider.w, enemy->collider.h, win_size)) {
j++;
continue;
}
if ( !enemies_drawn[j] &&
(enemy->stats.health <= 0 ||
(enemy_bb.x >= m_xf &&
enemy_bb.y >= m_yf &&
enemy_bb.x + enemy->collider.w <= m_point.x &&
enemy_bb.y <= m_point.y &&
object->z_index > 0 ))) {
if ( enemy->stats.health <= 0 ||
object->is_InFrontOf({m_xf, m_yf},
{enemy_bb.x,
enemy_bb.y,
enemy->collider.w,
enemy->collider.h})) {
enemy->Draw(renderer);
enemies_drawn[j] = true;
}
}
j++;
}
player_behind = false;
int am_i_behind_an_object = object->is_InFrontOf({m_xf, m_yf},
{character_collider_x,
character_collider_y,
player.collider.w,
player.collider.h});
if (am_i_behind_an_object) {
if ( am_i_behind_an_object == 1 &&
character_collider_y-player.height/3 > m_yf &&
( player.collider_world.x > m_xf ||
player.collider_world.x < m_xf+object->dimension.x ) )
player_behind = true;
if (!character_drawn) {
//std::cout << "DRAW BEHIND\n";
player.Draw(renderer);
character_drawn = true;
}
}
for (Bird* bird : Bird::birds) {
if (bird->drawn)
continue;
if ( static_cast<int>(bird->pos_x/Tile::tile_size) != x_i ||
static_cast<int>((bird->pos_y-bird->altitude)/Tile::tile_size) != y_i)
continue;
bird->Render(renderer);
// std::cout << "I AM GETTING CALLED BIRD AMP " << bird->pos_y << " " << bird->altitude << std::endl;
bird->drawn = true;
}
//std::cout << player_behind << std::endl;
//std::cout << "CHA: " << character_collider_x << " " << character_collider_y << " " << player.collider.w << " " << player.collider.h << std::endl;
//std::cout << "OJJ: " << m_xf << " " << m_yf << " " << m_point.x << " " << m_point.y << std::endl;
// START OBJECT DRAW
if (o.draw_from_this_tile)
object->Draw(renderer, {m_xf, m_yf}, player_behind);
// END OBJECT DRAW
}
size_t j = 0;
if ( !character_drawn &&
player.collider_world.x >= xf &&
player.collider_world.x <= xy_point.x &&
player.collider_world.y >= yf &&
player.collider_world.y <= xy_point.y) {
player.Draw(renderer);
character_drawn = true;
}
for (Enemy* enemy : Enemy::enemies) {
if ( !enemies_drawn[j] &&
enemy->stats.health > 0 &&
enemy->collider_world.x >= xf &&
enemy->collider_world.x <= xy_point.x &&
enemy->collider_world.y >= yf &&
enemy->collider_world.y <= xy_point.y) {
enemy->Draw(renderer);
enemies_drawn[j] = true;
}
j++;
}
}
for (Bird* bird : Bird::birds)
if (!bird->drawn) {
//std::cout << "BIRD AMP " << bird->pos_y << " " << bird->altitude << std::endl;
bird->Render(renderer);
}
}
void
set_Offset(Input& input) {
// Set map map offset set
if (!input.click_left && Map::offset_to_set.x != 0 && Map::offset_to_set.y != 0) {
Map::offset_to_set.x = 0;
Map::offset_to_set.y = 0;
Map::setting_offset = false;
}
// Set map offset
else if (Map::offset_to_set.x != 0 && Map::offset_to_set.y != 0) {
Map::offset.x = input.mouse_state.x - Map::offset_to_set.x;
Map::offset.y = input.mouse_state.y - Map::offset_to_set.y;
}
// Update map offset set
if (input.click_left && input.pressed_ctrl &&
(Map::offset_to_set.x == 0 && Map::offset_to_set.y == 0)) {
Map::offset_to_set.x = input.mouse_state.x - Map::offset.x;
Map::offset_to_set.y = input.mouse_state.y - Map::offset.y;
Map::setting_offset = true;
}
}
void
set_Collission(Input& input) {
size_t cells_per_tile = Tile::tile_size/Tile::tile_collider_size;
const size_t map_width = static_cast<size_t>(Map::size.x)*cells_per_tile;
for (size_t y = 0; y < static_cast<size_t>(Tile::selection_tile_dimension.y); ++y) {
for (size_t x = 0; x < static_cast<size_t>(Tile::selection_tile_dimension.x); ++x) {
size_t cell_x = static_cast<size_t>(input.mouse_tile_position.x) + x;
size_t cell_y = static_cast<size_t>(input.mouse_tile_position.y) + y;
size_t id = cell_y * map_width + cell_x;
if (id >= Map::tile_collission_map.size())
continue;
Map::tile_collission_map[id] = !Map::tile_collission_map[id];
}
}
}
std::vector<TileObject>&
get_ObjectsFromMapTile(SDL_Point tile_point) {
int index = static_cast<int>(tile_point.y * Map::size.x + tile_point.x);
return Map::tile_map[static_cast<size_t>(index)].objects;
}
void
place_ConstructionSide() {
TilesetObject object = BuildingData::active_build.object;
// object.building_status = TileBuildingStatus::CONSTRUCT;
TileType type = Tile::get_Type(object.tileset_info.set_category);
std::vector<bool> collision_map =
Tile::get_ObjectCollisionsFromTileset(object.tileset_info.set_id, object.tileset_info.object_id, type);
// std::cout << "I " << object.tileset_info.set_id << " " << object.tileset_info.object_id << " " << (int)type << " " << (int)BuildingData::active_build.type <<std::endl;
// set draw tile
SDL_Point draw_tile = {0,0};
int s = Tile::tile_size / Tile::tile_collider_size;
int width_c = object.position.w * s;
for (int y = 0; y < object.position.w; y++) {
for (int x = 0; x < object.position.w; x++) {
for (int yc = y * s; yc < y + 1 * s; yc++) {
for (int xc = x * s; xc < x + 1 * s; xc++) {
size_t i = static_cast<size_t>(yc * width_c + xc);
if (i >= collision_map.size()) break;
if (collision_map[i] && y > draw_tile.y) {
if (x <= draw_tile.x) draw_tile = {x, y};
}
}
}
}
}
// place in map array
Building* building = new Building(object.object_type,
ObjectCategory::BUILDING,
type,
object.tileset_info.set_id,
object.tileset_info.object_id,
{static_cast<int>(BuildingData::active_build.tile_position.x), static_cast<int>(BuildingData::active_build.tile_position.y)},
{object.position.w, object.position.h},
object.z_index,
BuildingData::active_build.type);
Object::objects.push_back(building);
for (int y = 0; y < object.position.h; y++) {
for (int x = 0; x < object.position.w; x++) {
SDL_Point world_position = {BuildingData::active_build.tile_position.x+x, BuildingData::active_build.tile_position.y+y};
object.position.x = x;
object.position.y = y;
object.draw_from_this_tile = false;
size_t index = static_cast<size_t>(world_position.y*Map::size.x + world_position.x);
if (x == draw_tile.x && y == draw_tile.y)
object.draw_from_this_tile = true;
Map::tile_map[index].objects.push_back({{x, y}, object.draw_from_this_tile, building->id});
// Sort map objects
std::sort(Map::tile_map[index].objects.begin(),
Map::tile_map[index].objects.end(),
[](const TileObject& a, const TileObject& b) {
Object* oa = Object::get_ObjectById(a.object_id);
Object* ob = Object::get_ObjectById(b.object_id);
return oa->z_index < ob->z_index;
});
set_TileCollissionForObjects({world_position.x, world_position.y});
}
}
std::cout << "PLACING!\n";
}
bool
is_FreePosition(SDL_FRect candidate_rect) {
int left_tile = static_cast<int>(candidate_rect.x / Tile::tile_collider_size);
int top_tile = static_cast<int>(candidate_rect.y / Tile::tile_collider_size);
int right_tile = static_cast<int>((candidate_rect.x + candidate_rect.w) / Tile::tile_collider_size);
int bottom_tile = static_cast<int>((candidate_rect.y + candidate_rect.h) / Tile::tile_collider_size);
for (int ty = top_tile; ty <= bottom_tile; ++ty) {
for (int tx = left_tile; tx <= right_tile; ++tx) {
if (tx < 0 || ty < 0 || tx >= (Map::size.x*Tile::tile_collider_size) || ty >= (Map::size.y*Tile::tile_collider_size)) {
return false;
}
size_t idx = static_cast<size_t>(ty * Map::size.x*Tile::tile_collider_size + tx);
if (Map::tile_collission_map[idx]) {
return false;
}
}
}
// CHECK ENEMY COLLISION
if (Enemy::is_CollidingWithEnemy(candidate_rect))
return false;
return true;
}
SDL_FPoint
get_ClosestPointWithoutCollision(const SDL_FRect &rect) {
int cx_tile = static_cast<int>(rect.x / Tile::tile_size);
int cy_tile = static_cast<int>(rect.y / Tile::tile_size);
if (is_FreePosition({rect.x - rect.w*0.5f, rect.y - rect.h*0.5f, rect.w, rect.h}))
return { rect.x, rect.y };
struct TilePos { int x, y; };
std::queue<TilePos> q;
std::set<std::pair<int,int>> visited;
q.push({ cx_tile, cy_tile });
visited.insert({ cx_tile, cy_tile });
const int dirs[4][2] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
while (!q.empty()) {
TilePos t = q.front();
q.pop();
float px = static_cast<size_t>(t.x) * Tile::tile_size;
float py = static_cast<size_t>(t.y) * Tile::tile_size;
SDL_FRect test_rect = { px - rect.w*0.5f, py - rect.h*0.5f, rect.w, rect.h };
if (is_FreePosition(test_rect))
return { px, py };
for (int i = 0; i < 4; ++i)
{
int nx = t.x + dirs[i][0];
int ny = t.y + dirs[i][1];
if (nx < 0 || ny < 0 || nx >= static_cast<int>(Map::size.x) || ny >= static_cast<int>(Map::size.y) )
continue;
if (visited.insert({ nx, ny }).second)
q.push({ nx, ny });
}
}
std::cout << "No point found!\n";
return { 0,0 };
}
};