UI: Add pazz0's dungeon-crawler-like interface - sacc - sacc(omys), simple cons… | |
git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
LICENSE | |
--- | |
commit d16bb53db353da7b817b18809adf57b57c4f973e | |
parent 0fce134e1baa2ae47aeb0ee7c091e6c25651bcea | |
Author: Quentin Rameau <[email protected]> | |
Date: Thu, 28 Mar 2024 12:13:51 +0100 | |
UI: Add pazz0's dungeon-crawler-like interface | |
This definitely looks fun and interesting | |
Diffstat: | |
A ui_rogue.c | 938 +++++++++++++++++++++++++++++… | |
1 file changed, 938 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/ui_rogue.c b/ui_rogue.c | |
@@ -0,0 +1,938 @@ | |
+#include <stdarg.h> | |
+#include <stdint.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <term.h> | |
+#include <termios.h> | |
+#include <unistd.h> | |
+#include <sys/types.h> | |
+ | |
+#include "common.h" | |
+#include "config.h" | |
+ | |
+#define C(c) #c | |
+#define S(c) C(c) | |
+ | |
+/* ncurses doesn't define those in term.h, where they're used */ | |
+#ifndef OK | |
+#define OK (0) | |
+#endif | |
+#ifndef ERR | |
+#define ERR (-1) | |
+#endif | |
+ | |
+static struct termios tsave; | |
+static struct termios tsacc; | |
+static Item *curentry; | |
+static int termset = ERR; | |
+static char bufout[256]; | |
+static char bufout2[256]; | |
+ | |
+void drawscreen(void); | |
+ | |
+uint32_t | |
+fnv1a(int n,...) | |
+{ | |
+ int i; | |
+ char *s; | |
+ va_list l; | |
+ uint32_t h; | |
+ | |
+ h = 0x811c9dc5; | |
+ | |
+ va_start(l, n); | |
+ for (i = 0; i < n; i++) { | |
+ for (s = va_arg(l, char*); *s; s++) { | |
+ h ^= *s; | |
+ h *= 0x01000193; | |
+ } | |
+ } | |
+ va_end(l); | |
+ | |
+ return h; | |
+} | |
+ | |
+uint32_t | |
+xorshift(uint32_t *s) | |
+{ | |
+ *s ^= *s << 13; | |
+ *s ^= *s >> 17; | |
+ *s ^= *s << 5; | |
+ return *s; | |
+} | |
+ | |
+struct cell { | |
+ char c; | |
+ size_t nitems; | |
+ Item **items; | |
+}; | |
+ | |
+#define MAPHEIGHT (25) | |
+#define MAPWIDTH (80) | |
+struct cell map[MAPHEIGHT][MAPWIDTH]; | |
+ | |
+struct room { | |
+ struct room *p; | |
+ void *d; | |
+ size_t x, y; | |
+ size_t w, h; | |
+}; | |
+ | |
+struct rect { | |
+ struct rect *next, *next2; | |
+ struct room *room; | |
+ size_t x1, y1; | |
+ size_t x2, y2; | |
+ size_t d; | |
+}; | |
+ | |
+struct rect * | |
+randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng) | |
+{ | |
+ struct rect *r, *result; | |
+ size_t n; | |
+ | |
+ n = 0; | |
+ result = NULL; | |
+ for (r = rs; r; r = r->next) { | |
+ if (r->y2 < x->y1 || r->y1 > x->y2 || r->x2 < x->x1 || r->x1 >… | |
+ continue; | |
+ if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r… | |
+ continue; | |
+ n++; | |
+ if (xorshift(prng) / (1. + UINT32_MAX) < 1. / n) | |
+ result = r; | |
+ } | |
+ | |
+ return result; | |
+} | |
+ | |
+#define ROOM_HEIGHT_MIN 3 | |
+#define ROOM_WIDTH_MIN 5 | |
+#define ROOM_MARGIN_MIN 1 | |
+#define CELL_HEIGHT_MIN (ROOM_HEIGHT_MIN + ROOM_MARGIN_MIN + 3) | |
+#define CELL_WIDTH_MIN (ROOM_WIDTH_MIN + ROOM_MARGIN_MIN + 3) | |
+size_t | |
+generaterooms_gnarf(uint32_t prng, struct room *rs, size_t l) | |
+{ | |
+ struct rect *queuehead, *queuetail; | |
+ struct rect *r, *t; | |
+ struct rect *rects, *walk; | |
+ size_t w, h, i, j, rl, n; | |
+ int vertical; | |
+ struct room *room; | |
+ | |
+ r = malloc(sizeof(*r)); | |
+ r->x1 = r->y1 = ROOM_MARGIN_MIN; | |
+ r->x2 = MAPWIDTH; | |
+ r->y2 = MAPHEIGHT; | |
+ r->d = 0; | |
+ | |
+ queuetail = r; | |
+ queuetail->next = NULL; | |
+ queuehead = r; | |
+ | |
+ rects = NULL; | |
+ rl = 0; | |
+ | |
+ while (queuehead) { | |
+ r = queuehead; | |
+ if (queuetail == queuehead) | |
+ queuetail = NULL; | |
+ queuehead = queuehead->next; | |
+ | |
+ if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2 && r->y2 - r->y1 >= CE… | |
+ vertical = xorshift(&prng) & 1; | |
+ } else if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2) { | |
+ vertical = 0; | |
+ } else if (r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) { | |
+ vertical = 1; | |
+ } else { | |
+ r->next = rects; | |
+ rects = r; | |
+ rl++; | |
+ continue; | |
+ } | |
+ | |
+ if (vertical) { | |
+ w = r->x2 - r->x1; | |
+ h = CELL_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r… | |
+ } else { | |
+ w = CELL_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r-… | |
+ h = r->y2 - r->y1; | |
+ } | |
+ | |
+ t = malloc(sizeof(*t)); | |
+ t->x1 = r->x1; | |
+ t->y1 = r->y1; | |
+ t->x2 = r->x1 + w; | |
+ t->y2 = r->y1 + h; | |
+ t->d = r->d + 1; | |
+ t->next = NULL; | |
+ t->room = NULL; | |
+ | |
+ if (!queuetail) { | |
+ queuehead = t; | |
+ queuetail = t; | |
+ } else { | |
+ queuetail->next = t; | |
+ queuetail = t; | |
+ } | |
+ | |
+ t = malloc(sizeof(*t)); | |
+ if (vertical) { | |
+ t->x1 = r->x1; | |
+ t->y1 = r->y1 + h; | |
+ } else { | |
+ t->x1 = r->x1 + w; | |
+ t->y1 = r->y1; | |
+ } | |
+ t->x2 = r->x2; | |
+ t->y2 = r->y2; | |
+ t->d = r->d + 1; | |
+ t->next = NULL; | |
+ t->room = NULL; | |
+ | |
+ queuetail->next = t; | |
+ queuetail = t; | |
+ | |
+ free(r); | |
+ } | |
+ | |
+ if (l > rl) | |
+ l = rl; | |
+ | |
+ for (r = rects; r; r = r->next) { | |
+ if (MAPHEIGHT / 2 >= r->y1 && MAPHEIGHT / 2 < r->y2 && | |
+ MAPWIDTH / 2 >= r->x1 && MAPWIDTH / 2 < r->x2) | |
+ break; | |
+ } | |
+ | |
+ i = 0; | |
+ rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM… | |
+ rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - ROO… | |
+ rs[i].x = r->x1 + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_M… | |
+ rs[i].y = r->y1 + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_M… | |
+ rs[i].p = NULL; | |
+ r->room = &rs[i]; | |
+ | |
+ walk = r; | |
+ walk->next2 = NULL; | |
+ | |
+ i++; | |
+ for (; i < l;) { | |
+ t = randomneighbor(r, rects, &prng); | |
+ if (!t || t->room) { | |
+ n = 0; | |
+ for (t = walk; t; t = t->next2) { | |
+ n++; | |
+ if (xorshift(&prng) / (1. + UINT32_MAX) < 1. /… | |
+ r = t; | |
+ | |
+ } | |
+ continue; | |
+ } | |
+ rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + t->x2 - t->x… | |
+ rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + t->y2 - t->… | |
+ rs[i].x = t->x1 + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_… | |
+ rs[i].y = t->y1 + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_… | |
+ rs[i].p = r->room; | |
+ t->room = &rs[i]; | |
+ i++; | |
+ r = t; | |
+ r->next2 = walk; | |
+ walk = r; | |
+ } | |
+ | |
+ for (r = rects; r;) { | |
+ t = r->next; | |
+ free(r); | |
+ r = t; | |
+ } | |
+ | |
+ return l; | |
+} | |
+ | |
+size_t | |
+distance(size_t x1, size_t y1, size_t x2, size_t y2) | |
+{ | |
+ size_t d; | |
+ | |
+ if (y1 < y2) | |
+ d = y2 - y1; | |
+ else | |
+ d = y1 - y2; | |
+ if (x1 < x2) | |
+ d += x2 - x1; | |
+ else | |
+ d += x1 - x2; | |
+ | |
+ return d; | |
+} | |
+ | |
+void | |
+nearestpoints(struct room *a, struct room *b, size_t *ax, size_t *ay, size_t *… | |
+{ | |
+ if (a->y >= b->y && a->y < b->y + b->h) { | |
+ *ay = *by = a->y; | |
+ } else if (b->y >= a->y && b->y < a->y + a->h) { | |
+ *ay = *by = b->y; | |
+ } else if (a->y >= b->y) { | |
+ *ay = a->y; | |
+ *by = b->y + b->h - 1; | |
+ } else if (b->y >= a->y) { | |
+ *ay = a->y + a->h - 1; | |
+ *by = b->y; | |
+ } | |
+ | |
+ if (a->x >= b->x && a->x < b->x + b->w) { | |
+ *ax = *bx = a->x; | |
+ } else if (b->x >= a->x && b->x < a->x + a->w) { | |
+ *ax = *bx = b->x; | |
+ } else if (a->x >= b->x) { | |
+ *ax = a->x; | |
+ *bx = b->x + b->w - 1; | |
+ } else if (b->x >= a->x) { | |
+ *ax = a->x + a->w - 1; | |
+ *bx = b->x; | |
+ } | |
+} | |
+ | |
+void | |
+connectrooms(struct room *a, struct room *b) | |
+{ | |
+ size_t i, j; | |
+ ssize_t ii; | |
+ size_t x1, y1; | |
+ size_t x2, y2; | |
+ | |
+ nearestpoints(a, b, &x1, &y1, &x2, &y2); | |
+ | |
+ if (y1 > y2) { | |
+ ii = -1; | |
+ } else if (y2 > y1) { | |
+ ii = 1; | |
+ } else { | |
+ ii = 0; | |
+ } | |
+ | |
+/* | |
+printf("%lu\t%lu\t%d\n", y1, y2, ii); | |
+*/ | |
+ for (i = y1; i != y2; i += ii) | |
+ map[i][x1].c = '.'; | |
+ | |
+ if (x1 > x2) { | |
+ ii = -1; | |
+ } else if (x2 > x1) { | |
+ ii = 1; | |
+ } else { | |
+ ii = 0; | |
+ } | |
+ | |
+ for (i = x1; i != x2; i += ii) | |
+ map[y2][i].c = '.'; | |
+} | |
+ | |
+void | |
+rendermap(void) | |
+{ | |
+ size_t i, j; | |
+ | |
+ for (i = 0; i < MAPHEIGHT; i++) { | |
+ for (j = 0; j < MAPWIDTH; j++) | |
+ putchar(map[i][j].c); | |
+ putchar('\n'); | |
+ } | |
+} | |
+ | |
+size_t | |
+placeitems_hash(Item *item, size_t *assocs, size_t k) | |
+{ | |
+ Dir *dir; | |
+ Item *citem; | |
+ size_t i; | |
+ | |
+ dir = item->dat; | |
+ for (i = 0; i < dir->nitems; i++) { | |
+ citem = &dir->items[i]; | |
+ /* TODO Somewhere else */ | |
+ if (!citem->host || !citem->port || !citem->selector) | |
+ continue; | |
+ assocs[i] = fnv1a(6, item->host, item->port, item->selector, c… | |
+ } | |
+ | |
+ return k; | |
+} | |
+ | |
+#define POSITIONS_LENGTH 4 | |
+enum { | |
+ Portal, | |
+ StaircaseDown, | |
+ Bookshelf, | |
+ Back | |
+}; | |
+ | |
+size_t px, py; | |
+ | |
+void | |
+generatemap(Item *item, int new) | |
+{ | |
+ Dir *dir; | |
+ Item *citem; | |
+ size_t i, j, k, l, ir; | |
+ size_t x, y; | |
+ ssize_t n, m; | |
+ size_t *cassocs; | |
+ struct room *rooms, *r; | |
+ struct { | |
+ unsigned char x, y; | |
+ } positions[POSITIONS_LENGTH]; | |
+ uint32_t prng; | |
+ char buffer[3]; | |
+ | |
+ for (i = 0; i < MAPHEIGHT; i++) { | |
+ for (j = 0; j < MAPWIDTH; j++) { | |
+ map[i][j].c = '#'; | |
+ free(map[i][j].items); | |
+ map[i][j].items = NULL; | |
+ map[i][j].nitems = 0; | |
+ } | |
+ } | |
+ | |
+ dir = item->dat; | |
+ for (j = l = 0; j < dir->nitems; j++) { | |
+ if (dir->items[j].type == '0' || | |
+ dir->items[j].type == '1') | |
+ l++; | |
+ } | |
+ | |
+ k = 1 + l / 10; | |
+ rooms = calloc(k, sizeof(*rooms)); | |
+ if (!rooms) | |
+ return; | |
+ k = generaterooms_gnarf(fnv1a(3, item->host, item->port, item->selecto… | |
+ | |
+ cassocs = calloc(dir->nitems, sizeof(*cassocs)); | |
+ if (!cassocs) | |
+ goto cleanup; | |
+ | |
+ k = placeitems_hash(item, cassocs, k); | |
+ | |
+ /* Insert rooms */ | |
+ for (i = 0; i < k; i++) { | |
+ for (y = rooms[i].y; y < rooms[i].y + rooms[i].h; y++) { | |
+ for (x = rooms[i].x; x < rooms[i].x + rooms[i].w; x++) | |
+ map[y][x].c = '.'; | |
+ } | |
+ } | |
+ | |
+ /* Insert connections */ | |
+ for (i = 0; i < k; i++) { | |
+ if (rooms[i].p) | |
+ connectrooms(&rooms[i], rooms[i].p); | |
+ } | |
+ | |
+ /* | |
+ Insert items | |
+ The placement of items affects the initial placement of the pl… | |
+ */ | |
+ ir = fnv1a(4, item->host, item->port, item->selector, "initial_room") … | |
+ | |
+ for (i = 0; i < k; i++) { | |
+ snprintf(buffer, sizeof(buffer), "%d", i); | |
+ prng = fnv1a(4, item->host, item->port, item->selector, buffer… | |
+ for (j = 0, n = 0, m = rooms[i].h * rooms[i].w; j < m; j++) { | |
+ if ((m - j) * (xorshift(&prng) / (double)UINT32_MAX) <… | |
+ positions[n].x = rooms[i].x + j % rooms[i].w; | |
+ positions[n].y = rooms[i].y + j / rooms[i].w; | |
+ n++; | |
+ } | |
+ if (n == POSITIONS_LENGTH) | |
+ break; | |
+ } | |
+ for (j = 0; j < dir->nitems; j++) { | |
+ if (cassocs[j] != i) | |
+ continue; | |
+ | |
+ citem = &dir->items[j]; | |
+ switch (citem->type) { | |
+ case '0': | |
+ x = positions[Bookshelf].x; | |
+ y = positions[Bookshelf].y; | |
+ if (map[y][x].nitems) | |
+ map[y][x].c = 'E'; | |
+ else | |
+ map[y][x].c = '?'; | |
+ break; | |
+ case '1': | |
+ if (strcmp(citem->host, item->host) || strcmp(… | |
+ x = positions[Portal].x; | |
+ y = positions[Portal].y; | |
+ if (map[y][x].nitems) | |
+ map[y][x].c = 'O'; | |
+ else | |
+ map[y][x].c = '0'; | |
+ } else { | |
+ x = positions[StaircaseDown].x; | |
+ y = positions[StaircaseDown].y; | |
+ if (map[y][x].nitems) | |
+ map[y][x].c = 'L'; | |
+ else | |
+ map[y][x].c = '>'; | |
+ } | |
+ break; | |
+ default: | |
+ continue; | |
+ } | |
+ map[y][x].nitems++; | |
+ map[y][x].items = realloc(map[y][x].items, map[y][x].n… | |
+ map[y][x].items[map[y][x].nitems-1] = citem; | |
+ | |
+ if (new && j == dir->curline && citem->raw) { | |
+ px = x; | |
+ py = y; | |
+ } | |
+ } | |
+ | |
+ if (i == ir && item->entry != item) { | |
+ y = positions[Back].y; | |
+ x = positions[Back].x; | |
+ if (strcmp(item->entry->host, item->host) || strcmp(it… | |
+ map[y][x].c = '0'; | |
+ else | |
+ map[y][x].c = '<'; | |
+ map[y][x].nitems++; | |
+ map[y][x].items = realloc(map[y][x].items, map[y][x].n… | |
+ map[y][x].items[map[y][x].nitems-1] = item->entry; | |
+ } | |
+ | |
+ if (i == ir && new && !dir->items[dir->curline].raw) { | |
+ px = positions[Back].x; | |
+ py = positions[Back].y; | |
+ } | |
+ } | |
+ free(cassocs); | |
+ | |
+cleanup: | |
+ free(rooms); | |
+} | |
+ | |
+void | |
+uisetup(void) | |
+{ | |
+ tcgetattr(0, &tsave); | |
+ tsacc = tsave; | |
+ tsacc.c_lflag &= ~(ECHO|ICANON); | |
+ tsacc.c_cc[VMIN] = 1; | |
+ tsacc.c_cc[VTIME] = 0; | |
+ tcsetattr(0, TCSANOW, &tsacc); | |
+ | |
+ if (termset != OK) | |
+ /* setupterm call exits on error */ | |
+ termset = setupterm(NULL, 1, NULL); | |
+ putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+} | |
+ | |
+void | |
+uicleanup(void) | |
+{ | |
+ tcsetattr(0, TCSANOW, &tsave); | |
+ | |
+ if (termset != OK) | |
+ return; | |
+ | |
+ putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+} | |
+ | |
+char * | |
+uiprompt(char *fmt, ...) | |
+{ | |
+ va_list ap; | |
+ char *input = NULL; | |
+ size_t n; | |
+ ssize_t r; | |
+ | |
+ putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ va_start(ap, fmt); | |
+ vsnprintf(bufout, sizeof(bufout), fmt, ap); | |
+ va_end(ap); | |
+ | |
+ n = mbsprint(bufout, columns); | |
+ | |
+ putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ tsacc.c_lflag |= (ECHO|ICANON); | |
+ tcsetattr(0, TCSANOW, &tsacc); | |
+ fflush(stdout); | |
+ | |
+ n = 0; | |
+ r = getline(&input, &n, stdin); | |
+ | |
+ tsacc.c_lflag &= ~(ECHO|ICANON); | |
+ tcsetattr(0, TCSANOW, &tsacc); | |
+ putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+ | |
+ if (r == -1) { | |
+ clearerr(stdin); | |
+ clear(&input); | |
+ } else if (input[r - 1] == '\n') { | |
+ input[--r] = '\0'; | |
+ } | |
+ | |
+ return input; | |
+} | |
+ | |
+void | |
+displaybar(char *s) { | |
+ size_t n; | |
+ | |
+ putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(cursor_address, lines-2, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ n = mbsprint(s, columns); | |
+ for (n = columns - n; n; n--) | |
+ putchar(' '); | |
+ | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+} | |
+ | |
+void | |
+vdisplayinfoline(char *fmt, va_list ap) | |
+{ | |
+ putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ vsnprintf(bufout, sizeof(bufout), fmt, ap); | |
+ | |
+ mbsprint(bufout, columns); | |
+ | |
+ putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+} | |
+ | |
+void | |
+uistatus(char *fmt, ...) | |
+{ | |
+ va_list ap; | |
+ size_t n; | |
+ | |
+ putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ va_start(ap, fmt); | |
+ n = vsnprintf(bufout, sizeof(bufout), fmt, ap); | |
+ va_end(ap); | |
+ | |
+ if (n < sizeof(bufout)-1) { | |
+ snprintf(bufout+n, sizeof(bufout)-n, | |
+ " [Press a key to continue \xe2\x98\x83]"); | |
+ } | |
+ | |
+ mbsprint(bufout, columns); | |
+ | |
+ putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ fflush(stdout); | |
+ | |
+ getchar(); | |
+} | |
+ | |
+void | |
+displayinfoline(char *fmt, ...) | |
+{ | |
+ va_list ap; | |
+ | |
+ va_start(ap, fmt); | |
+ vdisplayinfoline(fmt, ap); | |
+ va_end(ap); | |
+} | |
+ | |
+Item * | |
+showmenu(char *title, Item **item, size_t l) | |
+{ | |
+ size_t i; | |
+ | |
+ putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ printf("%s\n", title); | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ for (i = 0; i < l; i++) | |
+ printf("%lu\t%s\n", i, item[i]->username); | |
+ | |
+ if (!scanf("%lu", &i) || i >= l) | |
+ return NULL; | |
+ fflush(stdout); | |
+ | |
+ return item[i]; | |
+} | |
+ | |
+Item * | |
+prompt(char *text, Item *item) | |
+{ | |
+ displayinfoline(text, item->username); | |
+ getchar(); | |
+ return item; | |
+} | |
+ | |
+Item * | |
+interact(Item *item) | |
+{ | |
+ Item *selection; | |
+ | |
+ selection = NULL; | |
+ | |
+ switch (map[py][px].c) { | |
+ case '?': | |
+ case '0': | |
+ case '>': | |
+ case '<': | |
+ selection = map[py][px].items[0]; | |
+ break; | |
+ case 'E': | |
+ selection = showmenu("Bookshelf", map[py][px].items, map[py][p… | |
+ break; | |
+ case 'O': | |
+ selection = showmenu("Portal machine", map[py][px].items, map[… | |
+ break; | |
+ case 'L': | |
+ selection = showmenu("Elevator", map[py][px].items, map[py][px… | |
+ break; | |
+ } | |
+ | |
+ drawscreen(); | |
+ | |
+ if (selection) { | |
+ switch (map[py][px].c) { | |
+ case '?': | |
+ case 'E': | |
+ displayinfoline("A loading bar?! In a book?!"); | |
+ break; | |
+ case 'O': | |
+ case '0': | |
+ displayinfoline("You are getting transported through t… | |
+ break; | |
+ case 'L': | |
+ displayinfoline("You hear elevator music..."); | |
+ break; | |
+ case '<': | |
+ case '>': | |
+ displayinfoline("Too many stairs..."); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return selection; | |
+} | |
+ | |
+void | |
+describe(size_t x, size_t y, int verbose) | |
+{ | |
+ switch (map[y][x].c) { | |
+ case 'E': | |
+ displayinfoline("A bookshelf."); | |
+ break; | |
+ case 'O': | |
+ displayinfoline("A portal machine."); | |
+ break; | |
+ case 'L': | |
+ displayinfoline("An elevator."); | |
+ break; | |
+ case '?': | |
+ case '>': | |
+ case '<': | |
+ case '0': | |
+ if (*map[y][x].items[0]->username) { | |
+ displayinfoline("'%s'.", map[y][x].items[0]->username); | |
+ } else { | |
+ itemuri(map[y][x].items[0], bufout2, sizeof(bufout2)); | |
+ displayinfoline("'%s'.", bufout2); | |
+ } | |
+ break; | |
+ default: | |
+ if (verbose) { | |
+ switch (map[y][x].c) { | |
+ case '.': | |
+ displayinfoline("Floor."); | |
+ break; | |
+ case '#': | |
+ displayinfoline("Wall."); | |
+ break; | |
+ } | |
+ } else { | |
+ displayinfoline(""); | |
+ } | |
+ break; | |
+ } | |
+} | |
+ | |
+void | |
+move(ssize_t dx, ssize_t dy) | |
+{ | |
+ size_t x, y; | |
+ | |
+ /* allow wraparound of the world for the lulz, even if it's not happen… | |
+ y = (MAPHEIGHT + py + dy) % MAPHEIGHT; | |
+ x = (MAPWIDTH + px + dx) % MAPWIDTH; | |
+ | |
+ if (map[y][x].c == '#') | |
+ return; | |
+ | |
+ putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ putchar(map[py][px].c); | |
+ | |
+ py = y; | |
+ px = x; | |
+ putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ putchar('@'); | |
+ putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ | |
+ describe(x, y, 0); | |
+ putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+} | |
+ | |
+void | |
+drawscreen(void) | |
+{ | |
+ Dir *dir; | |
+ | |
+ putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ rendermap(); | |
+ | |
+ if (curentry->entry != curentry && (dir = curentry->entry->dat)) { | |
+ displaybar(dir->items[dir->curline].username); | |
+ } else { | |
+ itemuri(curentry, bufout, sizeof(bufout)); | |
+ displaybar(bufout); | |
+ } | |
+ | |
+ move(0, 0); | |
+ | |
+} | |
+ | |
+void | |
+uidisplay(Item *entry) | |
+{ | |
+ if (!entry || entry->type != '1') | |
+ return; | |
+ | |
+ generatemap(entry, curentry != entry); | |
+ | |
+ curentry = entry; | |
+ drawscreen(); | |
+} | |
+ | |
+void | |
+lookmode(void) | |
+{ | |
+ size_t x, y; | |
+ | |
+ x = px; | |
+ y = py; | |
+ | |
+ for (;;) { | |
+ switch (getchar()) { | |
+ case 0x1B: | |
+ case 'q': | |
+ putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0… | |
+ return; | |
+ case 'h': | |
+ x = (MAPWIDTH + x - 1) % MAPWIDTH; | |
+ break; | |
+ case 'j': | |
+ y = (y + 1) % MAPHEIGHT; | |
+ break; | |
+ case 'k': | |
+ y = (MAPHEIGHT + y - 1) % MAPHEIGHT; | |
+ break; | |
+ case 'l': | |
+ x = (x + 1) % MAPWIDTH; | |
+ break; | |
+ } | |
+ putp(tparm(cursor_address, y, x, 0, 0, 0, 0, 0, 0, 0)); | |
+ describe(x, y, 1); | |
+ } | |
+} | |
+ | |
+Item * | |
+uiselectitem(Item *entry) | |
+{ | |
+ Dir *dir; | |
+ Item *e; | |
+ size_t i; | |
+ | |
+ if (!entry || !(dir = entry->dat)) | |
+ return NULL; | |
+ | |
+ for (;;) { | |
+ switch (getchar()) { | |
+ case 'h': | |
+ move(-1, 0); | |
+ break; | |
+ case 'j': | |
+ move(0, 1); | |
+ break; | |
+ case 'k': | |
+ move(0, -1); | |
+ break; | |
+ case 'l': | |
+ move(1, 0); | |
+ break; | |
+ case 'L': | |
+ lookmode(); | |
+ break; | |
+ case ' ': | |
+ /* Portals, stairs, bookshelfs */ | |
+ if (e = interact(entry)) { | |
+ if (e->type == '1') { | |
+ for (i = 0; i < dir->nitems; i++) { | |
+ if (e == &dir->items[i]) { | |
+ dir->curline = i; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ return e; | |
+ } | |
+ break; | |
+ case 'q': | |
+ return NULL; | |
+ } | |
+ } | |
+} | |
+ | |
+void | |
+uisigwinch(int signal) | |
+{ | |
+ Dir *dir; | |
+ | |
+ if (termset == OK) | |
+ del_curterm(cur_term); | |
+ termset = setupterm(NULL, 1, NULL); | |
+ | |
+ if (!curentry || !(dir = curentry->dat)) | |
+ return; | |
+ | |
+ uidisplay(curentry); | |
+} |