diff --git a/main.cpp b/main.cpp index e169045..2270059 100644 --- a/main.cpp +++ b/main.cpp @@ -21,7 +21,7 @@ void ui_map(); int main(int argc, char *argv[]) { - UI ui(sf::Vector2u(1200, 1000), "JackEditor"); + UI ui(sf::Vector2u(1400, 1000), "JackEditor"); while (ui.Update()) { static bool demo_open = false; @@ -37,8 +37,16 @@ int main(int argc, char *argv[]) { if (demo_open) ig::ShowDemoWindow(&demo_open); - if (bundleOpen) + if (bundleOpen) { + ig::PushID(1); ui_bundle(); + ig::PopID(); + } + if (mapOpen) { + ig::PushID(2); + ui_map(); + ig::PopID(); + } ui.Render(); diff --git a/map.cpp b/map.cpp index 470630c..dd2c869 100644 --- a/map.cpp +++ b/map.cpp @@ -2,11 +2,145 @@ #include "gui/gui.hpp" #include "gui/imgui/imgui.h" #include "gui/imgui/imgui_stdlib.h" +#include "ig001.hpp" #include "map_types.hpp" -#include +#include +#include +#include +#include +#include -static std::set entities, to_delete; +static std::list entities; +static EntityBase *selected_entity = NULL; +static EntityBase *to_delete = NULL; +static Vec2 offset0(0, 0); +static double cutoff = 1500; + void ui_map() { + Vec2 newdelta(ig::GetMouseDragDelta(ImGuiMouseButton_Right)); + Vec2 offset = offset0 + newdelta; + if (!ig::IsMouseDragging(ImGuiMouseButton_Right)) + offset0 = offset0 + newdelta; + + if (ig::Begin("Entities")) { + if (ig::BeginMenu("Load Entities from Text")) { + static std::string buf_code; + ig::InputTextMultiline("##Code", &buf_code, ImVec2(400, 300)); + if (ig::Button("Load!")) { + for (EntityBase *e: entities) + delete e; + entities.clear(); + + std::istringstream is(buf_code); + std::string line; + while (std::getline(is, line)) { + if (line.size() == 0) + continue; + char *cmd = std::strtok(line.data(), " "); +#define CMD1(str) if (std::strcmp(cmd, str) == 0) +#define CMD(str) else if (std::strcmp(cmd, str) == 0) + if (!cmd) + continue; + EntityBase *e = NULL; + CMD1("HITBOX") + e = new Hitbox(); + CMD("PLAYER") + e = new Player(); + CMD("HAZARD_RESPAWN") + e = new HazardRespawn(); + CMD("HAZARD") + e = new Hazard(); + CMD("TEXTBOX") + e = new Textbox(); + CMD("LEVEL_TRANSITION") + e = new LevelTransition(); + CMD("CUTOFF") + cutoff = strtod(strtok(NULL, " "), NULL); + + if (e != NULL) { + e->from_strtok(); + entities.push_back(e); + } + } + + buf_code.clear(); + } + ig::EndMenu(); + } + + Vec2 center = Vec2(ig::GetIO().DisplaySize) * 0.5 - offset; + ig::SeparatorText("Add Entity"); + if (ig::Button("Hitbox")) + entities.push_back((new Hitbox())->setpos(center)); + ig::SameLine(); + if (ig::Button("Player")) + entities.push_back((new Player())->setpos(center)); + ig::SameLine(); + if (ig::Button("Hazard Respawn")) + entities.push_back((new HazardRespawn())->setpos(center)); + if (ig::Button("Hazard")) + entities.push_back((new Hazard())->setpos(center)); + ig::SameLine(); + if (ig::Button("Textbox")) + entities.push_back((new Textbox())->setpos(center)); + ig::SameLine(); + if (ig::Button("Level Transition")) + entities.push_back((new LevelTransition())->setpos(center)); + + DragDouble("Cutoff depth", &cutoff); + + ig::SeparatorText("Entities"); + if (ig::Selectable("<<< CLEAR >>>", selected_entity == NULL)) + selected_entity = NULL; + ig::Separator(); + + for (EntityBase *e: entities) { + ig::PushID(e); + if (ig::Selectable(e->type().c_str(), selected_entity == e)) + selected_entity = e; + if (ig::IsItemHovered()) + if (ig::IsMouseDoubleClicked(ImGuiMouseButton_Right)) + to_delete = e; + ig::PopID(); + } + } + ig::End(); + + if (ig::Begin("Properities") && selected_entity != NULL) { + ig::PushID(selected_entity); + selected_entity->imgui(); + ig::PopID(); + } + ig::End(); + + ImVec2 screen_size = ig::GetIO().DisplaySize; + ImDrawList *drawlist = ig::GetBackgroundDrawList(); + drawlist->AddLine(ImVec2(0, offset.y), ImVec2(screen_size.x, offset.y), IM_COL32_WHITE); + drawlist->AddLine(ImVec2(offset.x, 0), ImVec2(offset.x, screen_size.y), IM_COL32_WHITE); + drawlist->AddLine(ImVec2(0, cutoff + offset.y), ImVec2(screen_size.x, cutoff + offset.y), IM_COL32(255, 0, 0, 255)); + // Draw every entity + for (EntityBase *e: entities) + e->draw(offset, e == selected_entity); + + + static std::string buff; + buff.clear(); + buff = "CUTOFF " + std::to_string((int)std::round(cutoff)) + "\n"; + for (EntityBase *e: entities) { + buff += e->to_file(); + } + if (ig::Begin("Output")) { + if (ig::Button("Copy!")) + ig::SetClipboardText(buff.c_str()); + ig::InputTextMultiline("##Text", &buff, ImVec2(-1, -1), ImGuiInputTextFlags_ReadOnly); + } + ig::End(); + + + if (to_delete != NULL) { + entities.remove(to_delete); + to_delete = NULL; + } } diff --git a/map_types.hpp b/map_types.hpp index 69bfd55..e078c8e 100644 --- a/map_types.hpp +++ b/map_types.hpp @@ -3,18 +3,24 @@ #include "types.hpp" #include "ig001.hpp" #include "gui/imgui/imgui_stdlib.h" +#include +#include +#include #include class EntityBase { public: + virtual ~EntityBase() {} virtual std::string type() = 0; - virtual std::string to_file() = 0; + virtual std::string to_file() = 0; + virtual void from_strtok() = 0; // Pointer to the entity base is pushed outside - virtual void imgui() {} - virtual void draw(Vec2 offset, bool selected) {} + virtual void imgui() {} + virtual void draw(Vec2 offset, bool selected) {} + virtual EntityBase *setpos(Vec2 pos) { return this; } }; inline static void draw_box2(Box2 box, bool selected, ImU32 color) { @@ -34,6 +40,10 @@ inline static void draw_vec2(Vec2 vec, bool selected, ImU32 color) { list->AddCircle(vec.im(), 7.0f, color); } +#define TOKEN (strtok(NULL, " ")) +#define TOKEN_INT (atoi(TOKEN)) +#define TOKEN_DOUBLE (strtod(TOKEN, NULL)) + // Defined in bundle.cpp extern char buf[1024]; @@ -42,12 +52,25 @@ class Hitbox: public EntityBase { public: std::string type() override { return "hitbox"; } std::string to_file() override { - snprintf(buf, sizeof(buf), "HITBOX %lf %lf %lf %lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y); + snprintf(buf, sizeof(buf), "HITBOX %.0lf %.0lf %.0lf %.0lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y); return buf; } - void imgui() override { DragBox2("", &box); } - void draw(Vec2 offset, bool selected) override { draw_box2(box.offset(offset), selected, IM_COL32(0, 255, 0, 255)); } + void from_strtok() override { + double a, b, c, d; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + c = TOKEN_DOUBLE; + d = TOKEN_DOUBLE; + box = Box2(a, b, c, d); + } + + void imgui() override { DragBox2("", &box); } + void draw(Vec2 offset, bool selected) override { draw_box2(box.offset(offset), selected, IM_COL32(0, 255, 0, 255)); } + EntityBase *setpos(Vec2 pos) override { + box.lefttop = pos; + return this; + } Box2 box; }; @@ -56,12 +79,22 @@ class Player: public EntityBase { public: std::string type() override { return "player"; } std::string to_file() override { - snprintf(buf, sizeof(buf), "PLAYER %lf %lf\n", pos.x, pos.y); + snprintf(buf, sizeof(buf), "PLAYER %.0lf %.0lf\n", pos.x, pos.y); return buf; } + void from_strtok() override { + double a, b, c, d; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + pos = Vec2(a, b); + } - void imgui() override { DragVec2("Player Spawn", &pos); } - void draw(Vec2 offset, bool selected) override { draw_vec2(pos, selected, IM_COL32_WHITE); } + void imgui() override { DragVec2("Player Spawn", &pos); } + void draw(Vec2 offset, bool selected) override { draw_vec2(pos + offset, selected, IM_COL32_WHITE); } + EntityBase *setpos(Vec2 pos) override { + this->pos = pos; + return this; + } Vec2 pos; }; @@ -70,17 +103,33 @@ class HazardRespawn: public EntityBase { public: std::string type() override { return "hazard_respawn"; } std::string to_file() override { - snprintf(buf, sizeof(buf), "HAZARD_RESPAWN %lf %lf %lf %lf %lf %lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y, pos.x, pos.y); + snprintf(buf, sizeof(buf), "HAZARD_RESPAWN %.0lf %.0lf %.0lf %.0lf %.0lf %.0lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y, pos.x, pos.y); return buf; } + void from_strtok() override { + double a, b, c, d, e, f; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + c = TOKEN_DOUBLE; + d = TOKEN_DOUBLE; + e = TOKEN_DOUBLE; + f = TOKEN_DOUBLE; + box = Box2(a, b, c, d); + pos = Vec2(e, f); + } void imgui() override { DragBox2("", &box); DragVec2("Respawn", &pos); } void draw(Vec2 offset, bool selected) override { - draw_box2(box, selected, IM_COL32(255, 0, 255, 255)); - draw_vec2(pos, selected, IM_COL32(255, 0, 255, 255)); + draw_box2(box.offset(offset), selected, IM_COL32(255, 0, 255, 255)); + draw_vec2(pos + offset, selected, IM_COL32(255, 0, 255, 255)); + } + EntityBase *setpos(Vec2 pos) override { + box.lefttop = pos; + this->pos = pos; + return this; } Box2 box; @@ -91,12 +140,24 @@ class Hazard: public EntityBase { public: std::string type() override { return "hazard"; } std::string to_file() override { - snprintf(buf, sizeof(buf), "HAZARD %lf %lf %lf %lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y); + snprintf(buf, sizeof(buf), "HAZARD %.0lf %.0lf %.0lf %.0lf\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y); return buf; } + void from_strtok() override { + double a, b, c, d; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + c = TOKEN_DOUBLE; + d = TOKEN_DOUBLE; + box = Box2(a, b, c, d); + } - void imgui() override { DragBox2("", &box); } - void draw(Vec2 offset, bool selected) override { draw_box2(box, selected, IM_COL32(255, 0, 0, 255)); } + void imgui() override { DragBox2("", &box); } + void draw(Vec2 offset, bool selected) override { draw_box2(box.offset(offset), selected, IM_COL32(255, 0, 0, 255)); } + EntityBase *setpos(Vec2 pos) override { + box.lefttop = pos; + return this; + } Box2 box; }; @@ -106,7 +167,7 @@ public: std::string type() override { return "textbox"; } std::string to_file() override { std::string str; - snprintf(buf, sizeof(buf), "TEXTBOX %lf %lf %lf %lf ", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y); + snprintf(buf, sizeof(buf), "TEXTBOX %.0lf %.0lf %.0lf %.0lf %s ", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y, render_bundle.c_str()); str += buf; // Escape all the whitespaces @@ -129,18 +190,98 @@ public: str += "\n"; return str; } + void from_strtok() override { + double a, b, c, d; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + c = TOKEN_DOUBLE; + d = TOKEN_DOUBLE; + box = Box2(a, b, c, d); + + char *t = TOKEN; + render_bundle = (t ? t : ""); + t = TOKEN; + std::string temp = (t ? t : ""); + text.clear(); + for (int i = 0; i < temp.size(); i++) { + if (temp[i] == '\\') { + if (i == temp.size() - 1) + text.push_back('\\'); // Weird case + else { + if (temp[i + 1] == 's') + text.push_back(' '); + else if (temp[i + 1] == 'n') + text.push_back('\n'); + else if (temp[i + 1] == 't') + text.push_back('\t'); + i++; + } + } else + text.push_back(temp[i]); + } + } void imgui() override { DragBox2("", &box); + ig::InputText("Render Bundle", &render_bundle); ig::InputTextMultiline("##Text", &text, ImVec2(-1, 0)); } void draw(Vec2 offset, bool selected) override { - draw_box2(box, selected, IM_COL32(0, 0, 255, 255)); + draw_box2(box.offset(offset), selected, IM_COL32(0, 0, 255, 255)); // Draw text ImDrawList *list = ig::GetBackgroundDrawList(); - list->AddText(box.lefttop.im(), IM_COL32_WHITE, text.c_str()); + list->AddText((box.lefttop + offset).im(), IM_COL32_WHITE, text.c_str()); + list->AddText((box.lefttop + offset + Vec2(0, box.size.y)).im(), IM_COL32_WHITE, render_bundle.c_str()); + } + EntityBase *setpos(Vec2 pos) override { + box.lefttop = pos; + return this; } std::string text; + std::string render_bundle; Box2 box; }; + + +class LevelTransition: public EntityBase { +public: + std::string type() override { return "level_transition"; } + std::string to_file() override { + snprintf(buf, sizeof(buf), "LEVEL_TRANSITION %.0lf %.0lf %.0lf %.0lf %s\n", box.lefttop.x, box.lefttop.y, box.size.x, box.size.y, next_level.c_str()); + return buf; + } + + void from_strtok() override { + double a, b, c, d; + a = TOKEN_DOUBLE; + b = TOKEN_DOUBLE; + c = TOKEN_DOUBLE; + d = TOKEN_DOUBLE; + box = Box2(a, b, c, d); + + char *t = TOKEN; + if (t != NULL) + next_level = t; + else + next_level.clear(); + } + + void imgui() override { + DragBox2("", &box); + ig::InputText("Next Level", &next_level); + } + void draw(Vec2 offset, bool selected) override { + draw_box2(box.offset(offset), selected, IM_COL32(255, 255, 0, 255)); + // Draw text + ImDrawList *list = ig::GetBackgroundDrawList(); + list->AddText((box.lefttop + offset).im(), IM_COL32_WHITE, next_level.c_str()); + } + EntityBase *setpos(Vec2 pos) override { + box.lefttop = pos; + return this; + } + + Box2 box; + std::string next_level; +}; diff --git a/types.hpp b/types.hpp index ef93c23..5f02a3a 100644 --- a/types.hpp +++ b/types.hpp @@ -43,6 +43,9 @@ public: Vec2 operator+(Vec2 other) const { return Vec2(x + other.x, y + other.y); } + Vec2 operator-(Vec2 other) const { + return Vec2(x - other.x, y - other.y); + } Vec2 operator*(double other) const { return Vec2(x * other, y * other); }