Update ui_rogue - sacc - sacc(omys), simple console gopher client | |
git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
LICENSE | |
--- | |
commit 90a9d3c81f27588c62faa43f22d946740042a3ce | |
parent cc6a6881e06ab4b851c7778842374b953e51539c | |
Author: Julian Schweinsberg <[email protected]> | |
Date: Mon, 28 Oct 2024 15:17:33 +0100 | |
Update ui_rogue | |
ui_rogue: Overengineered Edition | |
This adds a readme and notes (mostly ideas) file for ui_rogue, too. | |
These changes were made half a year ago and I don't do real | |
documentation of my changes... | |
(There is some git branch, but the commit messages aren't helpful: | |
"HuH?!", "Well, better than nothing...", "Typical | |
pazz0-overengineering.", ...) | |
Changes: | |
- Better menus | |
- Bigger maps (160x50) | |
- Moving scrolls the displayed map if needed | |
- Rooms and corridors use ASCII characters to look like hack | |
- Removes look mode for now | |
- Replaces xorshift PRNG algorithm with ranqd1 (this means that the | |
layout of gopherholes change with this update) | |
- Moved setupterm call out of signal handler (IIRC [you remember: | |
months] there were some crashes I had in combination with heap | |
allocations, my thought was that setupterm is using heap allocations) | |
- Parameters for room generation are randomly selected (based on the | |
hostname and port) from a list of "dungeontypes" | |
Diffstat: | |
M ui_rogue.c | 1225 +++++++++++++++++++++--------… | |
A ui_rogue_notes | 31 +++++++++++++++++++++++++++++… | |
A ui_rogue_readme | 28 ++++++++++++++++++++++++++++ | |
3 files changed, 912 insertions(+), 372 deletions(-) | |
--- | |
diff --git a/ui_rogue.c b/ui_rogue.c | |
@@ -1,3 +1,5 @@ | |
+#include <errno.h> | |
+#include <signal.h> | |
#include <stdarg.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
@@ -6,6 +8,7 @@ | |
#include <term.h> | |
#include <termios.h> | |
#include <unistd.h> | |
+#include <sys/select.h> | |
#include <sys/types.h> | |
#include "common.h" | |
@@ -22,6 +25,47 @@ | |
#define ERR (-1) | |
#endif | |
+#define maplines (lines - 2) | |
+ | |
+enum { | |
+ Blocks = 1, | |
+ Standout = 2, | |
+ Important = 4 | |
+}; | |
+ | |
+struct cell; | |
+struct tile { | |
+ char c; | |
+ char flags; | |
+ char *name; | |
+ char *description; | |
+ char *afterinteract; | |
+ Item *(*interact)(struct cell *); | |
+}; | |
+ | |
+struct cell { | |
+ struct tile *tile; | |
+ size_t nitems; | |
+ Item **items; | |
+}; | |
+ | |
+struct room { | |
+ size_t x, y; | |
+ size_t w, h; | |
+}; | |
+ | |
+struct rect { | |
+ struct rect *next, *next2; | |
+ struct rect *p; | |
+ size_t x1, y1; | |
+ size_t x2, y2; | |
+ size_t d; | |
+ union { | |
+ void *p; | |
+ int i; | |
+ } data; | |
+}; | |
+ | |
static struct termios tsave; | |
static struct termios tsacc; | |
static Item *curentry; | |
@@ -29,8 +73,88 @@ static int termset = ERR; | |
static char bufout[256]; | |
static char bufout2[256]; | |
+size_t ox, oy; | |
+size_t px, py; | |
+ | |
+#define MAPHEIGHT (50) | |
+#define MAPWIDTH (160) | |
+struct cell map[MAPHEIGHT][MAPWIDTH]; | |
+ | |
+enum { | |
+ DungeonScreen, | |
+ MenuScreen | |
+} screen; | |
+ | |
+Item *interactitem(struct cell *); | |
+Item *interactmenu(struct cell *); | |
+ | |
+struct tile tile_void = { ' ', Blocks, "Void", "The void. The thing which is e… | |
+struct tile tile_floor = { '.', 0, "Floor", "An ordinary stone floor.", NULL, … | |
+struct tile tile_corridor = { '#', 0, "Different Floor", "This floor looks dif… | |
+struct tile tile_verticalwall = { '|', Blocks, "Wall", "Wall.", NULL, NULL }; | |
+struct tile tile_horizontalwall = { '-', Blocks, "Wall", "Wall.", NULL, NULL }; | |
+struct tile tile_door = { '/', 0, "Door", "A door.", NULL, NULL }; | |
+struct tile tile_bookshelf = { 'E', Important, "Bookshelf", "A bookshelf.", "A… | |
+struct tile tile_book = { '?', Important, "%s", "A book: '%s'.", "A loading ba… | |
+struct tile tile_portal = { '0', Important, "%s", "A portal: '%s'.", "You are … | |
+struct tile tile_portalmachine = { 'O', Important, "Portal Machine", "A portal… | |
+struct tile tile_heapofstuff = { '%', Important, "Heap", "A heap of stuff.", "… | |
+struct tile tile_elevator = { 'L', Important, "Elevator", "An elevator.", "You… | |
+struct tile tile_stairsdown = { '>', Important, "'%s'", "A staircase leading d… | |
+ | |
+struct tile tile_stairsup = { '<', Standout | Important, "'%s'", "A staircase … | |
+struct tile tile_backportal = { '0', Standout | Important, "'%s'", "A portal l… | |
+ | |
void drawscreen(void); | |
+int | |
+mygetchar_(void) | |
+{ | |
+ int r; | |
+ fd_set fdset; | |
+ | |
+ FD_ZERO(&fdset); | |
+ FD_SET(0, &fdset); | |
+ | |
+ if ((r = select(1, &fdset, NULL, NULL, NULL)) == -1) { | |
+ if (errno == EINTR) | |
+ return -1; | |
+ return -2; | |
+ } | |
+ | |
+ return getchar(); | |
+} | |
+ | |
+volatile sig_atomic_t sigwinch; | |
+ | |
+int | |
+mygetchar(void) | |
+{ | |
+ int r; | |
+ | |
+ while ((r = mygetchar_()) == -1) { | |
+ if (sigwinch) { | |
+ sigwinch = 0; | |
+ | |
+ if (termset == OK) | |
+ del_curterm(cur_term); | |
+ termset = setupterm(NULL, 1, NULL); | |
+ | |
+ drawscreen(); | |
+ } | |
+ } | |
+ | |
+ if (r == -2) | |
+ die("mygetchar: %s", strerror(errno)); | |
+ | |
+ return r; | |
+} | |
+ | |
+/* | |
+ FNV-1a ( http://www.isthe.com/chongo/tech/comp/fnv/ ) | |
+ FNV was published into the public domain ( https://creativecommons.org… | |
+ by Landon Curt Noll: http://www.isthe.com/chongo/tech/comp/fnv/#public… | |
+*/ | |
uint32_t | |
fnv1a(int n,...) | |
{ | |
@@ -41,7 +165,7 @@ fnv1a(int n,...) | |
h = 0x811c9dc5; | |
- va_start(l, n); | |
+ va_start(l, n); | |
for (i = 0; i < n; i++) { | |
for (s = va_arg(l, char*); *s; s++) { | |
h ^= *s; | |
@@ -53,42 +177,17 @@ fnv1a(int n,...) | |
return h; | |
} | |
-uint32_t | |
-xorshift(uint32_t *s) | |
+/* | |
+ An LCG using the constants from "Numerical Recipes". | |
+*/ | |
+uint16_t | |
+ranqd1(uint32_t *s) | |
{ | |
- *s ^= *s << 13; | |
- *s ^= *s >> 17; | |
- *s ^= *s << 5; | |
- return *s; | |
+ return (*s = 1664525 * (*s) + 1013904223) >> 16; | |
} | |
-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) | |
+randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng, int (*filter)(… | |
{ | |
struct rect *r, *result; | |
size_t n; | |
@@ -96,35 +195,54 @@ randomneighbor(struct rect *x, struct rect *rs, uint32_t *… | |
n = 0; | |
result = NULL; | |
for (r = rs; r; r = r->next) { | |
+ if (r == x) | |
+ continue; | |
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; | |
+ if (!filter(x, r)) | |
+ continue; | |
n++; | |
- if (xorshift(prng) / (1. + UINT32_MAX) < 1. / n) | |
+ if (ranqd1(prng) / (1. + UINT16_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) | |
+min(size_t a, size_t b) | |
+{ | |
+ if (a < b) | |
+ return a; | |
+ return b; | |
+} | |
+ | |
+size_t | |
+max(size_t a, size_t b) | |
+{ | |
+ if (a > b) | |
+ return a; | |
+ return b; | |
+} | |
+ | |
+/* | |
+ Creates an uneven grid by splitting the map recursively. | |
+ Returns an array containing the cells (rects) of the grid. | |
+*/ | |
+struct rect * | |
+generaterects(size_t heightmin, size_t widthmin, uint32_t prng) | |
{ | |
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; | |
+ struct rect *rects; | |
+ size_t w, h; | |
+ int vertical, spaceforvertical, spaceforhorizontal; | |
r = malloc(sizeof(*r)); | |
- r->x1 = r->y1 = ROOM_MARGIN_MIN; | |
+ memset(r, 0, sizeof(*r)); | |
+ r->x1 = r->y1 = 0; | |
r->x2 = MAPWIDTH; | |
r->y2 = MAPHEIGHT; | |
r->d = 0; | |
@@ -134,7 +252,6 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size_t … | |
queuehead = r; | |
rects = NULL; | |
- rl = 0; | |
while (queuehead) { | |
r = queuehead; | |
@@ -142,35 +259,36 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size_… | |
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) { | |
+ spaceforvertical = r->y2 - r->y1 >= heightmin * 2; | |
+ spaceforhorizontal = r->x2 - r->x1 >= widthmin * 2; | |
+ | |
+ if (spaceforhorizontal && spaceforvertical) { | |
+ vertical = ranqd1(&prng) & 1; | |
+ } else if (spaceforhorizontal) { | |
vertical = 0; | |
- } else if (r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) { | |
+ } else if (spaceforvertical) { | |
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… | |
+ h = heightmin + ranqd1(&prng) % (1 + r->y2 - r->y1 - h… | |
} else { | |
- w = CELL_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r-… | |
+ w = widthmin + ranqd1(&prng) % (1 + r->x2 - r->x1 - wi… | |
h = r->y2 - r->y1; | |
} | |
t = malloc(sizeof(*t)); | |
+ memset(t, 0, 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; | |
@@ -181,6 +299,7 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size_t … | |
} | |
t = malloc(sizeof(*t)); | |
+ memset(t, 0, sizeof(*t)); | |
if (vertical) { | |
t->x1 = r->x1; | |
t->y1 = r->y1 + h; | |
@@ -191,8 +310,6 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size_t … | |
t->x2 = r->x2; | |
t->y2 = r->y2; | |
t->d = r->d + 1; | |
- t->next = NULL; | |
- t->room = NULL; | |
queuetail->next = t; | |
queuetail = t; | |
@@ -200,75 +317,107 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size… | |
free(r); | |
} | |
- if (l > rl) | |
- l = rl; | |
+ return rects; | |
+} | |
- 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; | |
- } | |
+void | |
+connectpoints_horizontal(size_t y, | |
+ size_t ax, int ea, struct tile *at, | |
+ size_t bx, int eb, struct tile *bt, | |
+ struct tile *t) | |
+{ | |
+ size_t i, s, e; | |
+ ssize_t ii; | |
- for (r = rects; r;) { | |
- t = r->next; | |
- free(r); | |
- r = t; | |
- } | |
+ if (ax < bx) | |
+ ii = 1; | |
+ else if (ax > bx) | |
+ ii = -1; | |
+ else | |
+ ii = 0; | |
- return l; | |
+ s = ax; | |
+ if (ea) | |
+ s += ii; | |
+ e = bx + ii; | |
+ if (eb) | |
+ e -= ii; | |
+ | |
+ for (i = s; i != e; i += ii) | |
+ map[y][i].tile = t; | |
+ | |
+ if (e - ii == s) { | |
+ if (at != t) | |
+ map[y][s].tile = at; | |
+ if (bt != t) | |
+ map[y][s].tile = bt; | |
+ } else { | |
+ map[y][s].tile = at; | |
+ map[y][e - ii].tile = bt; | |
+ } | |
} | |
-size_t | |
-distance(size_t x1, size_t y1, size_t x2, size_t y2) | |
+void | |
+connectpoints_vertical(size_t x, | |
+ size_t ay, int ea, struct tile *at, | |
+ size_t by, int eb, struct tile *bt, | |
+ struct tile *t) | |
{ | |
- size_t d; | |
+ size_t i, s, e; | |
+ ssize_t ii; | |
- if (y1 < y2) | |
- d = y2 - y1; | |
- else | |
- d = y1 - y2; | |
- if (x1 < x2) | |
- d += x2 - x1; | |
+ if (ay < by) | |
+ ii = 1; | |
+ else if (ay > by) | |
+ ii = -1; | |
else | |
- d += x1 - x2; | |
+ ii = 0; | |
- return d; | |
+ s = ay; | |
+ if (ea) | |
+ s += ii; | |
+ e = by + ii; | |
+ if (eb) | |
+ e -= ii; | |
+ | |
+ for (i = s; i != e; i += ii) | |
+ map[i][x].tile = t; | |
+ | |
+ if (e - ii == s) { | |
+ if (at != t) | |
+ map[s][x].tile = at; | |
+ if (bt != t) | |
+ map[s][x].tile = bt; | |
+ } else { | |
+ map[s][x].tile = at; | |
+ map[e - ii][x].tile = bt; | |
+ } | |
+} | |
+ | |
+void | |
+connectpoints(size_t ax, size_t ay, int ea, struct tile *at, | |
+ size_t bx, size_t by, int eb, struct tile *bt, | |
+ int vertical, struct tile *ct) | |
+{ | |
+ if (!vertical) { | |
+ connectpoints_horizontal(ay, | |
+ ax, ea, at, | |
+ bx, 0, ct, | |
+ ct); | |
+ connectpoints_vertical(bx, | |
+ ay, 0, ct, | |
+ by, eb, bt, | |
+ ct); | |
+ } else { | |
+ connectpoints_vertical(ax, | |
+ ay, ea, at, | |
+ by, 0, ct, | |
+ ct); | |
+ connectpoints_horizontal(by, | |
+ ax, 0, ct, | |
+ bx, eb, bt, | |
+ ct); | |
+ } | |
} | |
void | |
@@ -300,50 +449,246 @@ nearestpoints(struct room *a, struct room *b, size_t *ax… | |
} | |
void | |
-connectrooms(struct room *a, struct room *b) | |
+connectadjacentrooms(struct rect *a, struct room *ar, struct rect *b, struct r… | |
{ | |
- size_t i, j; | |
- ssize_t ii; | |
- size_t x1, y1; | |
- size_t x2, y2; | |
+ size_t irx1, iry1, irx2, iry2; | |
+ size_t rx1, ry1, rx2, ry2; | |
+ size_t cx, cy; | |
+ struct rect *r1, *r2; | |
+ struct room *room1, *room2; | |
+ int vertical; | |
- nearestpoints(a, b, &x1, &y1, &x2, &y2); | |
+ if (a->x2 == b->x1) { | |
+ r1 = a; | |
+ room1 = ar; | |
+ r2 = b; | |
+ room2 = br; | |
+ } else if (b->x2 == a->x1) { | |
+ r1 = b; | |
+ room1 = br; | |
+ r2 = a; | |
+ room2 = ar; | |
+ } else if (a->y2 == b->y1) { | |
+ r1 = a; | |
+ room1 = ar; | |
+ r2 = b; | |
+ room2 = br; | |
+ } else if (b->y2 == a->y1) { | |
+ r1 = b; | |
+ room1 = br; | |
+ room2 = ar; | |
+ r2 = a; | |
+ } else { | |
+ return; | |
+ } | |
- if (y1 > y2) { | |
- ii = -1; | |
- } else if (y2 > y1) { | |
- ii = 1; | |
+ if (r1->y2 == r2->y1) { | |
+ irx1 = max(r1->x1, r2->x1); | |
+ irx2 = min(r1->x2, r2->x2); | |
+ iry1 = r1->y2; | |
+ iry2 = r1->y2 + 1; | |
} else { | |
- ii = 0; | |
+ iry1 = max(r1->y1, r2->y1); | |
+ iry2 = min(r1->y2, r2->y2); | |
+ irx1 = r1->x2; | |
+ irx2 = r1->x2 + 1; | |
+ } | |
+ | |
+ nearestpoints(room1, room2, &rx1, &ry1, &rx2, &ry2); | |
+ | |
+ if (r1->y2 == r2->y1) { | |
+ /* both points are in the intersection */ | |
+ if (rx1 >= irx1 && rx1 < irx2 && | |
+ rx2 >= irx1 && rx2 < irx2) { | |
+ vertical = 1; | |
+ cx = (rx2 + rx1) / 2; | |
+ cy = (ry2 + ry1) / 2; | |
+ } else | |
+ /* none is in the intersection */ | |
+ if (!(rx1 >= irx1 && rx1 < irx2) && | |
+ !(rx2 >= irx1 && rx2 < irx2)) { | |
+ vertical = 0; | |
+ cx = irx1; | |
+ cy = r1->y2; | |
+ } else if (rx1 >= irx1 && rx1 < irx2) { | |
+ vertical = 1; | |
+ cx = (rx2 + rx1) / 2; | |
+ cy = r1->y2; | |
+ } else if (rx2 >= irx1 && rx2 < irx2) { | |
+ vertical = 1; | |
+ cx = rx2; | |
+ cy = r1->y2 - 1; | |
+ } | |
+ } else { | |
+ /* both points are in the intersection */ | |
+ if (ry1 >= iry1 && ry1 < iry2 && | |
+ ry2 >= iry1 && ry2 < iry2) { | |
+ vertical = 0; | |
+ cx = (rx2 + rx1) / 2; | |
+ cy = (ry2 + ry1) / 2; | |
+ } else | |
+ /* none is in the intersection */ | |
+ if (!(ry1 >= iry1 && ry1 < iry2) && | |
+ !(ry2 >= iry1 && ry2 < iry2)) { | |
+ vertical = 1; | |
+ cx = r1->x2; | |
+ cy = iry1; | |
+ } else if (ry1 >= iry1 && ry1 < iry2) { | |
+ vertical = 0; | |
+ cx = r1->x2; | |
+ cy = (ry2 + ry1) / 2; | |
+ } else if (ry2 >= iry1 && ry2 < iry2) { | |
+ vertical = 0; | |
+ cx = r1->x2 - 1; | |
+ cy = ry2; | |
+ } | |
} | |
+ if (rx1 == rx2) { | |
+ connectpoints_vertical(rx1, | |
+ ry1, 1, &tile_door, | |
+ ry2, 1, &tile_door, | |
+ &tile_corridor); | |
+ } else if (ry1 == ry2) { | |
+ connectpoints_horizontal(ry1, | |
+ rx1, 1, &tile_door, | |
+ rx2, 1, &tile_door, | |
+ &tile_corridor); | |
+ } else { | |
+ connectpoints(rx1, ry1, 1, &tile_door, | |
+ cx, cy, 0, &tile_corridor, | |
+ vertical, &tile_corridor); | |
+ connectpoints(cx, cy, 1, &tile_corridor, | |
+ rx2, ry2, 1, &tile_door, | |
+ !vertical, &tile_corridor); | |
+ } | |
+} | |
+ | |
+int | |
+rectisfull(struct rect *x, struct rect *r) | |
+{ | |
+ return !!r->data.i; | |
+} | |
+ | |
+int | |
+rectisempty(struct rect *x, struct rect *r) | |
+{ | |
+ return !r->data.i; | |
+} | |
+ | |
+int | |
+rectisnotp(struct rect *x, struct rect *r) | |
+{ | |
+ return r->data.p && x->p != r && r->p != x; | |
+} | |
+ | |
+int | |
+rectisrandom(struct rect *x, struct rect *r) | |
+{ | |
+ return 1; | |
+} | |
+ | |
/* | |
-printf("%lu\t%lu\t%d\n", y1, y2, ii); | |
+ Basically https://www.roguebasin.com/index.php/Diffusion-limited_aggre… | |
+ Returns the list of carved rooms. | |
*/ | |
- for (i = y1; i != y2; i += ii) | |
- map[i][x1].c = '.'; | |
+struct rect * | |
+dla(struct rect *rects, size_t l, uint32_t prng) { | |
+ size_t rl, i, n; | |
+ struct rect *r, *t, *walk, *p; | |
- if (x1 > x2) { | |
- ii = -1; | |
- } else if (x2 > x1) { | |
- ii = 1; | |
- } else { | |
- ii = 0; | |
+ for (r = rects, rl = 0; r; r = r->next) | |
+ rl++; | |
+ | |
+ if (l > rl) | |
+ l = rl; | |
+ | |
+ /* get the rect which contains the map center */ | |
+ 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; | |
} | |
- for (i = x1; i != x2; i += ii) | |
- map[y2][i].c = '.'; | |
+ p = NULL; | |
+ walk = NULL; | |
+ i = 0; | |
+ for (;;) { | |
+ r->p = p; | |
+ r->data.i = 1; | |
+ r->next2 = walk; | |
+ walk = r; | |
+ | |
+ if (i >= l - 1) | |
+ break; | |
+ | |
+ t = NULL; | |
+ for (r = rects, n = 0; r; r = r->next) { | |
+ if (r->data.i) | |
+ continue; | |
+ n++; | |
+ if (ranqd1(&prng) / (1. + UINT16_MAX) < 1. / n) | |
+ t = r; | |
+ } | |
+ | |
+ /* there is no free rect left */ | |
+ if (!t) | |
+ break; | |
+ | |
+ /* do a random walk starting from t until the walk collides wi… | |
+ while ((r = randomneighbor(t, rects, &prng, rectisrandom)) && … | |
+ t = r; | |
+ | |
+ p = r; | |
+ r = t; | |
+ | |
+ i++; | |
+ } | |
+ | |
+ return walk; | |
+} | |
+ | |
+void | |
+rendermapchar(size_t i, size_t j) { | |
+ if (map[i][j].tile->flags & Standout) | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ putchar(map[i][j].tile->c); | |
+ if (map[i][j].tile->flags & Standout) | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+} | |
+ | |
+void | |
+rendermapline(size_t i) | |
+{ | |
+ size_t j; | |
+ | |
+ for (j = ox; j < min(MAPWIDTH, ox + columns); j++) | |
+ rendermapchar(i, j); | |
} | |
void | |
rendermap(void) | |
{ | |
- size_t i, j; | |
+ size_t i; | |
- for (i = 0; i < MAPHEIGHT; i++) { | |
- for (j = 0; j < MAPWIDTH; j++) | |
- putchar(map[i][j].c); | |
- putchar('\n'); | |
+ if (px < columns / 2 || MAPWIDTH <= columns) | |
+ ox = 0; | |
+ else if (px >= MAPWIDTH - columns / 2 - 1) | |
+ ox = MAPWIDTH - columns; | |
+ else | |
+ ox = px - columns / 2; | |
+ | |
+ if (py < maplines / 2 || MAPHEIGHT <= maplines) | |
+ oy = 0; | |
+ else if (py >= MAPHEIGHT - maplines / 2 - 1) | |
+ oy = MAPHEIGHT - maplines; | |
+ else | |
+ oy = py - maplines / 2; | |
+ | |
+ for (i = oy; i < min(MAPHEIGHT, oy + (lines - 2)); i++) { | |
+ if (i != oy) | |
+ putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ rendermapline(i); | |
} | |
} | |
@@ -366,92 +711,171 @@ placeitems_hash(Item *item, size_t *assocs, size_t k) | |
return k; | |
} | |
-#define POSITIONS_LENGTH 4 | |
+#define POSITIONS_LENGTH 5 | |
enum { | |
Portal, | |
StaircaseDown, | |
Bookshelf, | |
+ OtherStuff, | |
Back | |
}; | |
-size_t px, py; | |
+enum { | |
+ FillEntireCell = 1 | |
+}; | |
+ | |
+#define length(a) (sizeof(a) / sizeof(a[0])) | |
+static struct dungeontype { | |
+ char *name; | |
+ char flags; | |
+ size_t heightmin; | |
+ size_t heightmax; | |
+ size_t widthmin; | |
+ size_t widthmax; | |
+ size_t margin; | |
+ size_t wiggle; | |
+} dungeontypes[] = { | |
+ { "rogueish", 0, 2, 5, 3, 7, 2, 0 }, | |
+ { "rogueish-wide", 0, 2, 5, 3, 7, 3, 1 }, | |
+ { "compact", FillEntireCell, 2, 2, 3, 3, 1, 0 }, | |
+}; | |
void | |
-generatemap(Item *item, int new) | |
+generatemap(Item *item, Item *pitem) | |
{ | |
Dir *dir; | |
Item *citem; | |
- size_t i, j, k, l, ir; | |
- size_t x, y; | |
- ssize_t n, m; | |
+ size_t l, i, j, k, ir, n, m, x, y; | |
+ struct rect *rects, *walk, *tr, *cr; | |
+ struct room *rooms, *room; | |
size_t *cassocs; | |
- struct room *rooms, *r; | |
+ int changedlevel, gonedown; | |
+ char buffer[10]; | |
+ uint32_t prng; | |
struct { | |
- unsigned char x, y; | |
+ size_t x, y; | |
} positions[POSITIONS_LENGTH]; | |
- uint32_t prng; | |
- char buffer[3]; | |
+ size_t cellwidth, cellheight; | |
+ struct dungeontype *type; | |
+ | |
+ type = &dungeontypes[fnv1a(3, item->host, item->port, "dungeontype") %… | |
+ | |
+ cellheight = type->heightmin + 2 * type->margin + type->wiggle; | |
+ cellwidth = type->widthmin + 2 * type->margin + type->wiggle; | |
+ | |
+ rects = generaterects(cellheight, cellwidth, fnv1a(4, item->host, item… | |
+ | |
+ dir = item->dat; | |
+ for (j = l = 0; j < dir->nitems; j++) { | |
+ if (dir->items[j].type != 0 && | |
+ dir->items[j].type != 'i' && | |
+ dir->items[j].type != '3') | |
+ l++; | |
+ } | |
+ | |
+ k = 1 + l / 10; | |
+ walk = dla(rects, k, fnv1a(4, item->host, item->port, item->selector, … | |
+ for (cr = walk, k = 0; cr; cr = cr->next2, k++); | |
+ | |
+ for (cr = rects; cr; cr = cr->next) | |
+ cr->data.p = NULL; | |
+ | |
+ rooms = calloc(k, sizeof(*rooms)); | |
+ for (cr = walk, i = 0; cr; cr = cr->next2, i++) | |
+ cr->data.p = &rooms[i]; | |
+ | |
+ prng = fnv1a(4, item->host, item->port, item->selector, "roomsseed"); | |
+ for (cr = walk; cr; cr = cr->next2) { | |
+ room = cr->data.p; | |
+ | |
+ if (type->flags & FillEntireCell) { | |
+ room->w = cr->x2 - cr->x1 - 2 * type->margin; | |
+ room->x = cr->x1 + type->margin; | |
+ room->h = cr->y2 - cr->y1 - 2 * type->margin; | |
+ room->y = cr->y1 + type->margin; | |
+ } else { | |
+ room->w = type->widthmin + ranqd1(&prng) % (1 + min(cr… | |
+ room->x = cr->x1 + type->margin + ranqd1(&prng) % (1 +… | |
+ room->h = type->heightmin + ranqd1(&prng) % (1 + min(c… | |
+ room->y = cr->y1 + type->margin + ranqd1(&prng) % (1 +… | |
+ } | |
+ } | |
for (i = 0; i < MAPHEIGHT; i++) { | |
for (j = 0; j < MAPWIDTH; j++) { | |
- map[i][j].c = '#'; | |
+ map[i][j].tile = &tile_void; | |
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++; | |
+ for (cr = walk; cr; cr = cr->next2) { | |
+ room = cr->data.p; | |
+ | |
+ for (x = room->x - 1; x < room->x + room->w + 1; x++) | |
+ map[room->y-1][x].tile = &tile_horizontalwall; | |
+ for (y = room->y; y < room->y + room->h; y++) { | |
+ map[y][room->x - 1].tile = &tile_verticalwall; | |
+ for (x = room->x; x < room->x + room->w; x++) | |
+ map[y][x].tile = &tile_floor; | |
+ map[y][room->x + room->w].tile = &tile_verticalwall; | |
+ } | |
+ for (x = room->x - 1; x < room->x + room->w + 1; x++) | |
+ map[room->y + room->h][x].tile = &tile_horizontalwall; | |
} | |
- k = 1 + l / 10; | |
- rooms = calloc(k, sizeof(*rooms)); | |
- if (!rooms) | |
- return; | |
- k = generaterooms_gnarf(fnv1a(3, item->host, item->port, item->selecto… | |
+ for (cr = walk; cr; cr = cr->next2) { | |
+ if (cr->p) | |
+ connectadjacentrooms(cr, cr->data.p, | |
+ cr->p, cr->p->data.p); | |
+ | |
+ /* Add some loop possibility */ | |
+ if (tr = randomneighbor(cr, rects, &prng, rectisnotp)) | |
+ connectadjacentrooms(cr, cr->data.p, | |
+ tr, tr->data.p); | |
+ } | |
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); | |
- } | |
+ changedlevel = item != pitem; | |
+ gonedown = pitem == item->entry; | |
/* | |
Insert items | |
- The placement of items affects the initial placement of the pl… | |
+ The placement of items affects the initial placement of the | |
+ player, because they could have gone back to this map, so they | |
+ should appear at the elevator/portal/stair they used. | |
*/ | |
- ir = fnv1a(4, item->host, item->port, item->selector, "initial_room") … | |
+ | |
+ /* | |
+ The initial room is everytime the first one. Reason: The count | |
+ of rooms is based on how many entries are in the gophermap and | |
+ how many rooms can fit on the map. There will be at minimum 1. | |
+ So when more entries get added there will be more rooms but the | |
+ first one stays at the same position. I think about the | |
+ retrying and clownflare things on bitreich.org, the selector | |
+ doesn't change... | |
+ */ | |
+ ir = 0; | |
for (i = 0; i < k; i++) { | |
- snprintf(buffer, sizeof(buffer), "%d", i); | |
+ /* select random positions for different item types inside the… | |
+ snprintf(buffer, sizeof(buffer), "%lu", 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) <… | |
+ for (j = 0, m = rooms[i].h * rooms[i].w; j < m; j++) { | |
+ n = j; | |
+ if (j >= POSITIONS_LENGTH) | |
+ n *= ranqd1(&prng) / (double)UINT16_MAX; | |
+ | |
+ if (n < POSITIONS_LENGTH) { | |
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; | |
@@ -462,35 +886,44 @@ generatemap(Item *item, int new) | |
x = positions[Bookshelf].x; | |
y = positions[Bookshelf].y; | |
if (map[y][x].nitems) | |
- map[y][x].c = 'E'; | |
+ map[y][x].tile = &tile_bookshelf; | |
else | |
- map[y][x].c = '?'; | |
+ map[y][x].tile = &tile_book; | |
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'; | |
+ map[y][x].tile = &tile_portalm… | |
else | |
- map[y][x].c = '0'; | |
+ map[y][x].tile = &tile_portal; | |
} else { | |
x = positions[StaircaseDown].x; | |
y = positions[StaircaseDown].y; | |
if (map[y][x].nitems) | |
- map[y][x].c = 'L'; | |
+ map[y][x].tile = &tile_elevato… | |
else | |
- map[y][x].c = '>'; | |
+ map[y][x].tile = &tile_stairsd… | |
} | |
break; | |
- default: | |
+ case 0: | |
+ case 'i': | |
+ case '3': | |
continue; | |
+ break; | |
+ default: | |
+ x = positions[OtherStuff].x; | |
+ y = positions[OtherStuff].y; | |
+ map[y][x].tile = &tile_heapofstuff; | |
+ break; | |
} | |
+ | |
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) { | |
+ if (changedlevel && citem == pitem) { | |
px = x; | |
py = y; | |
} | |
@@ -500,23 +933,28 @@ generatemap(Item *item, int new) | |
y = positions[Back].y; | |
x = positions[Back].x; | |
if (strcmp(item->entry->host, item->host) || strcmp(it… | |
- map[y][x].c = '0'; | |
+ map[y][x].tile = &tile_backportal; | |
else | |
- map[y][x].c = '<'; | |
+ map[y][x].tile = &tile_stairsup; | |
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) { | |
+ if (changedlevel && i == ir && (gonedown || !pitem)) { | |
px = positions[Back].x; | |
py = positions[Back].y; | |
} | |
} | |
- free(cassocs); | |
-cleanup: | |
+ free(cassocs); | |
free(rooms); | |
+ | |
+ for (cr = rects; cr;) { | |
+ tr = cr; | |
+ cr = cr->next; | |
+ free(tr); | |
+ } | |
} | |
void | |
@@ -544,6 +982,7 @@ uicleanup(void) | |
if (termset != OK) | |
return; | |
+ putp(tparm(change_scroll_region, 0, lines-1, 0, 0, 0, 0, 0, 0, 0)); | |
putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
fflush(stdout); | |
} | |
@@ -655,7 +1094,7 @@ uistatus(char *fmt, ...) | |
putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
fflush(stdout); | |
- getchar(); | |
+ mygetchar(); | |
} | |
void | |
@@ -668,225 +1107,287 @@ displayinfoline(char *fmt, ...) | |
va_end(ap); | |
} | |
-Item * | |
-showmenu(char *title, Item **item, size_t l) | |
+char *menutitle; | |
+Item **menuitems; | |
+size_t menunitems; | |
+volatile size_t menuoffset; | |
+size_t menuselected; | |
+ | |
+void | |
+menudraw(void) | |
{ | |
- size_t i; | |
+ size_t i, n; | |
+ putp(tparm(change_scroll_region, 1, lines-1, 0, 0, 0, 0, 0, 0, 0)); | |
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); | |
+ puts(menutitle); | |
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; | |
+ if (menuselected - menuoffset >= lines - 1) | |
+ menuoffset = menuselected - (lines - 1) + 1; | |
+ | |
+ for (i = menuoffset, n = 0; i < menunitems && n < lines - 1; i++, n++)… | |
+ if (i != menuoffset) | |
+ putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ if (i == menuselected) | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0… | |
+ mbsprint(menuitems[i]->username, columns); | |
+ if (i == menuselected) | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0,… | |
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ } | |
fflush(stdout); | |
- | |
- return item[i]; | |
} | |
Item * | |
-prompt(char *text, Item *item) | |
-{ | |
- displayinfoline(text, item->username); | |
- getchar(); | |
- return item; | |
-} | |
- | |
-Item * | |
-interact(Item *item) | |
+showmenu(char *title, Item **item, size_t l) | |
{ | |
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; | |
- } | |
- | |
+ menutitle = title; | |
+ menuitems = item; | |
+ menunitems = l; | |
+ menuselected = 0; | |
+ menuoffset = 0; | |
+ screen = MenuScreen; | |
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… | |
+ selection = NULL; | |
+ for (;;) { | |
+ switch (mygetchar()) { | |
+ case 'j': | |
+ if (menuselected + 1 < menunitems) { | |
+ putp(tparm(cursor_address, 1 + menuselected - … | |
+ mbsprint(menuitems[menuselected]->username, co… | |
+ menuselected++; | |
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0… | |
+ if (menuselected - menuoffset >= lines - 1) { | |
+ menuoffset++; | |
+ putp(tparm(scroll_forward, 0, 0, 0, 0,… | |
+ } else { | |
+ putp(tparm(cursor_down, 0, 0, 0, 0, 0,… | |
+ } | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0,… | |
+ mbsprint(menuitems[menuselected]->username, co… | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, … | |
+ } | |
break; | |
- case 'L': | |
- displayinfoline("You hear elevator music..."); | |
+ case 'k': | |
+ if (menuselected > 0) { | |
+ putp(tparm(cursor_address, 1 + menuselected - … | |
+ mbsprint(menuitems[menuselected]->username, co… | |
+ menuselected--; | |
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0… | |
+ if (menuselected < menuoffset) { | |
+ menuoffset = menuselected; | |
+ putp(tparm(scroll_reverse, 0, 0, 0, 0,… | |
+ } else { | |
+ putp(tparm(cursor_up, 0, 0, 0, 0, 0, 0… | |
+ } | |
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0,… | |
+ mbsprint(menuitems[menuselected]->username, co… | |
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, … | |
+ } | |
break; | |
- case '<': | |
- case '>': | |
- displayinfoline("Too many stairs..."); | |
+ case ' ': | |
+ selection = menuitems[menuselected]; | |
+ case 0x1b: | |
+ goto endloop; | |
break; | |
} | |
+ fflush(stdout); | |
} | |
+endloop: | |
+ screen = DungeonScreen; | |
+ drawscreen(); | |
+ | |
return selection; | |
} | |
+Item * | |
+interactitem(struct cell *c) | |
+{ | |
+ displayinfoline(map[py][px].tile->afterinteract); | |
+ | |
+ return map[py][px].items[0]; | |
+} | |
+ | |
+Item * | |
+interactmenu(struct cell *c) | |
+{ | |
+ Item *selection; | |
+ | |
+ if (selection = showmenu(map[py][px].tile->name, map[py][px].items, ma… | |
+ displayinfoline(map[py][px].tile->afterinteract); | |
+ | |
+ return selection; | |
+} | |
+ | |
+Item * | |
+interact(Item *item) | |
+{ | |
+ if (map[py][px].tile->interact) | |
+ return map[py][px].tile->interact(&map[py][px]); | |
+ | |
+ return NULL; | |
+} | |
+ | |
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': | |
+ char *name; | |
+ | |
+ if (map[y][x].nitems) { | |
if (*map[y][x].items[0]->username) { | |
- displayinfoline("'%s'.", map[y][x].items[0]->username); | |
+ name = map[y][x].items[0]->username; | |
} else { | |
itemuri(map[y][x].items[0], bufout2, sizeof(bufout2)); | |
- displayinfoline("'%s'.", bufout2); | |
+ name = bufout2; | |
} | |
- break; | |
- default: | |
- if (verbose) { | |
- switch (map[y][x].c) { | |
- case '.': | |
- displayinfoline("Floor."); | |
- break; | |
- case '#': | |
- displayinfoline("Wall."); | |
- break; | |
- } | |
- } else { | |
- displayinfoline(""); | |
- } | |
- break; | |
+ } else { | |
+ name = NULL; | |
} | |
+ if (map[y][x].tile->flags & Important || verbose) | |
+ displayinfoline(map[y][x].tile->description, name); | |
+ else | |
+ displayinfoline(""); | |
+} | |
+ | |
+void | |
+dungeondraw(void) | |
+{ | |
+ putp(tparm(change_scroll_region, 0, lines-3, 0, 0, 0, 0, 0, 0, 0)); | |
+ putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ rendermap(); | |
+ | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, 0, 0, 0, 0)); | |
+ putchar('@'); | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, 0, 0, 0, 0)); | |
+ | |
+ if (curentry->entry != curentry) { | |
+ displaybar(curentry->username); | |
+ } else { | |
+ itemuri(curentry, bufout, sizeof(bufout)); | |
+ displaybar(bufout); | |
+ } | |
+ | |
+ describe(px, py, 0); | |
} | |
void | |
move(ssize_t dx, ssize_t dy) | |
{ | |
+ ssize_t i; | |
size_t x, y; | |
+ size_t noy, nox; | |
+ | |
+ if ((ssize_t)py + dy >= MAPHEIGHT || (ssize_t)py + dy < 0) | |
+ return; | |
+ if ((ssize_t)px + dx >= MAPWIDTH || (ssize_t)px + dx < 0) | |
+ return; | |
- /* allow wraparound of the world for the lulz, even if it's not happen… | |
- y = (MAPHEIGHT + py + dy) % MAPHEIGHT; | |
- x = (MAPWIDTH + px + dx) % MAPWIDTH; | |
+ x = px + dx; | |
+ y = py + dy; | |
- if (map[y][x].c == '#') | |
+ if (map[y][x].tile->flags & Blocks) | |
return; | |
- putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
- putchar(map[py][px].c); | |
+ if (dx) { | |
+ if (x < columns / 2 || MAPWIDTH <= columns) | |
+ nox = 0; | |
+ else if (x >= MAPWIDTH - columns / 2 - 1) | |
+ nox = MAPWIDTH - columns; | |
+ else | |
+ nox = x - columns / 2; | |
+ | |
+ if (ox != nox) { | |
+ putp(tparm(cursor_address, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ rendermap(); | |
+ } else { | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, … | |
+ rendermapchar(py, px); | |
+ } | |
+ } else if (dy) { | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, 0, 0, 0,… | |
+ rendermapchar(py, px); | |
+ | |
+ if (y < maplines / 2 || MAPHEIGHT <= maplines) { | |
+ noy = 0; | |
+ } else if (y >= MAPHEIGHT - maplines / 2 - 1) { | |
+ noy = MAPHEIGHT - maplines; | |
+ } else { | |
+ noy = y - maplines / 2; | |
+ } | |
+ | |
+ if (noy < oy) { | |
+ putp(tparm(cursor_address, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
+ for (i = (ssize_t)oy - 1; i >= (ssize_t)noy; i--) { | |
+ putp(tparm(scroll_reverse, 0, 0, 0, 0, 0, 0, 0… | |
+ rendermapline(i); | |
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0… | |
+ } | |
+ } else if (noy > oy) { | |
+ putp(tparm(cursor_address, lines-3, 0, 0, 0, 0, 0, 0, … | |
+ for (i = oy + 1; i <= noy; i++) { | |
+ putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0… | |
+ rendermapline(i + maplines - 1); | |
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0… | |
+ } | |
+ } | |
+ oy = noy; | |
+ } | |
py = y; | |
px = x; | |
- putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, 0, 0, 0, 0)); | |
putchar('@'); | |
- putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 0, 0, 0, 0, 0, 0)); | |
- describe(x, y, 0); | |
- putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 )); | |
+ describe(px, py, 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); | |
+ switch (screen) { | |
+ case DungeonScreen: | |
+ dungeondraw(); | |
+ break; | |
+ case MenuScreen: | |
+ menudraw(); | |
+ break; | |
} | |
- | |
- move(0, 0); | |
- | |
+ fflush(stdout); | |
} | |
void | |
uidisplay(Item *entry) | |
{ | |
- if (!entry || entry->type != '1') | |
+ if (!entry || !(entry->type == '1' || entry->type == '+' || entry->typ… | |
return; | |
- generatemap(entry, curentry != entry); | |
+ if (entry != curentry) { | |
+ generatemap(entry, curentry); | |
+ 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)) | |
+ if (!entry || !entry->dat) | |
return NULL; | |
for (;;) { | |
- switch (getchar()) { | |
+ switch (mygetchar()) { | |
case 'h': | |
move(-1, 0); | |
break; | |
@@ -899,40 +1400,20 @@ uiselectitem(Item *entry) | |
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; | |
- } | |
- } | |
- } | |
+ if (e = interact(entry)) | |
return e; | |
- } | |
break; | |
case 'q': | |
+ case 0x1b: | |
return NULL; | |
} | |
+ fflush(stdout); | |
} | |
} | |
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); | |
+ sigwinch = 1; | |
} | |
diff --git a/ui_rogue_notes b/ui_rogue_notes | |
@@ -0,0 +1,31 @@ | |
+# Notes | |
+- in need for a big gopher directory? use gopher://bitreich.org/1/memecache/ | |
+ | |
+# Bugs | |
+- not all corridors have 2 doors | |
+ | |
+# Future Features? | |
+- k-medoids clustering using selector strings in reasonable time... | |
+ - items with similar selectors should get inserted into the same rooms | |
+ - one could use some mapping (sammon mapping?) to place the rooms based on t… | |
+ - similar clusters are near together | |
+- examine mode | |
+ - allow the cursor to move freely and describe the tiles with describe() in … | |
+- running using H, J, K, L | |
+ - go into the direction until the floor tile changes? | |
+ - with wall sliding to go through corridors | |
+ - needs to stop at junctions | |
+- spells | |
+ - teleport to a random room on the current level | |
+ - teleport to a random already visited level | |
+ - inscribe a scroll to teleport later back to the current level | |
+- visibility thingies to declutter things | |
+ - maybe like hack: keep things visible after discovery | |
+ - and what's with persisting this information? | |
+ - only show the content of the current room | |
+- better dungeon generation | |
+ - analyse things to create clever corridors | |
+- different dungeon themes | |
+ - so basically an extended dungeontype thing, with different dungeon generat… | |
+ - maybe some special layouts for root directories (empty selector) | |
+ - i look at you, ultima castle generator! https://slash.itch.io/ultima-cas… | |
diff --git a/ui_rogue_readme b/ui_rogue_readme | |
@@ -0,0 +1,28 @@ | |
+# Description | |
+This UI module (?) for sacc displays directories like rogue/hack dungeon level… | |
+ | |
+The code is ugly. It's obvious that I (pazz0) don't know what I do. | |
+ | |
+# Compiling | |
+Copy ui_rogue.c in the sacc source directory. | |
+Go in the sacc source directory and execute blindly the following commands: | |
+``` | |
+make clean && make UI=rogue | |
+``` | |
+sacc will now have the much more confusing ui_rogue interface. | |
+ | |
+# Key mapping | |
+h, j, k, l: left, down, up, right | |
+space: interact (in dungeon), select (in menu) | |
+ESC or q: exit (in dungeon), close menu | |
+ | |
+# Map explanation | |
+E or ?: bookshelf/book (gopher type '0') | |
+L or >: elevator/staircase down/staircase up (gopher type '1' on the curre… | |
+O or 0: portal machine/portal (gopher type '1' on another server) | |
+%: heap of stuff (other non-handled gopher types, apart from 'i' and … | |
+standout (inverse) 0 or <: a way to go back to the previous gopher directory | |
+ | |
+. or #: floor | |
+/: doors (behaves like floor) | |
+| or -: wall |