Fully working map editor
This commit is contained in:
		
							
								
								
									
										12
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								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(); | ||||
|   | ||||
							
								
								
									
										138
									
								
								map.cpp
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								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 <set> | ||||
| #include <cmath> | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| #include <list> | ||||
| #include <sstream> | ||||
|  | ||||
|  | ||||
| static std::set<EntityBase *> entities, to_delete; | ||||
| static std::list<EntityBase *> 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; | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										177
									
								
								map_types.hpp
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								map_types.hpp
									
									
									
									
									
								
							| @@ -3,18 +3,24 @@ | ||||
| #include "types.hpp" | ||||
| #include "ig001.hpp" | ||||
| #include "gui/imgui/imgui_stdlib.h" | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| #include <iterator> | ||||
| #include <string> | ||||
|  | ||||
|  | ||||
| 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; | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user