From 205f7d49530e6c32ec0a09045d9cb45dcf9b1752 Mon Sep 17 00:00:00 2001
From: quou <quou@disroot.org>
Date: Wed, 31 Jul 2024 22:12:48 +1000
Subject: GUI + map editor beginnings

---
 3de.c    |  91 ++++++++++++++++++++---------
 Makefile |   3 +
 config.h |   5 ++
 editor.c |  29 ++++++++++
 editor.h |  13 +++++
 gui.c    | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gui.h    |  70 ++++++++++++++++++++++
 map.c    |   5 ++
 map.h    |   4 ++
 9 files changed, 390 insertions(+), 28 deletions(-)
 create mode 100644 editor.c
 create mode 100644 editor.h
 create mode 100644 gui.c
 create mode 100644 gui.h
 create mode 100644 map.c

diff --git a/3de.c b/3de.c
index 1e40f91..bf114ec 100644
--- a/3de.c
+++ b/3de.c
@@ -1,5 +1,7 @@
 #include "asset.h"
 #include "config.h"
+#include "editor.h"
+#include "gui.h"
 #include "map.h"
 #include "maths.h"
 #include "memory.h"
@@ -9,6 +11,25 @@
 #include "services.h"
 #include "tile.h"
 
+static int map_data[] = {
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
 #include <stdio.h>
 
 void draw_spinny(Renderer* r, Colour c) {
@@ -51,28 +72,17 @@ void draw_line(Renderer* r, int x, int y) {
 	ren_line(r, make_red(), s, e);
 }
 
-void draw_map(Renderer* r, Player* p) {
-	int data[512] = {
-		16, 16,
-		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
-		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-	};
+void init_map(Map* m) {
+	int* t = (int*)&m[1], i;
+	m->w = 16;
+	m->h = 16;
+	for (i = 0; i < 0x100; i++) {
+		t[i] = map_data[i];
+	}
+}
+
+void draw_map(Renderer* r, const Player* p, const Map* m) {
 	int pos[2];
-	Map* m = (Map*)data;
 	pos[0] = p->p[0];
 	pos[1] = p->p[2];
 	ren_map(r, m, pos, p->f, p->l);
@@ -81,14 +91,19 @@ void draw_map(Renderer* r, Player* p) {
 int entrypoint(int argc, const char** argv, Arena* a) {
 	App* app;
 	Renderer r = { 0 };
+	GUI g;
+	Editor e;
 	Heap h;
 	FPS f;
 	char buf[32];
 	int* depth = 0;
-	Arena preload;
+	Arena preload, gui_arena, map_arena;
+	Map* map;
+	void* gui_mem;
 	Colour blue = make_aliceblue();
 	Player p;
 	Services s;
+	int editing = 0;
 	(void)argc;
 	(void)argv;
 	init_maths();
@@ -102,6 +117,12 @@ int entrypoint(int argc, const char** argv, Arena* a) {
 		arena_alloc(a, asset_preload_memory_size),
 		asset_preload_memory_size
 	);
+	init_arena(
+		&map_arena,
+		arena_alloc(a, map_memory_size),
+		map_memory_size
+	);
+	gui_mem = arena_alloc(a, gui_memory_size);
 	app = new_app(&h, 640, 480, "3D Engine");
 	if (!app) return app->err;
 	init_fps(&f, 20);
@@ -115,20 +136,34 @@ int entrypoint(int argc, const char** argv, Arena* a) {
 	s.r = &r;
 	cfg_mouse(app, 0);
 	init_player(&p);
+	map = arena_alloc(&map_arena, map_size(max_map_w, max_map_h));
+	init_map(map);
 	while (app->o) {
 		fps_begin(&f);
 		while (f.now >= f.next) {
 			app_begin(app);
-			update_player(&p, &s);
+			init_arena(&gui_arena, gui_mem, gui_memory_size);
+			gui_begin(&g, app, &gui_arena);
+			if (app->key_states[key_f12] & key_state_just_pressed) {
+				editing = !editing;
+				cfg_mouse(app, editing);
+			}
+			if (!editing)
+				update_player(&p, &s);
 			ren_begin(&r, app->fb, depth, app->w, app->h);
-				ren_clear(&r);
+				ren_clearc(&r, make_colour(0xc3c3c3, 0xff));
 				ren_cleard(&r, f1 * 300);
 /*				draw_line(&r, app->mx, app->my);*/
-				push_player_cam(&p);
-					draw_gun(&r);
-					draw_map(&r, &p);
-				pop_player_cam();
+				if (editing) {
+					edit_map(&e, &g, map);
+				} else {
+					push_player_cam(&p);
+						draw_map(&r, &p, map);
+						draw_gun(&r);
+					pop_player_cam();
+				}
 				/*draw_tri(&r, app->mx, app->my);*/
+				gui_end(&g, &r);
 				sprintf(buf, "FPS: %d", app->fps);
 				ren_texts(&r, blue, 3, 3,  buf);
 				sprintf(buf, "CAP: %d", f.fps);
diff --git a/Makefile b/Makefile
index 109b94f..e36d50b 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,9 @@ pack = pack
 sources = \
 	3de.c \
 	asset.c \
+	editor.c \
+	gui.c \
+	map.c \
 	maths.c \
 	memory.c \
 	plat.c \
diff --git a/config.h b/config.h
index 3e4a32d..87a4824 100644
--- a/config.h
+++ b/config.h
@@ -4,6 +4,8 @@
 #define memory_size (1024 * 1024 * 128)
 #define app_memory_size (1024 * 1024 * 32)
 #define asset_preload_memory_size (1024 * 1024 * 8)
+#define gui_memory_size (1024 * 8)
+#define map_memory_size (1024 * 8)
 
 #define max_vp_w 2000
 #define max_vp_h 2000
@@ -11,4 +13,7 @@
 #define max_pc_window_w 5000
 #define max_pc_window_h 3000
 
+#define max_map_w 32
+#define max_map_h 32
+
 #endif
diff --git a/editor.c b/editor.c
new file mode 100644
index 0000000..0aae694
--- /dev/null
+++ b/editor.c
@@ -0,0 +1,29 @@
+#include "editor.h"
+#include "asset.h"
+#include "gui.h"
+#include "map.h"
+#include "render.h"
+
+static GUI_Scroll_State map_ss = { 0 };
+
+void draw_map_scrollable(
+	GUI* g,
+	Renderer* r,
+	const Rect* rect,
+	const GUI_Scroll_State* ss
+) {
+	(void)g;
+	(void)ss;
+	ren_texture(r, rect, get_texture(asset_id_floorboards_texture));
+}
+
+void edit_map(Editor* e, GUI* g, Map* m) {
+	Rect b = gui_viewport(g);
+	Rect t = gui_cut_up(&b, gui_text_height() + 4);
+	(void)e;
+	(void)m;
+	gui_btn(g, gui_cut_left(&t, gui_text_width("Load", 4) + 4), "Save");
+	gui_btn(g, gui_cut_left(&t, gui_text_width("Save", 4) + 4), "Load");
+	gui_scrollable(g, b, &map_ss, draw_map_scrollable);
+}
+
diff --git a/editor.h b/editor.h
new file mode 100644
index 0000000..ffe22e5
--- /dev/null
+++ b/editor.h
@@ -0,0 +1,13 @@
+#ifndef editor_h
+#define editor_h
+
+struct GUI;
+struct Map;
+
+typedef struct {
+	int pad;
+} Editor;
+
+void edit_map(Editor* e, struct GUI* g, struct Map* m);
+
+#endif
diff --git a/gui.c b/gui.c
new file mode 100644
index 0000000..0fe1f69
--- /dev/null
+++ b/gui.c
@@ -0,0 +1,198 @@
+#include "gui.h"
+#include "memory.h"
+#include "plat.h"
+#include "render.h"
+#include "standard.h"
+
+void* gui_alloc(GUI* g, int size) {
+	GUI_El* e = arena_alloc(g->a, size);
+	if (g->prev)
+		g->prev->next = e;
+	if (!g->first)
+		g->first = e;
+	g->prev = e;
+	e->next = 0;
+	return e;
+}
+
+void gui_begin(GUI* g, const App* app, Arena* a) {
+	g->a = a;
+	g->app = app;
+	g->pad = 2;
+	g->first = 0;
+	g->cursor[0] = 10;
+	g->cursor[1] = 10;
+}
+
+void gui_get_layout(GUI* g, Rect* r) {
+	r->x = g->cursor[0];
+	r->y = g->cursor[1];
+	g->cursor[1] += r->h;
+}
+
+int gui_text_width(const char* text, int len) {
+	(void)text;
+	return len * 10;
+}
+
+int gui_text_height(void) {
+	return 10;
+}
+
+int mouse_over(const App* app, const Rect* r) {
+	return
+		app->mx > r->x &&
+		app->my > r->y &&
+		app->mx < r->x + r->w &&
+		app->my < r->y + r->h;
+}
+
+Rect gui_viewport(GUI* g) {
+	Rect r = { 0 };
+	r.w = g->app->w;
+	r.h = g->app->h;
+	return r;
+}
+
+Rect gui_cut_left(Rect* a, int v) {
+	Rect r = * a;
+	a->x += v;
+	a->w -= v;
+	r.w -= a->w;
+	return r;
+}
+
+Rect gui_cut_right(Rect* a, int v) {
+	Rect r = *a;
+	a->w -= v;
+	r.x += v;
+	r.w -= v;
+	return r;
+}
+
+Rect gui_cut_down(Rect* a, int v) {
+	Rect r = *a;
+	a->h -= v;
+	r.y += v;
+	r.h -= v;
+	return r;
+}
+
+Rect gui_cut_up(Rect* a, int v) {
+	Rect r = * a;
+	a->y += v;
+	a->h -= v;
+	r.h -= a->h;
+	return r;
+}
+
+int gui_btn(GUI* g, Rect r, const char* text) {
+	int len = string_len(text);
+	GUI_Btn* e = gui_alloc(g, sizeof *e);
+	e->el.type = gui_el_type_btn;
+	e->el.r = r;
+	e->text = arena_alloc_aligned(g->a, len + 1, 1);
+	string_copy(e->text, text);
+	e->hover = mouse_over(g->app, &e->el.r);
+	e->active = e->hover &&
+		g->app->mbtn_states[mbtn_left] & key_state_pressed;
+	return e->hover &&
+		g->app->mbtn_states[mbtn_left] & key_state_just_released;
+}
+
+void gui_scrollable(
+	GUI* g,
+	Rect r,
+	GUI_Scroll_State* state,
+	GUI_Scrollable_Draw_Fn fn
+) {
+	GUI_Scrollable* e = gui_alloc(g, sizeof *e);
+	e->el.type = gui_el_type_scrollable;
+	e->el.r = r;
+	e->ss = state;
+	e->draw = fn;
+}
+
+void rect_cfit(Rect* r) {
+	r->x++;
+	r->y++;
+	r->w -= 2;
+	r->h -= 2;
+}
+
+void draw_container(Renderer* r, const Rect* rect, int active) {
+	const Colour bg   = make_colour(0xc3c3c3, 0xff);
+	Colour out1       = make_colour(0xffffff, 0xff);
+	Colour out2       = make_colour(0x000000, 0xff);
+	Rect t = *rect;
+	if (active) {
+		Colour t = out1;
+		out1 = out2;
+		out2 = t;
+	}
+	ren_rect(r, bg, rect);
+	t.h = 1;
+	ren_rect(r, out1, &t);
+	t.w = 1;
+	t.h = rect->h;
+	ren_rect(r, out1, &t);
+	t.h--;
+	t.x += rect->w - 1;
+	t.y++;
+	ren_rect(r, out2, &t);
+	t.y = rect->y + rect->h - 1;
+	t.x = rect->x + 1;
+	t.w = rect->w - 1;
+	t.h = 1;
+	ren_rect(r, out2, &t);
+}
+
+void draw_btn(GUI* g, Renderer* r, const GUI_Btn* btn) {
+	const Colour txt  = make_colour(0x000000, 0xff);
+	int tp[2];
+	Rect rect = btn->el.r;
+	(void)g;
+	tp[0] =
+		rect.x + rect.w / 2 -
+		gui_text_width(btn->text, string_len(btn->text)) / 2;
+	tp[1] =
+		rect.y + rect.h / 2 -
+		gui_text_height() / 2;
+	if (btn->active) {
+		tp[0]++;
+		tp[1]++;
+	}
+	draw_container(r, &rect, btn->active);
+	rect_cfit(&rect);
+	ren_clip(r, &rect);
+	ren_text(r, txt, tp[0], tp[1], btn->text);
+	ren_clipr(r);
+}
+
+void draw_scrollable(
+	GUI* g,
+	Renderer* r,
+	const GUI_Scrollable* s
+) {
+	Rect rect = s->el.r;
+	draw_container(r, &s->el.r, 0);
+	rect_cfit(&rect);
+	ren_clip(r, &rect);
+	s->draw(g, r, &rect, s->ss);
+	ren_clipr(r);
+}
+
+void gui_end(GUI* g, Renderer* r) {
+	GUI_El* el = g->first;
+	while (el) {
+		switch (el->type) {
+			case gui_el_type_btn:
+				draw_btn(g, r, (GUI_Btn*)el);
+				break;
+			case gui_el_type_scrollable:
+				draw_scrollable(g, r, (GUI_Scrollable*)el);
+				break;
+		}
+		el = el->next;
+	}
+}
diff --git a/gui.h b/gui.h
new file mode 100644
index 0000000..50927f4
--- /dev/null
+++ b/gui.h
@@ -0,0 +1,70 @@
+#ifndef gui_h
+#define gui_h
+
+#include "rect.h"
+
+struct App;
+struct Arena;
+struct GUI;
+struct Renderer;
+
+typedef enum {
+	gui_el_type_btn,
+	gui_el_type_scrollable
+} GUI_El_Type;
+
+typedef struct GUI_El {
+	GUI_El_Type type;
+	struct GUI_El* next;
+	Rect r;
+} GUI_El;
+
+typedef struct {
+	GUI_El el;
+	char* text;
+	int hover, active;
+} GUI_Btn;
+
+typedef struct {
+	int x, y;
+} GUI_Scroll_State;
+
+typedef void (*GUI_Scrollable_Draw_Fn)(
+	struct GUI*,
+	struct Renderer*,
+	const Rect*,
+	const GUI_Scroll_State*
+);
+
+typedef struct {
+	GUI_El el;
+	GUI_Scrollable_Draw_Fn draw;
+	GUI_Scroll_State* ss;
+} GUI_Scrollable;
+
+typedef struct GUI {
+	const struct App* app;
+	struct Arena* a;
+	GUI_El* first, * prev;
+	int cursor[2], pad;
+} GUI;
+
+int gui_text_width(const char* text, int len);
+int gui_text_height(void);
+
+void gui_begin(GUI* g, const struct App* app, struct Arena* a);
+void gui_end(GUI* g, struct Renderer* r);
+Rect gui_viewport(GUI* g);
+Rect gui_cut_left(Rect* a, int v);
+Rect gui_cut_right(Rect* a, int v);
+Rect gui_cut_down(Rect* a, int v);
+Rect gui_cut_up(Rect* a, int v);
+int gui_btn(GUI* g, Rect r, const char* text);
+void gui_scrollable(
+	GUI* g,
+	Rect r,
+	GUI_Scroll_State* state,
+	GUI_Scrollable_Draw_Fn fn
+);
+
+#endif
diff --git a/map.c b/map.c
new file mode 100644
index 0000000..3313f10
--- /dev/null
+++ b/map.c
@@ -0,0 +1,5 @@
+#include "map.h"
+
+int map_size(int w, int h) {
+	return sizeof(Map) + w * h * sizeof(Map_Tile);
+}
diff --git a/map.h b/map.h
index 6b99d9a..fbd64f1 100644
--- a/map.h
+++ b/map.h
@@ -5,4 +5,8 @@ typedef struct Map {
 	int w, h;
 } Map;
 
+typedef unsigned Map_Tile;
+
+int map_size(int w, int h);
+
 #endif
-- 
cgit v1.2.3-54-g00ecf