#include #include "base_data.h" #include "world_editor.h" #include "editor_event.h" #include "building_layout_graph.h" #define MIN_ROOM_SIZE 16 /* 4 * 4 tiles */ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs) { ecs.module(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); ecs.component(); const String &module_name = "::BuildingLayoutGraph::graph_module"; ecs.system( "FloorIndex") .kind(0) .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_floor_index &index, const WorldEditor::components::buildings_layout_floor &f) { flecs::entity e = it.entity(count); List queue; e.children([&queue](flecs::entity ec) { if (!ec.has()) queue.push_back(ec); }); while (!queue.empty()) { flecs::entity qe = queue.front()->get(); queue.pop_front(); if (!qe.has()) { qe.set( { index.index }); print_line(String(qe.path()) + " index set"); qe.children([&queue](flecs::entity ec) { queue.push_back(ec); }); } } print_line("floor index done"); }); ecs.system("RoomArea") .kind(0) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_room &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { MIN_ROOM_SIZE }); }); ecs.system("ZoneArea") .kind(0) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_zone &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system("UnitArea") .kind(0) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_unit &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system("FloorArea") .kind(0) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_floor &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system( "ZoneAreaSum") .kind(0) .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_zone &f, WorldEditor::components::buildings_layout_area &area, WorldEditor::components::buildings_layout_floor_index &index) { flecs::entity zone_e = it.entity(count); flecs::query< WorldEditor::components::buildings_layout_room, const WorldEditor::components:: buildings_layout_area, const WorldEditor::components:: buildings_layout_floor_index> q = zone_e.world() .query_builder< WorldEditor::components:: buildings_layout_room, const WorldEditor::components:: buildings_layout_area, const WorldEditor::components:: buildings_layout_floor_index>() .with(flecs::ChildOf, zone_e) .build(); float sum = 0.0f; q.each([&sum, index](flecs::entity e, WorldEditor::components:: buildings_layout_room &r, const WorldEditor::components:: buildings_layout_area &rarea, const WorldEditor::components:: buildings_layout_floor_index &rindex) { if (index.index == rindex.index) sum += MAX(rarea.area, MIN_ROOM_SIZE); }); zone_e.set< WorldEditor::components::buildings_layout_area>( { sum }); print_line("area: " + String(zone_e.path()) + ": " + String::num(sum)); }); ecs.system( "UnitAreaSum") .kind(0) .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_unit &f, WorldEditor::components::buildings_layout_area &area, WorldEditor::components::buildings_layout_floor_index &index) { flecs::entity unit_e = it.entity(count); flecs::query< WorldEditor::components::buildings_layout_zone, const WorldEditor::components:: buildings_layout_area, const WorldEditor::components:: buildings_layout_floor_index> q = unit_e.world() .query_builder< WorldEditor::components:: buildings_layout_zone, const WorldEditor::components:: buildings_layout_area, const WorldEditor::components:: buildings_layout_floor_index>() .with(flecs::ChildOf, unit_e) .build(); float sum = 0.0f; q.each([&sum, index](flecs::entity e, WorldEditor::components:: buildings_layout_zone &r, const WorldEditor::components:: buildings_layout_area &rarea, const WorldEditor::components:: buildings_layout_floor_index &rindex) { if (index.index == rindex.index) sum += rarea.area; }); unit_e.set< WorldEditor::components::buildings_layout_area>( { sum }); print_line("area: " + String(unit_e.path()) + ": " + String::num(sum)); }); ecs.system( "FloorAreaSum") .kind(0) .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &f, WorldEditor::components::buildings_layout_area &area, WorldEditor::components::buildings_layout_floor_index &index) { flecs::entity floor_e = it.entity(count); flecs::query q = floor_e.world() .query_builder< const WorldEditor::components:: buildings_layout_area, const WorldEditor::components:: buildings_layout_floor_index>() .scope_open() .with() .or_() .with() .scope_close() .with(flecs::ChildOf, floor_e) .build(); float sum = 0.0f; q.each([&sum, index](flecs::entity e, const WorldEditor::components:: buildings_layout_area &rarea, const WorldEditor::components:: buildings_layout_floor_index &rindex) { if (index.index == rindex.index) sum += rarea.area; }); floor_e.set< WorldEditor::components::buildings_layout_area>( { sum }); print_line("area: " + String(floor_e.path()) + ": " + String::num(sum)); }); ecs.system( "CreateFloorData") .kind(0) .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_graph &p, const WorldEditor::components:: buildings_layout_floor_index &idx, const WorldEditor::components::buildings_layout_area &area) { flecs::entity e = it.entity(count); flecs::query q = e.world() .query_builder< WorldEditor::components:: buildings_layout_area>() .with() .with(flecs::ChildOf, e) .build(); float max_area = area.area; q.each([&max_area](flecs::entity ec, WorldEditor::components:: buildings_layout_area &r) { if (max_area < r.area) max_area = r.area; }); float grid_size = Math::ceil( Math::ceil(Math::sqrt(max_area)) / 4.0f); e.set( { (int)Math::ceil(grid_size) }); print_line("grid size: " + itos((int)Math::ceil(grid_size))); }); ecs.system( "FloorCompleteArea") .kind(0) .each([module_name]( flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_floor &f) { flecs::entity floor_e = it.entity(count); flecs::world w = floor_e.world(); std::vector systems = { "RoomArea", "ZoneArea", "UnitArea", "FloorArea", "ZoneAreaSum", "UnitAreaSum", "FloorAreaSum", "CreateFloorData" }; int i; for (i = 0; i < (int)systems.size(); i++) { w.system(w.lookup((module_name + "::" + systems[i]) .ascii() .ptr())) .run(); } print_line("floor complete"); }); #if 0 ecs.system("FloorArea") .kind(flecs::OnUpdate) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_floor &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system("UnitArea") .kind(flecs::OnUpdate) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_unit &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system("ZoneArea") .kind(flecs::OnUpdate) .without() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_zone &f) { flecs::entity floor_e = it.entity(count); floor_e.set< WorldEditor::components::buildings_layout_area>( { 0.0f }); }); ecs.system( "MakeZoneAreas") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_area &area, const WorldEditor::components::buildings_layout_zone &f, const WorldEditor::components:: buildings_layout_floor_index &idx) { flecs::entity zone_e = it.entity(count); int index = idx.index; float parea = 0.0f; zone_e.children([index, &parea](flecs::entity ec) { if (ec.has() && ec.has() && ec.has()) { if (ec.get() ->index == index) parea += ec.get() ->area; } }); area.area = parea; print_line(String(zone_e.path()) + " area: " + String::num(area.area)); }); ecs.system( "MakeUnitAreas") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_area &area, const WorldEditor::components::buildings_layout_unit &f, const WorldEditor::components:: buildings_layout_floor_index &idx) { flecs::entity zone_e = it.entity(count); int index = idx.index; float parea = 0.0f; zone_e.children([index, &parea](flecs::entity ec) { if (ec.has() && ec.has() && ec.has()) { if (ec.get() ->index == index) parea += ec.get() ->area; } }); area.area = parea; print_line(String(zone_e.path()) + " area: " + String::num(area.area)); }); ecs.system( "MakeFloorAreas") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_area &area, const WorldEditor::components::buildings_layout_floor &f, const WorldEditor::components:: buildings_layout_floor_index &idx) { flecs::entity floor_e = it.entity(count); int index = idx.index; float parea = 0.0f; floor_e.children([index, &parea](flecs::entity ec) { if (ec.has() && ec.has() && ec.has()) { if (ec.get() ->index == index) parea += ec.get() ->area; } if (ec.has() && ec.has() && ec.has()) { if (ec.get() ->index == index) parea += ec.get() ->area; } }); area.area = parea; print_line(String(floor_e.path()) + " area: " + String::num(area.area)); }); #if 0 ecs.system( "MakeAreas") .kind(flecs::OnUpdate) // .read() .with() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_area &area, const WorldEditor::components::buildings_layout_floor &f, const WorldEditor::components:: buildings_layout_floor_index &idx) { flecs::entity floor_e = it.entity(count); flecs::world ecs_ = floor_e.world(); ecs_.system(ecs_.lookup("MakeZoneAreas")).run(); ecs_.system(ecs_.lookup("MakeUnitAreas")).run(); ecs_.system(ecs_.lookup("MakeFloorAreas")).run(); }); #endif ecs.system( "MakeGridEntities") .kind(flecs::OnUpdate) // .read() .with() .each([](flecs::iter &it, size_t count, WorldEditor::components::buildings_layout_area &area, const WorldEditor::components::buildings_layout_floor &f, const WorldEditor::components:: buildings_layout_floor_index &idx) { #if 0 flecs::entity floor_e = it.entity(count); List queue; assert(false); floor_e.children([&queue](flecs::entity ec) { if (!ec.has()) queue.push_back(ec); }); while (!queue.empty()) { flecs::entity e = queue.front()->get(); queue.pop_front(); } #endif }); #if 0 ecs.system( "CreateZoneData") .kind(flecs::OnUpdate) .without() .write() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_zone &p) { flecs::entity e = it.entity(count); e.set( { 0.0f, false }); e.add(); String path(it.entity(count).path()); print_line("Create zone data: entity: " + path); }); ecs.system( "CreateUnitData") .kind(flecs::OnUpdate) .without() .write() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_unit &p) { flecs::entity e = it.entity(count); e.set( { 0.0f }); e.add(); String path(it.entity(count).path()); print_line("Create unit data: entity: " + path); }); #if 0 ecs.system("CreateFloorIndex") .kind(flecs::OnUpdate) .without() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &p) { flecs::entity e = it.entity(count); e.set( { 0.0f }); }); #endif ecs.system( "UpdateUnitData") .kind(flecs::OnUpdate) .with() .with() .write() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_unit &p) { flecs::entity e = it.entity(count); float area = 0.0f; e.children([&area](flecs::entity ec) { if (ec.has()) area += ec.get() ->area; }); WorldEditor::components::buildings_layout_unit_data *udata = e.get_mut< WorldEditor::components:: buildings_layout_unit_data>(); udata->area = area; String path(it.entity(count).path()); print_line("Update unit data: entity: " + path + " area: " + String::num(area)); e.remove(); BuildingLayoutGraph::get_singleton() ->get_layout_base() .add(); }); ecs.system( "UpdateZoneData") .kind(flecs::OnUpdate) .with() .with() .write() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_zone &p) { flecs::entity e = it.entity(count); float area = 0.0f; bool window = false; e.children([&area, &window](flecs::entity ec) { if (ec.has()) area += ec.get() ->area; if (ec.has()) { bool w = ec.get() ->window; if (w) window = true; } }); WorldEditor::components::buildings_layout_zone_data *zdata = e.get_mut< WorldEditor::components:: buildings_layout_zone_data>(); zdata->area = area; zdata->align_wall = window; String path(it.entity(count).path()); print_line("Update zone data: entity: " + path + " area: " + String::num(area)); e.remove(); flecs::entity parent_e = e.parent(); if (parent_e.has()) parent_e.add(); BuildingLayoutGraph::get_singleton() ->get_layout_base() .add(); }); ecs.system( "PropogateFloorIndex") .kind(flecs::OnUpdate) .with() .write() .write() .write() .immediate() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &r) { flecs::entity e = it.entity(count); print_line("propogate: " + String(e.path()) + " " + itos(r.index)); List queue; float area = 0.0f; /* e is a floor so skip it and add children instead*/ e.children([&area](flecs::entity ec) { if (ec.has()) area += ec.get() ->area; if (ec.has()) area += ec.get() ->area; }); float size = Math::ceil(Math::sqrt(area) * 2.0f); int grid_size = (int)Math::ceil(size / 2.0f); e.set( { area, grid_size }); e.children([&queue](flecs::entity ec) { /* do not add children floors to the queue */ if (!ec.has()) queue.push_back(ec); }); while (!queue.empty()) { flecs::entity qe = queue.front()->get(); queue.pop_front(); if (!qe.has()) { /* do not add children under another floor either */ qe.set( { r.index }); qe.children([&queue](flecs::entity ec) { queue.push_back(ec); }); } } BuildingLayoutGraph::get_singleton() ->get_layout_base() .add(); e.remove(); }); #if 0 ecs.system( "CollectFloors") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_graph &p) { flecs::entity e = it.entity(count); List queue; queue.push_back(e); while (!queue.empty()) { flecs::entity em = queue.front()->get(); if (em.has(); }); ecs.system( "DestroyGrid") .kind(flecs::OnUpdate) .with() .write() .immediate() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_base &p) { flecs::entity e = it.entity(count); flecs::entity grid_base_e = BuildingLayoutGraph::get_singleton() ->get_layout_grid_base(); grid_base_e.children([](flecs::entity ce) { print_line("delete: " + String(ce.path())); ce.destruct(); }); }); ecs.system("CreateGrid") .kind(flecs::OnUpdate) .with() .write() .immediate() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_base &p) { flecs::entity e = it.entity(count); flecs::entity grid_base_e = BuildingLayoutGraph::get_singleton() ->get_layout_grid_base(); e.children([&grid_base_e](flecs::entity ec) { if (ec.has()) { print_line(String(grid_base_e.path())); flecs::entity gc = ec.world() .entity(ec.name()) .child_of(grid_base_e); gc.add(); gc.add(); print_line("create: " + String(gc.path())); } }); }); #endif #if 0 ecs.system( "PopulateGrid") .kind(flecs::OnUpdate) .with() .write() .immediate() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_grid &p) { flecs::entity e = it.entity(count); flecs::entity grid_layout_e = BuildingLayoutGraph::get_singleton() ->get_layout_base(); flecs::entity layout_e }); #endif ecs.system( "UpdateGraph") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_base &p) { flecs::entity e = it.entity(count); EditorEvent::get_singleton()->event.emit( "update_layout_view", varray()); e.remove(); }); #endif #if 0 ecs.observer() .event(flecs::OnSet) .each([](flecs::entity e, WorldEditor::components::buildings_layout_room &r) { e.add(); print_line("room set " + String(e.path())); }); ecs.observer() .event(flecs::OnSet) .each([](flecs::entity e, WorldEditor::components::buildings_layout_zone &r) { e.add(); print_line("zone set " + String(e.path())); }); ecs.observer() .event(flecs::OnSet) .each([](flecs::entity e, WorldEditor::components::buildings_layout_unit &r) { e.add(); print_line("unit set " + String(e.path())); }); #endif ecs.system( "CreateFloorData") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &p, const WorldEditor::components:: buildings_layout_floor_index &idx, const WorldEditor::components::buildings_layout_area &area) { flecs::entity e = it.entity(count); float grid_size = Math::ceil( Math::ceil(Math::sqrt(area.area)) / 4.0f); e.set( { (int)Math::ceil(grid_size) }); print_line("grid size: " + itos((int)Math::ceil(grid_size))); assert(false); }); ecs.system( "CreateGridEntity") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &p, const WorldEditor::components:: buildings_layout_floor_index &idx, const WorldEditor::components::buildings_layout_area &area) { flecs::entity e = it.entity(count); flecs::entity grid_e = e.lookup("grid"); if (!grid_e.is_valid()) grid_e = e.world().entity("grid").child_of(e); grid_e.add< WorldEditor::components::buildings_layout_grid>(); /* create grid here */ }); ecs.system( "FinishGraphUpdate") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::iter &it, size_t count, const WorldEditor::components::buildings_layout_floor &p, const WorldEditor::components:: buildings_layout_floor_index &idx, const WorldEditor::components::buildings_layout_area &area) { flecs::entity e = it.entity(count); EditorEvent::get_singleton()->event.emit( "update_layout_view", varray()); e.remove(); }); #endif ecs.observer() .event(flecs::OnSet) .with() .write() .each([module_name]( flecs::entity e, const WorldEditor::components::buildings_layout_area &r) { flecs::world w = e.world(); // create indices w.system(w.lookup((module_name + "::FloorIndex") .ascii() .ptr())) .run(); w.system(w.lookup((module_name + "::FloorCompleteArea") .ascii() .ptr())) .run(); EditorEvent::get_singleton()->event.emit( "update_layout_view", varray()); #if 0 /* if set for room make zone dirty */ flecs::entity parent_e = e.parent(); while (parent_e.is_valid()) { print_line(String(parent_e.path())); if (parent_e.has< WorldEditor::components:: buildings_layout_floor>()) { parent_e.add< WorldEditor::components:: buildings_layout_dirty>(); break; } parent_e = parent_e.parent(); } #endif #if 0 if (parent_e.has()) parent_e.add< WorldEditor::components:: buildings_layout_dirty>(); parent_e.set( { 0.0f }); #endif #if 0 flecs::world ecs_ = e.world(); ecs_.system(ecs_.lookup("MakeZoneAreas")).run(); ecs_.system(ecs_.lookup("MakeUnitAreas")).run(); ecs_.system(ecs_.lookup("MakeFloorAreas")).run(); #endif print_line(String(e.path()) + ": set area"); }); /* Propagate floor index to children which are not floor */ ecs.observer() .event(flecs::OnSet) .with() .each([module_name](flecs::entity e, const WorldEditor::components:: buildings_layout_floor_index &r) { flecs::world w = e.world(); // create indices w.system(w.lookup((module_name + "::FloorIndex") .ascii() .ptr())) .run(); w.system(w.lookup((module_name + "::FloorCompleteArea") .ascii() .ptr())) .run(); EditorEvent::get_singleton()->event.emit( "update_layout_view", varray()); print_line(String(e.path()) + ": set floor"); #if 0 List queue; queue.push_back(e); while (!queue.empty()) { flecs::entity qe = queue.front()->get(); queue.pop_front(); if (!qe.has()) { qe.set( { r.index }); } } BuildingLayoutGraph::get_singleton() ->get_layout_base() .add(); #endif }); } /* These functions are used from GUI */ flecs::entity BuildingLayoutGraph::create_graph_entity(const String &base_path, const String &entity_type) { flecs::world ecs = BaseData::get_singleton()->get(); flecs::entity base_e = ecs.lookup(base_path.ascii().ptr()); int count = 0; const String &type_name = entity_type; base_e.children([type_name, &count](flecs::entity e) { String name(e.name()); if (name.begins_with(type_name + "_" + itos(count))) count++; }); String ename = type_name + "_" + itos(count); flecs::entity new_e = ecs.entity(ename.ascii().ptr()).child_of(base_e); return new_e; } void BuildingLayoutGraph::create_zone(const String &base_path, int zone_type) { flecs::entity new_e = create_graph_entity(base_path, "zone"); new_e.set( { zone_type }); new_e.set( { 0, 0, 0 }); int count = get_children_count(new_e.parent()); new_e.set({ count }); } void BuildingLayoutGraph::create_unit(const String &base_path) { flecs::entity new_e = create_graph_entity(base_path, "unit"); new_e.set({ 0 }); new_e.set( { 0, 0, 0 }); int count = get_children_count(new_e.parent()); new_e.set({ count }); } void BuildingLayoutGraph::create_floor(const String &base_path) { flecs::entity new_e = create_graph_entity(base_path, "floor"); new_e.set({ 0 }); new_e.set( { 0, 0, 0 }); int count = get_children_count(new_e.parent()); new_e.set({ count }); } void BuildingLayoutGraph::create_room(const String &base_path, int id) { Array room; BuildingLayoutGraph::get_singleton()->get_room_data(id, room); assert(!room.empty()); String type_name = room[1]; Dictionary room_options = room[2]; bool window = room_options.get("window", false); type_name = type_name.replace(" ", "_").to_lower(); flecs::entity new_e = create_graph_entity(base_path, type_name); new_e.set( { id, window }); new_e.set( { 0, 0, 0 }); new_e.set({ 0.0f }); assert(new_e.has()); int count = get_children_count(new_e.parent()); new_e.set({ count }); new_e.set({ -1 }); } void BuildingLayoutGraph::create_new_layout(const String &layout_name) { flecs::world ecs = BaseData::get_singleton()->get(); flecs::entity base = get_layout_base(); flecs::entity e = base.lookup(layout_name.ascii().ptr()); if (e.is_valid()) return; e = ecs.entity(layout_name.ascii().ptr()).child_of(base); if (e.is_valid()) { e.add(); e.set({ 0 }); e.set( { 0, 0, 0 }); e.set( { 0 }); e.set({ 0 }); int count = get_children_count(base); e.set( { count }); } }