commit 963ea7493c3dcd6f5ac9ab18c9b913dd62e127aa
parent 0deed8c756e9aa4647ab36218b24c1fbc4497953
Author: hhvn <dev@hhvn.uk>
Date: Thu, 9 Jun 2022 23:06:20 +0100
Draw system view
Diffstat:
12 files changed, 686 insertions(+), 87 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -5,3 +5,4 @@ data/worlds.tsv
db/dbtool
game
testdb/
+core.*
diff --git a/Makefile b/Makefile
@@ -8,19 +8,21 @@ OBJ = $(SRC:.c=.o)
BIN = game
RAYLIB = -lraylib -lGL -lm -lpthread -ldl -lrt -lX11
LDFLAGS = $(RAYLIB) $(DBLIB)
-CFLAGS = -g3 -O0
+CFLAGS = -Wall -g3 -O0
all: db data $(BIN)
-$(BIN): $(OBJ)
+$(OBJ): src/struct.h
+$(BIN): $(OBJ) $(DBLIB)
$(CC) $(LDFLAGS) -o $(BIN) $(OBJ)
clean: db-clean
rm -f $(BIN) $(OBJ)
-db:
+db: $(DBLIB)
+$(DBLIB):
@echo $(DBDIR): make $(DBLIB)
- @cd $(DBDIR); make `basename $(DBLIB)`
+ @cd $(DBDIR); make CFLAGS="$(CFLAGS)" `basename $(DBLIB)`
db-clean:
@echo $(DBDIR): make clean
@cd $(DBDIR); make clean
diff --git a/data/worlds-parse.awk b/data/worlds-parse.awk
@@ -44,7 +44,7 @@ BEGIN {
if (type == "*") nicetype = "Star"
if (type == "A") nicetype = "Asteroid"
- if (type == "AS") nicetype = "Asteroid moon"
+ if (type == "AS") nicetype = "Moon"
if (type == "M") nicetype = "Moon"
if (type == "P") nicetype = "Planet"
@@ -132,9 +132,10 @@ BEGIN {
printf("mindist\t%d\n", minorb) > file
printf("maxdist\t%d\n", maxorb) > file
printf("curdist\t%d\n", (rand() * (maxorb - minorb)) + minorb) > file
+ printf("theta\t%d\n", rand() * 360) > file
} else {
printf("dist\t%d\n", (minorb + maxorb) / 2) > file
+ printf("curtheta\t%d\n", rand() * 360) > file
}
- printf("curtheta\t%d\n", rand() * 360) > file
}
diff --git a/db/db.c b/db/db.c
@@ -329,8 +329,8 @@ dblistgroups_f(char ***ret, char *dir, int (*filter)(void *data, char *path), vo
*ret = NULL;
return 0; /* malloc sets errno */
}
- for (p = res, prev = NULL; p; p = p->next) {
- *((*ret) + i++) = strdup(p->path + strlen(dir) + 1);
+ for (i = 0, p = res, prev = NULL; p; p = p->next, i++) {
+ *((*ret) + i) = strdup(p->path + strlen(dir) + 1);
free(p->path);
free(prev);
prev = p;
diff --git a/src/main.c b/src/main.c
@@ -29,10 +29,12 @@ main(void) {
else if (IsKeyPressed(KEY_SIX)) view_tabs.sel = 5;
}
+ view_handlers[view_tabs.sel]();
+
BeginDrawing();
ClearBackground(COL_BG);
- ui_draw_views();
view_drawers[view_tabs.sel]();
+ ui_draw_views();
EndDrawing();
}
diff --git a/src/main.h b/src/main.h
@@ -3,6 +3,7 @@
#include <raylib.h>
#include "struct.h"
#include "style.h"
+#include "maths.h"
#include "../db/db.h"
#define ELEMS(array) (sizeof(array)/sizeof(array[0]))
@@ -13,26 +14,42 @@ extern Save *save;
/* str.c */
char * vsmprintf(char *fmt, va_list args);
char * smprintf(char *fmt, ...); /* return allocated string */
-char * vsbprintf(char *fmt, va_list args);
-char * sbprintf(char *fmt, ...); /* return string that is usable until next call */
+char * nstrdup(char *str); /* NULL-safe */
+char * strkmdist(float km);
+char * strlightdist(float km);
+int streq(char *s1, char *s2); /* NULL-safe, no `== 0` required */
+int strlistpos(char *str, char **list, size_t len);
+float strnum(char *str);
/* ui.c */
-#define VIEWS_MAX_WIDTH (UI_VIEW_LAST*100)
-#define VIEWS_HEIGHT 25
-#define WINDOW_TAB_HEIGHT 20
extern Tabs view_tabs;
+extern void (*view_handlers[UI_VIEW_LAST])(void);
extern void (*view_drawers[UI_VIEW_LAST])(void);
void ui_init(void);
void ui_deinit(void);
void ui_print(int x, int y, Color col, char *format, ...);
int ui_textsize(char *text);
+int ui_collides(Rect rect, Vector2 point);
void ui_clickable_register(int x, int y, int w, int h, enum UiElements type, void *elem);
void ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable);
void ui_clickable_update(void);
void ui_draw_views(void);
void ui_draw_border(int x, int y, int w, int h, int px);
-void ui_draw_tabs(Tabs *tabs, int x, int y, int w, int h);
-void ui_draw_tabbed_window(Tabs *tabs, int x, int y, int w, int h);
+void ui_draw_tabs(int x, int y, int w, int h, Tabs *tabs);
+void ui_draw_tabbed_window(int x, int y, int w, int h, Tabs *tabs);
+void ui_draw_checkbox(int x, int y, Checkbox *checkbox);
+Vector2 ui_kmtopx(Vector2 km);
+Vector2 ui_pxtokm(Vector2 vector);
+Vector2 ui_vectordiff(Vector2 a, Vector2 b);
+float ui_vectordist(Vector2 a, Vector2 b);
+int ui_should_draw_body(Body *body, int orbit);
+void ui_draw_body(Body *body);
+void ui_handle_view_main(void);
+void ui_handle_view_colonies(void);
+void ui_handle_view_fleets(void);
+void ui_handle_view_design(void);
+void ui_handle_view_systems(void);
+void ui_handle_view_settings(void);
void ui_draw_view_main(void);
void ui_draw_view_colonies(void);
void ui_draw_view_fleets(void);
@@ -45,8 +62,10 @@ Vector2 system_vectorize(Polar polar);
Vector2 system_vectorize_around(Vector2 around, Polar polar);
Polar system_polarize(Vector2 vector);
Polar system_sum_polar(Polar absolute, Polar relative);
-Vector2 system_get_vector(char *system, char *body);
-Polar system_get_polar(char *system, char *body);
+Vector2 system_get_vector(Body *body);
+Polar system_get_polar(Body *body);
+System *system_init(char *name);
+System *system_load(System *s, char *name);
/* save.c */
Save * save_init(char *savedir);
diff --git a/src/maths.h b/src/maths.h
@@ -0,0 +1,18 @@
+#define YOTTA (ZETTA * 1000)
+#define ZETTA (EXA * 1000)
+#define EXA (PETA * 1000)
+#define PETA (TERA * 1000)
+#define TERA (GIGA * 1000)
+#define GIGA (MEGA * 1000)
+#define MEGA (KILO * 1000)
+#define KILO 1000
+
+#define C_MS 299792458 /* c, in m/s */
+
+#define SEC_LEN 1
+#define MIN_LEN (60 * SEC_LEN)
+#define HOUR_LEN (60 * MIN_LEN)
+#define DAY_LEN (24 * HOUR_LEN)
+#define WEEK_LEN (7 * DAY_LEN)
+#define YEAR_LEN (365.25 * DAY_LEN)
+#define MONTH_APPROX (YEAR_LEN / 12)
diff --git a/src/save.c b/src/save.c
@@ -13,5 +13,6 @@ save_init(char *dir) {
ret->races = smprintf("%s/Races", dir);
ret->systems = smprintf("%s/Systems", dir);
ret->fleets = smprintf("%s/Fleets", dir);
+ memset(&ret->cache, 0, sizeof(ret->cache));
return ret;
};
diff --git a/src/str.c b/src/str.c
@@ -1,6 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <math.h>
+#include "main.h"
char *
vsmprintf(char *fmt, va_list args) {
@@ -36,24 +41,77 @@ smprintf(char *fmt, ...) {
}
char *
-vsbprintf(char *fmt, va_list args) {
+nstrdup(char *str) {
+ if (!str) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return strdup(str);
+}
+
+char *
+strkmdist(float km) {
static char *ret = NULL;
- va_list ap;
free(ret);
- va_copy(ap, args);
- ret = vsmprintf(fmt, args);
- va_end(ap);
+
+ if (km > GIGA)
+ ret = smprintf("%.2fb km", km / GIGA);
+ else if (km > MEGA)
+ ret = smprintf("%.2fm km", km / MEGA);
+ else if (km > KILO)
+ ret = smprintf("%.2fk km", km / KILO);
+ else
+ ret = smprintf("%.2f km", km);
+
return ret;
}
char *
-sbprintf(char *fmt, ...) {
- va_list ap;
- char *ret;
+strlightdist(float km) {
+ static char *ret = NULL;
+ float ls = km * KILO / C_MS;
+
+ free(ret);
+
+ if (ls > YEAR_LEN)
+ ret = smprintf("%.2f ly", ls / YEAR_LEN);
+ else if (ls > DAY_LEN)
+ ret = smprintf("%.2f ld", ls / DAY_LEN);
+ else if (ls > HOUR_LEN)
+ ret = smprintf("%.2f lh", ls / HOUR_LEN);
+ else if (ls > MIN_LEN)
+ ret = smprintf("%.2f lm", ls / MIN_LEN);
+ else
+ ret = smprintf("%.2f ls", ls);
- va_start(ap, fmt);
- ret = vsbprintf(fmt, ap);
- va_end(ap);
return ret;
}
+
+int
+streq(char *s1, char *s2) {
+ if (s1 == s2)
+ return 1;
+ else if (!s1 || !s2)
+ return 0;
+ else if (strcmp(s1, s2) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+int
+strlistpos(char *str, char **list, size_t len) {
+ int i;
+
+ for (i = 0; i < INT_MAX && i < len; i++)
+ if (streq(str, *(list + i)))
+ return i;
+ return -1;
+}
+
+float
+strnum(char *str) {
+ if (!str) return 0;
+ return strtof(str, NULL);
+}
diff --git a/src/struct.h b/src/struct.h
@@ -1,26 +1,96 @@
#include <stddef.h>
-typedef struct Save Save;
-struct Save {
+/* system.c */
+typedef struct {
+ float r;
+ float theta;
+} Polar;
+
+/* body->type + body->parent->type == complex type
+ *
+ * Ex. for orbiting a star:
+ * BODY_PLANET + BODY_STAR == BODY_PLANET
+ *
+ * Ex. for a moon orbiting a body:
+ * BODY_MOON + BODY_PLANET is unique
+ */
+enum BodyType {
+ BODY_STAR = 0,
+ BODY_PLANET,
+ BODY_DWARF,
+ BODY_ASTEROID,
+ BODY_COMET,
+ BODY_MOON,
+ BODY_LAST = BODY_MOON * 2
+};
+
+typedef struct Body Body;
+struct Body {
+ Body *parent;
+ Polar polar;
+ Vector2 vector;
+ char *name;
+ enum BodyType type;
+ float radius;
+ float mass;
+ float orbdays;
+ union {
+ /* comet */
+ struct {
+ float mindist;
+ float maxdist;
+ float curdist;
+ float theta;
+ int inward;
+ };
+ /* everything else */
+ struct {
+ float dist;
+ float curtheta;
+ };
+ };
+};
+
+typedef struct {
+ char *name;
+ Body **bodies;
+ size_t bodies_len;
+} System;
+
+/* save.c */
+typedef struct {
/* db locator strings.
* dir the base, everything else is "dir/var" */
char *dir;
char *races;
char *systems;
char *fleets;
-};
+ struct {
+ System *system;
+ } cache;
+} Save;
/* ui.c */
+/* I know raylib has Rectangle, but I don't want to type width and height */
+typedef struct {
+ int x, y;
+ int w, h;
+} Rect;
+
#define TABS_MAX 16
-typedef struct Tabs Tabs;
-struct Tabs {
+typedef struct {
int n;
int sel;
struct {
char *name;
int w; /* 0 = fill */
} tabs[TABS_MAX];
-};
+} Tabs;
+
+typedef struct {
+ int val;
+ char *label;
+} Checkbox;
enum UiViews {
UI_VIEW_MAIN,
@@ -34,20 +104,12 @@ enum UiViews {
enum UiElements {
UI_TAB,
+ UI_CHECKBOX,
};
#define CLICKABLE_MAX 64
-typedef struct Clickable Clickable;
-struct Clickable {
- int x, y;
- int w, h;
+typedef struct {
+ Rect geom;
enum UiElements type;
void *elem;
-};
-
-/* system.c */
-typedef struct Polar Polar;
-struct Polar {
- float r;
- float theta;
-};
+} Clickable;
diff --git a/src/system.c b/src/system.c
@@ -4,6 +4,19 @@
#include "raylib.h"
#include "main.h"
+char *body_names[BODY_LAST] = {
+ [BODY_STAR] = "Star",
+ [BODY_PLANET] = "Planet",
+ [BODY_DWARF] = "Dwarf planet",
+ [BODY_ASTEROID] = "Asteroid",
+ [BODY_COMET] = "Comet",
+ [BODY_MOON] = "Moon",
+ [BODY_PLANET + BODY_MOON] = "Moon",
+ [BODY_DWARF + BODY_MOON] = "Dwarf planet moon",
+ [BODY_ASTEROID + BODY_MOON] = "Asteroid moon",
+ [BODY_COMET + BODY_MOON] = "Comet moon",
+};
+
Vector2
system_vectorize(Polar polar) {
return (Vector2) {
@@ -31,31 +44,147 @@ system_polarize(Vector2 vector) {
Polar
system_sum_polar(Polar absolute, Polar relative) {
- return system_polarize(
- system_vectorize_around(system_vectorize(absolute), relative));
+ return system_polarize(system_vectorize_around(system_vectorize(absolute), relative));
}
Vector2
-system_get_vector(char *system, char *body) {
- return system_vectorize(system_get_polar(system, body));
+system_get_vector(Body *body) {
+ return system_vectorize(system_get_polar(body));
}
Polar
-system_get_polar(char *system, char *body) {
+system_get_polar(Body *body) {
Polar polar;
- char *group, *dist, *parent;
- group = sbprintf("%s/%s", system, body);
- if (strcmp(dbget(save->systems, group, "type"), "Comet") == 0)
- dist = dbget(save->systems, group, "curdist");
- else
- dist = dbget(save->systems, group, "dist");
+ if (body->polar.r != INFINITY &&
+ body->polar.theta != INFINITY)
+ return body->polar;
+
+ if (body->type == BODY_COMET) {
+ if (!body->parent) {
+ polar.r = body->curdist;
+ polar.theta = body->theta;
+ } else {
+ polar = system_sum_polar(system_get_polar(body->parent),
+ (Polar){body->curdist, body->theta});
+ }
+ } else {
+ if (!body->parent) {
+ polar.r = body->dist;
+ polar.theta = body->curtheta;
+ } else {
+ polar = system_sum_polar(system_get_polar(body->parent),
+ (Polar){body->dist, body->curtheta});
+ }
+ }
- parent = dbget(save->systems, group, "parent");
- polar.r = strtof(dist, NULL);
- polar.theta = strtof(dbget(save->systems, group, "curtheta"), NULL);
- if (!parent)
- return (Polar) {polar.r, polar.theta};
+ body->polar = polar;
+ return polar;
+}
+
+System *
+system_init(char *name) {
+ System *ret = malloc(sizeof(System));
+ if (!ret) return NULL;
+ ret->name = strdup(name);
+ ret->bodies = NULL;
+ ret->bodies_len = 0;
+ return ret;
+}
+
+static int
+filter_bodyinsystem(void *data, char *path) {
+ char *system = data;
+
+ if (!strstr(path, "/index") && strncmp(path, system, strlen(system)) != 0)
+ return 1;
else
- return system_sum_polar(system_get_polar(system, parent), polar);
+ return 0;
+}
+
+/* If s is true, ignore name and load the system.
+ * If s is NULL, call system_init(name); */
+System *
+system_load(System *s, char *name) {
+ char *dir, *tmp;
+ char **bname;
+ char **bparent;
+ size_t blen, i;
+ int pos;
+
+ if (!s) s = system_init(name);
+
+ dir = smprintf("%s/", s->name);
+ s->bodies_len = blen = dblistgroups_f(&bname, save->systems, &filter_bodyinsystem, dir);
+ if (!bname) return NULL;
+ bparent = malloc(sizeof(char *) * blen);
+ s->bodies = malloc(sizeof(Body *) * blen);
+
+ if (!bparent || !s->bodies)
+ return NULL;
+
+ /* first pass: init bodies and parents */
+ for (i = 0; i < blen; i++) {
+ s->bodies[i] = malloc(sizeof(Body));
+ s->bodies[i]->name = nstrdup(bname[i] + strlen(dir));
+ bparent[i] = dbget(save->systems, bname[i], "parent");
+
+ tmp = dbget(save->systems, bname[i], "type");
+ if (streq(tmp, "Star"))
+ s->bodies[i]->type = BODY_STAR;
+ else if (streq(tmp, "Planet"))
+ s->bodies[i]->type = BODY_PLANET;
+ else if (streq(tmp, "Dwarf planet"))
+ s->bodies[i]->type = BODY_DWARF;
+ else if (streq(tmp, "Asteroid"))
+ s->bodies[i]->type = BODY_ASTEROID;
+ else if (streq(tmp, "Comet"))
+ s->bodies[i]->type = BODY_COMET;
+ else if (streq(tmp, "Moon"))
+ s->bodies[i]->type = BODY_MOON;
+
+ s->bodies[i]->radius = strnum(dbget(save->systems, bname[i], "radius"));
+ s->bodies[i]->mass = strnum(dbget(save->systems, bname[i], "mass"));
+ s->bodies[i]->orbdays = strnum(dbget(save->systems, bname[i], "orbdays"));
+ if (s->bodies[i]->type == BODY_COMET) {
+ /* mindist is on opposite side of parent */
+ s->bodies[i]->mindist = 0 - strnum(dbget(save->systems, bname[i], "mindist"));
+ s->bodies[i]->maxdist = strnum(dbget(save->systems, bname[i], "maxdist"));
+ s->bodies[i]->curdist = strnum(dbget(save->systems, bname[i], "curdist"));
+ s->bodies[i]->theta = strnum(dbget(save->systems, bname[i], "theta"));
+ s->bodies[i]->inward = strnum(dbget(save->systems, bname[i], "inward"));
+ } else {
+ s->bodies[i]->dist = strnum(dbget(save->systems, bname[i], "dist"));
+ s->bodies[i]->curtheta = strnum(dbget(save->systems, bname[i], "curtheta"));
+ }
+
+ /* so system_get_polar() knows if it's usable */
+ s->bodies[i]->polar = (Polar) { INFINITY, INFINITY };
+ }
+
+ /* second pass: assign parents (needs bparent[] from first pass) */
+ for (i = 0; i < blen; i++) {
+ tmp = smprintf("%s%s", dir, bparent[i]);
+ if ((pos = strlistpos(tmp, bname, blen)) != -1)
+ s->bodies[i]->parent = s->bodies[pos];
+ else
+ s->bodies[i]->parent = NULL;
+ free(tmp);
+ }
+
+ /* third pass: get coords (needs parent ptr from second pass) */
+ for (i = 0; i < blen; i++) {
+ system_get_polar(s->bodies[i]); /* Builds the cache for us: this is more
+ efficient as it can cache the parent too */
+ s->bodies[i]->vector = system_vectorize(s->bodies[i]->polar);
+ }
+
+ for (i = 0; i < blen; i++) {
+ free(bparent[i]);
+ }
+ free(bparent);
+ dblistfree(bname, blen);
+ free(dir);
+
+ return s;
}
diff --git a/src/ui.c b/src/ui.c
@@ -1,9 +1,27 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
#include <raylib.h>
#include "main.h"
+#define VIEWS_MAX_WIDTH (UI_VIEW_LAST*100)
+#define VIEWS_HEIGHT 25
+#define WINDOW_TAB_HEIGHT 20
+#define TARGET_FPS 60
+#define MIN_BODY_DIAM 3
+
static Font font;
static Clickable clickable[CLICKABLE_MAX];
+void (*view_handlers[UI_VIEW_LAST])(void) = {
+ [UI_VIEW_MAIN] = ui_handle_view_main,
+ [UI_VIEW_COLONIES] = ui_handle_view_colonies,
+ [UI_VIEW_FLEETS] = ui_handle_view_fleets,
+ [UI_VIEW_DESIGN] = ui_handle_view_design,
+ [UI_VIEW_SYSTEMS] = ui_handle_view_systems,
+ [UI_VIEW_SETTINGS] = ui_handle_view_settings,
+};
+
void (*view_drawers[UI_VIEW_LAST])(void) = {
[UI_VIEW_MAIN] = ui_draw_view_main,
[UI_VIEW_COLONIES] = ui_draw_view_colonies,
@@ -18,11 +36,72 @@ Tabs view_tabs = {
{"Systems", 0}, {"Settings", 0}}
};
+static struct {
+ struct {
+ Tabs tabs;
+ struct {
+ Checkbox dwarf;
+ Checkbox asteroid;
+ Checkbox comet;
+ } names;
+ struct {
+ Checkbox dwarf;
+ Checkbox asteroid;
+ Checkbox comet;
+ } orbit;
+ Rect geom;
+ } infobox;
+ int pan;
+ struct {
+ int held;
+ Vector2 origin;
+ } ruler;
+ float kmx, kmy;
+ float kmperpx;
+ struct {
+ int x, y; /* real y = GetScreenHeight() - y */
+ int w, h;
+ } scale;
+} view_main = {
+ .infobox = {
+ .tabs = {
+ 2, 0, {{"Display", 0}, {"Minerals", 0}}
+ },
+ .names = {
+ .dwarf = {1, "Name: dwarf planets"},
+ .asteroid = {0, "Name: asteroid"},
+ .comet = {1, "Name: comet"},
+ },
+ .orbit = {
+ .dwarf = {0, "Orbit: dwarf planets"},
+ .asteroid = {0, "Orbit: asteroid"},
+ .comet = {0, "Orbit: comet"},
+ },
+ .geom = {
+ .x = 10,
+ .y = VIEWS_HEIGHT + 10,
+ .w = 200,
+ .h = 400,
+ },
+ },
+ .pan = 0,
+ .ruler = {.held = 0},
+ .kmx = 0,
+ .kmy = 0,
+ .kmperpx = 500000,
+ .scale = {
+ .x = 10,
+ .y = 10, /* from bottom */
+ .w = 50,
+ .h = 3,
+ },
+};
+
void
ui_init(void) {
InitWindow(500, 500, "testing raylib");
SetWindowState(FLAG_WINDOW_RESIZABLE);
- SetTargetFPS(30);
+ SetTargetFPS(TARGET_FPS);
font = LoadFontFromMemory(".ttf",
DejaVuSansMono_ttf, DejaVuSansMono_ttf_size,
@@ -38,12 +117,15 @@ void
ui_print(int x, int y, Color col, char *fmt, ...) {
va_list ap;
Vector2 pos;
+ char *text;
pos.x = x;
pos.y = y;
va_start(ap, fmt);
- DrawTextEx(font, vsbprintf(fmt, ap), pos, (float)FONT_SIZE, FONT_SIZE/10, col);
+ text = vsmprintf(fmt, ap);
va_end(ap);
+ DrawTextEx(font, text, pos, (float)FONT_SIZE, FONT_SIZE/10, col);
+ free(text);
}
int
@@ -51,16 +133,25 @@ ui_textsize(char *text) {
return MeasureTextEx(font, text, FONT_SIZE, FONT_SIZE/10).x;
}
+int
+ui_collides(Rect rect, Vector2 point) {
+ if (point.x >= rect.x && point.x <= rect.x + rect.w &&
+ point.y >= rect.y && point.y <= rect.y + rect.h)
+ return 1;
+ else
+ return 0;
+}
+
void
ui_clickable_register(int x, int y, int w, int h, enum UiElements type, void *elem) {
int i;
for (i = 0; i < CLICKABLE_MAX; i++) {
if (!clickable[i].elem) {
- clickable[i].x = x;
- clickable[i].y = y;
- clickable[i].w = w;
- clickable[i].h = h;
+ clickable[i].geom.x = x;
+ clickable[i].geom.y = y;
+ clickable[i].geom.w = w;
+ clickable[i].geom.h = h;
clickable[i].type = type;
clickable[i].elem = elem;
return;
@@ -73,6 +164,7 @@ ui_clickable_register(int x, int y, int w, int h, enum UiElements type, void *el
void
ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable) {
Tabs *tabs;
+ Checkbox *checkbox;
int ftabw, fw, fn, tabw, x;
int i;
@@ -81,16 +173,16 @@ ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable) {
if (button != MOUSE_BUTTON_LEFT)
return;
tabs = clickable->elem;
- for (fw = clickable->w, fn = i = 0; i < tabs->n; i++) {
+ for (fw = clickable->geom.w, fn = i = 0; i < tabs->n; i++) {
if (!tabs->tabs[i].w)
fn++;
else
fw -= tabs->tabs[i].w;
}
ftabw = fw / fn;
- for (i = 0, x = clickable->x; i < tabs->n; x += tabw, i++) {
+ for (i = 0, x = clickable->geom.x; i < tabs->n; x += tabw, i++) {
if (i == tabs->n - 1)
- tabw = clickable->x + clickable->w - x;
+ tabw = clickable->geom.x + clickable->geom.w - x;
else if (!tabs->tabs[i].w)
tabw = ftabw;
else
@@ -101,6 +193,12 @@ ui_clickable_handle(Vector2 mouse, MouseButton button, Clickable *clickable) {
}
}
break;
+ case UI_CHECKBOX:
+ if (button != MOUSE_BUTTON_LEFT)
+ return;
+ checkbox = clickable->elem;
+ checkbox->val = !checkbox->val;
+ break;
}
}
@@ -118,11 +216,7 @@ ui_clickable_update(void) {
else button = -1;
for (i = 0; i < CLICKABLE_MAX; i++) {
- if (clickable[i].elem &&
- mouse.x >= clickable[i].x &&
- mouse.x <= clickable[i].x + clickable[i].w &&
- mouse.y >= clickable[i].y &&
- mouse.y <= clickable[i].y + clickable[i].h)
+ if (clickable[i].elem && ui_collides(clickable[i].geom, mouse))
ui_clickable_handle(mouse, button, &clickable[i]);
clickable[i].elem = NULL;
}
@@ -132,7 +226,7 @@ void
ui_draw_views(void) {
int sw = GetScreenWidth();
if (sw > VIEWS_MAX_WIDTH) sw = VIEWS_MAX_WIDTH;
- ui_draw_tabs(&view_tabs, 0, 0, sw, VIEWS_HEIGHT);
+ ui_draw_tabs(0, 0, sw, VIEWS_HEIGHT, &view_tabs);
}
void
@@ -144,13 +238,15 @@ ui_draw_border(int x, int y, int w, int h, int px) {
}
void
-ui_draw_tabs(Tabs *tabs, int x, int y, int w, int h) {
+ui_draw_tabs(int x, int y, int w, int h, Tabs *tabs) {
int fw, fn, ftabw;
int tabw;
int padx, pady;
int cx, selx = -1;
int i;
+ DrawRectangle(x, y, w, h, COL_BG);
+
for (fw = w, fn = i = 0; i < tabs->n; i++) {
if (!tabs->tabs[i].w)
fn++;
@@ -193,18 +289,228 @@ ui_draw_tabs(Tabs *tabs, int x, int y, int w, int h) {
}
void
-ui_draw_tabbed_window(Tabs *tabs, int x, int y, int w, int h) {
- ui_draw_tabs(tabs, x, y, w, WINDOW_TAB_HEIGHT);
+ui_draw_checkbox(int x, int y, Checkbox *box) {
+ int w, h;
+
+ w = h = FONT_SIZE;
+ ui_draw_border(x, y, w, h, 1);
+ DrawRectangle(x + 1, y + 1, w - 2, h - 2, box->val ? COL_FG : COL_BG);
+ ui_print(x + w + (w / 2), y + (h / 6), COL_FG, "%s", box->label);
+ ui_clickable_register(x, y, w + (w / 2) + ui_textsize(box->label), h, UI_CHECKBOX, box);
+}
+
+Vector2
+ui_kmtopx(Vector2 km) {
+ return (Vector2) {
+ (GetScreenWidth() / 2) + (km.x - view_main.kmx) / view_main.kmperpx,
+ (GetScreenHeight() / 2) + (km.y - view_main.kmy) / view_main.kmperpx
+ };
+}
+
+Vector2
+ui_pxtokm(Vector2 vector) {
+ return (Vector2) {
+ ((vector.x - GetScreenWidth() / 2) * view_main.kmperpx) + view_main.kmx,
+ ((vector.y - GetScreenHeight() / 2) * view_main.kmperpx) + view_main.kmy
+ };
+}
+
+Vector2
+ui_vectordiff(Vector2 a, Vector2 b) {
+ float x = a.x - b.x;
+ float y = a.y - b.y;
+ if (x < 0)
+ x *= -1;
+ if (y < 0)
+ y *= -1;
+ return (Vector2) {x, y};
+}
+
+float
+ui_vectordist(Vector2 a, Vector2 b) {
+ Vector2 diff = ui_vectordiff(a, b);
+ return sqrtf(diff.x * diff.x + diff.y * diff.y);
+}
+
+void
+ui_draw_tabbed_window(int x, int y, int w, int h, Tabs *tabs) {
+ DrawRectangle(x, y, w, h, COL_BG);
+ ui_draw_tabs(x, y, w, WINDOW_TAB_HEIGHT, tabs);
ui_draw_border(x, y, w, h, 2);
}
void
+ui_handle_view_main(void) {
+ Vector2 mouse = GetMousePosition();
+ Vector2 delta = GetMouseDelta();
+ float wheel;
+
+#define SCROLL_DIVISOR 10
+ wheel = GetMouseWheelMove();
+ if (wheel < 0)
+ view_main.kmperpx += (GetMouseWheelMove() * -1) * (view_main.kmperpx/SCROLL_DIVISOR);
+ else if (wheel > 0)
+ view_main.kmperpx -= GetMouseWheelMove() * (view_main.kmperpx/SCROLL_DIVISOR);
+ if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
+ if (view_main.pan) {
+ view_main.kmx -= delta.x * view_main.kmperpx;
+ view_main.kmy -= delta.y * view_main.kmperpx;
+ } else if (!ui_collides(view_main.infobox.geom, mouse)) {
+ view_main.pan = 1;
+ }
+ } else view_main.pan = 0;
+
+ if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT) && !view_main.ruler.held &&
+ !ui_collides(view_main.infobox.geom, mouse)) {
+ view_main.ruler.held = 1;
+ view_main.ruler.origin = mouse;
+ } else if (IsMouseButtonUp(MOUSE_BUTTON_RIGHT))
+ view_main.ruler.held = 0;
+
+}
+
+void
+ui_handle_view_colonies(void) {
+
+}
+
+void
+ui_handle_view_fleets(void) {
+
+}
+
+void
+ui_handle_view_design(void) {
+
+}
+
+void
+ui_handle_view_systems(void) {
+
+}
+
+void
+ui_handle_view_settings(void) {
+
+}
+
+int
+ui_should_draw_body(Body *body, int orbit) {
+ if (!orbit && body->type != BODY_COMET && body->dist / view_main.kmperpx < ui_textsize(body->name))
+ return 0;
+ if ((body->type == BODY_DWARF || (body->parent && body->parent->type == BODY_DWARF)) &&
+ (!orbit || !view_main.infobox.orbit.dwarf.val) &&
+ (orbit || !view_main.infobox.names.dwarf.val))
+ return 0;
+ if ((body->type == BODY_ASTEROID || (body->parent && body->parent->type == BODY_ASTEROID)) &&
+ (!orbit || !view_main.infobox.orbit.asteroid.val) &&
+ (orbit || !view_main.infobox.names.asteroid.val))
+ return 0;
+ if ((body->type == BODY_COMET || (body->parent && body->parent->type == BODY_COMET)) &&
+ (!orbit || !view_main.infobox.orbit.comet.val) &&
+ (orbit || !view_main.infobox.names.comet.val))
+ return 0;
+ return 1;
+}
+
+void
+ui_draw_body(Body *body) {
+ Vector2 parent;
+ Vector2 location;
+ int w;
+
+ if (body->parent) {
+ parent = ui_kmtopx(body->parent->vector);
+ } else {
+ parent.x = 0;
+ parent.y = 0;
+ }
+
+ location = ui_kmtopx(body->vector);
+
+ if (ui_should_draw_body(body, 1)) {
+ if (body->parent && body->type == BODY_COMET)
+ DrawLineV(parent, location, COL_ORBIT);
+ else if (body->parent)
+ DrawCircleLines(parent.x, parent.y, ui_vectordist(parent, location), COL_ORBIT);
+ }
+ if (body->radius / view_main.kmperpx > MIN_BODY_DIAM)
+ w = body->radius / view_main.kmperpx;
+ else
+ w = MIN_BODY_DIAM;
+ DrawCircle(location.x, location.y, MIN_BODY_DIAM, COL_FG);
+ if (ui_should_draw_body(body, 0))
+ ui_print(location.x + 5, location.y + 5, COL_FG, "%s", body->name);
+}
+
+void
ui_draw_view_main(void) {
- static Tabs window_tabs = {
- 2, 0, {{"Display", 0}, {"Minerals", 0}}
- };
- ui_draw_tabbed_window(&window_tabs, 10, VIEWS_HEIGHT + 10, 200, 400);
- ui_print(GetScreenWidth() / 2, GetScreenHeight() / 2, COL_FG, "Pannable body view here");
+ Vector2 mouse = GetMousePosition();
+ Vector2 mousekm = ui_pxtokm(mouse);
+ Vector2 v[2];
+ Vector2 km;
+ Polar polar;
+ float dist;
+ size_t i;
+
+ /* debug info */
+ ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + 10, COL_FG, "Xoff: %f | Yoff: %f | km/px: %f",
+ view_main.kmx, view_main.kmy, view_main.kmperpx);
+ ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + 20, COL_FG, "X: %f | Y: %f",
+ mousekm.x, mousekm.y);
+ ui_print(GetScreenWidth() / 2, VIEWS_HEIGHT + 30, COL_FG, "FPS: %d (target: %d)", GetFPS(), TARGET_FPS);
+
+ /* system bodies */
+ if (!save->cache.system)
+ save->cache.system = system_load(NULL, "Sol");
+ for (i = 0; i < save->cache.system->bodies_len; i++)
+ ui_draw_body(save->cache.system->bodies[i]);
+
+ /* ruler */
+ if (view_main.ruler.held) {
+ DrawLineV(view_main.ruler.origin, mouse, COL_INFO);
+ km = ui_pxtokm(view_main.ruler.origin);
+ dist = ui_vectordist(km, mousekm);
+ ui_print(mouse.x + 10, mouse.y - 10, COL_INFO, "%s (%s)", strkmdist(dist), strlightdist(dist));
+ }
+
+ /* scale */
+ DrawRectangle(view_main.scale.x,
+ GetScreenHeight() - view_main.scale.y,
+ view_main.scale.w, 1, COL_INFO); /* horizontal */
+ DrawRectangle(view_main.scale.x,
+ GetScreenHeight() - view_main.scale.y - view_main.scale.h,
+ 1, view_main.scale.h, COL_INFO); /* left vertical */
+ DrawRectangle(view_main.scale.x + view_main.scale.w,
+ GetScreenHeight() - view_main.scale.y - view_main.scale.h,
+ 1, view_main.scale.h, COL_INFO); /* right vertical */
+ dist = view_main.scale.w * view_main.kmperpx;
+ ui_print(view_main.scale.x + view_main.scale.w + FONT_SIZE / 3,
+ GetScreenHeight() - view_main.scale.y - FONT_SIZE / 2,
+ COL_INFO, "%s", strkmdist(dist));
+
+ /* infobox */
+ ui_draw_tabbed_window(view_main.infobox.geom.x, view_main.infobox.geom.y,
+ view_main.infobox.geom.w, view_main.infobox.geom.h,
+ &view_main.infobox.tabs);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*1.5,
+ &view_main.infobox.names.dwarf);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*3,
+ &view_main.infobox.names.asteroid);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*4.5,
+ &view_main.infobox.names.comet);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*6,
+ &view_main.infobox.orbit.dwarf);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*7.5,
+ &view_main.infobox.orbit.asteroid);
+ ui_draw_checkbox(view_main.infobox.geom.x + FONT_SIZE,
+ view_main.infobox.geom.y + WINDOW_TAB_HEIGHT + FONT_SIZE*9,
+ &view_main.infobox.orbit.comet);
}
void