commit 90a9d3c81f27588c62faa43f22d946740042a3ce
parent cc6a6881e06ab4b851c7778842374b953e51539c
Author: Julian Schweinsberg <pazz0@0xfa.de>
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 everywhere where nothing is.", NULL, NULL };
+struct tile tile_floor = { '.', 0, "Floor", "An ordinary stone floor.", NULL, NULL };
+struct tile tile_corridor = { '#', 0, "Different Floor", "This floor looks different than the other one.", NULL, NULL };
+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 loading bar?! In a book?!", interactmenu };
+struct tile tile_book = { '?', Important, "%s", "A book: '%s'.", "A loading bar?! In a book?!", interactitem };
+struct tile tile_portal = { '0', Important, "%s", "A portal: '%s'.", "You are getting transported through time and space.", interactitem };
+struct tile tile_portalmachine = { 'O', Important, "Portal Machine", "A portal machine.", "You are getting transported through time and space.", interactmenu };
+struct tile tile_heapofstuff = { '%', Important, "Heap", "A heap of stuff.", "The thing you touches glows strangely...", interactmenu };
+struct tile tile_elevator = { 'L', Important, "Elevator", "An elevator.", "You hear elevator music...", interactmenu };
+struct tile tile_stairsdown = { '>', Important, "'%s'", "A staircase leading down: '%s'.", "Too many stairs...", interactitem };
+
+struct tile tile_stairsup = { '<', Standout | Important, "'%s'", "A staircase leading up: '%s'.", "Too many stairs...", interactitem };
+struct tile tile_backportal = { '0', Standout | Important, "'%s'", "A portal leading back to wherever you came from: '%s'.", "You are getting transported through time and space.", interactitem };
+
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/publicdomain/zero/1.0/ )
+ by Landon Curt Noll: http://www.isthe.com/chongo/tech/comp/fnv/#public_domain
+*/
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 *, struct rect *))
{
struct rect *r, *result;
size_t n;
@@ -96,35 +195,54 @@ randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng)
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 > x->x2)
continue;
if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r->x1 == x->x2))
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 l)
queuehead = r;
rects = NULL;
- rl = 0;
while (queuehead) {
r = queuehead;
@@ -142,35 +259,36 @@ generaterooms_gnarf(uint32_t prng, struct room *rs, size_t l)
queuetail = NULL;
queuehead = queuehead->next;
- if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2 && r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) {
- 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->y1 - CELL_HEIGHT_MIN * 2);
+ h = heightmin + ranqd1(&prng) % (1 + r->y2 - r->y1 - heightmin * 2);
} else {
- w = CELL_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - CELL_WIDTH_MIN * 2);
+ w = widthmin + ranqd1(&prng) % (1 + r->x2 - r->x1 - widthmin * 2);
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 l)
}
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 l)
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_t l)
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_MARGIN_MIN - ROOM_WIDTH_MIN);
- rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN);
- rs[i].x = r->x1 + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_MIN - rs[i].w);
- rs[i].y = r->y1 + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - rs[i].h);
- 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. / n)
- r = t;
-
- }
- continue;
- }
- rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - ROOM_WIDTH_MIN);
- rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN);
- rs[i].x = t->x1 + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - rs[i].w);
- rs[i].y = t->y1 + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - rs[i].h);
- 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, size_t *ay, size_t *bx
}
void
-connectrooms(struct room *a, struct room *b)
+connectadjacentrooms(struct rect *a, struct room *ar, struct rect *b, struct room *br)
{
- 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_aggregation
+ 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 with a carved room (r) */
+ while ((r = randomneighbor(t, rects, &prng, rectisrandom)) && !r->data.i)
+ 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") % length(dungeontypes)];
+
+ 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->port, item->selector, "gridseed"));
+
+ 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, "randomwalkseed"));
+ 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->x2 - cr->x1 - type->widthmin - 2 * type->margin, type->widthmax - type->widthmin));
+ room->x = cr->x1 + type->margin + ranqd1(&prng) % (1 + cr->x2 - cr->x1 - room->w - 2 * type->margin);
+ room->h = type->heightmin + ranqd1(&prng) % (1 + min(cr->y2 - cr->y1 - type->heightmin - 2 * type->margin, type->heightmax - type->heightmin));
+ room->y = cr->y1 + type->margin + ranqd1(&prng) % (1 + cr->y2 - cr->y1 - room->h - 2 * type->margin);
+ }
+ }
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->selector), rooms, k);
+ 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 player, because they could have gone back to this map, so they should appear at the elevator/portal/stair they used.
+ 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") % k;
+
+ /*
+ 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 current room */
+ 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) < POSITIONS_LENGTH - n) {
+ 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(citem->port, item->port)) {
x = positions[Portal].x;
y = positions[Portal].y;
if (map[y][x].nitems)
- map[y][x].c = 'O';
+ map[y][x].tile = &tile_portalmachine;
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_elevator;
else
- map[y][x].c = '>';
+ map[y][x].tile = &tile_stairsdown;
}
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].nitems * sizeof(*map[y][x].items));
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(item->entry->port, item->port))
- 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].nitems * sizeof(*map[y][x].items));
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, 0));
+ mbsprint(menuitems[i]->username, columns);
+ if (i == menuselected)
+ putp(tparm(exit_standout_mode, 0, 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][px].nitems);
- break;
- case 'O':
- selection = showmenu("Portal machine", map[py][px].items, map[py][px].nitems);
- break;
- case 'L':
- selection = showmenu("Elevator", map[py][px].items, map[py][px].nitems);
- 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 time and space.");
+ selection = NULL;
+ for (;;) {
+ switch (mygetchar()) {
+ case 'j':
+ if (menuselected + 1 < menunitems) {
+ putp(tparm(cursor_address, 1 + menuselected - menuoffset, 0, 0, 0, 0, 0, 0, 0, 0));
+ mbsprint(menuitems[menuselected]->username, columns);
+ menuselected++;
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ if (menuselected - menuoffset >= lines - 1) {
+ menuoffset++;
+ putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ } else {
+ putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ mbsprint(menuitems[menuselected]->username, columns);
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
break;
- case 'L':
- displayinfoline("You hear elevator music...");
+ case 'k':
+ if (menuselected > 0) {
+ putp(tparm(cursor_address, 1 + menuselected - menuoffset, 0, 0, 0, 0, 0, 0, 0, 0));
+ mbsprint(menuitems[menuselected]->username, columns);
+ menuselected--;
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ if (menuselected < menuoffset) {
+ menuoffset = menuselected;
+ putp(tparm(scroll_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ } else {
+ putp(tparm(cursor_up, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
+ putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ mbsprint(menuitems[menuselected]->username, columns);
+ putp(tparm(exit_standout_mode, 0, 0, 0, 0, 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, map[py][px].nitems))
+ 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 happening */
- 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, 0, 0, 0, 0));
+ rendermapchar(py, px);
+ }
+ } else if (dy) {
+ putp(tparm(cursor_address, py - oy, px - ox, 0, 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, 0, 0));
+ rendermapline(i);
+ putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
+ } else if (noy > oy) {
+ putp(tparm(cursor_address, lines-3, 0, 0, 0, 0, 0, 0, 0, 0));
+ for (i = oy + 1; i <= noy; i++) {
+ putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ rendermapline(i + maplines - 1);
+ putp(tparm(column_address, 0, 0, 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->type == '7'))
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 the cluster medoids
+ - similar clusters are near together
+- examine mode
+ - allow the cursor to move freely and describe the tiles with describe() in verbose mode
+- 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 generation algorithms
+ - maybe some special layouts for root directories (empty selector)
+ - i look at you, ultima castle generator! https://slash.itch.io/ultima-castle-generator
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 levels.
+
+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 current server)
+O or 0: portal machine/portal (gopher type '1' on another server)
+%: heap of stuff (other non-handled gopher types, apart from 'i' and '3')
+standout (inverse) 0 or <: a way to go back to the previous gopher directory
+
+. or #: floor
+/: doors (behaves like floor)
+| or -: wall