cepheid

An Aurora 4X clone
Log | Files | Refs | README

commit 963ea7493c3dcd6f5ac9ab18c9b913dd62e127aa
parent 0deed8c756e9aa4647ab36218b24c1fbc4497953
Author: hhvn <dev@hhvn.uk>
Date:   Thu,  9 Jun 2022 23:06:20 +0100

Draw system view

Diffstat:
M.gitignore | 1+
MMakefile | 10++++++----
Mdata/worlds-parse.awk | 5+++--
Mdb/db.c | 4++--
Msrc/main.c | 4+++-
Msrc/main.h | 37++++++++++++++++++++++++++++---------
Asrc/maths.h | 18++++++++++++++++++
Msrc/save.c | 1+
Msrc/str.c | 80++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/struct.h | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/system.c | 163++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/ui.c | 352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
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