URI: 
       ui_rogue.c - sacc - sacc(omys), simple console gopher client
  HTML git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/sacc/
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR LICENSE
       ---
       ui_rogue.c (29707B)
       ---
            1 #include <errno.h>
            2 #include <signal.h>
            3 #include <stdarg.h>
            4 #include <stdint.h>
            5 #include <stdio.h>
            6 #include <stdlib.h>
            7 #include <string.h>
            8 #include <term.h>
            9 #include <termios.h>
           10 #include <unistd.h>
           11 #include <sys/select.h>
           12 #include <sys/types.h>
           13 
           14 #include "common.h"
           15 #include "config.h"
           16 
           17 #define C(c) #c
           18 #define S(c) C(c)
           19 
           20 /* ncurses doesn't define those in term.h, where they're used */
           21 #ifndef OK
           22 #define OK (0)
           23 #endif
           24 #ifndef ERR
           25 #define ERR (-1)
           26 #endif
           27 
           28 #define maplines (lines - 2)
           29 
           30 enum {
           31         Blocks = 1,
           32         Standout = 2,
           33         Important = 4
           34 };
           35 
           36 struct cell;
           37 struct tile {
           38         char c;
           39         char flags;
           40         char *name;
           41         char *description;
           42         char *afterinteract;
           43         Item *(*interact)(struct cell *);
           44 };
           45 
           46 struct cell {
           47         struct tile *tile;
           48         size_t nitems;
           49         Item **items;
           50 };
           51 
           52 struct room {
           53         size_t x, y;
           54         size_t w, h;
           55 };
           56 
           57 struct rect {
           58         struct rect *next, *next2;
           59         struct rect *p;
           60         size_t x1, y1;
           61         size_t x2, y2;
           62         size_t d;
           63         union {
           64                 void *p;
           65                 int i;
           66         } data;
           67 };
           68 
           69 static struct termios tsave;
           70 static struct termios tsacc;
           71 static Item *curentry;
           72 static int termset = ERR;
           73 static char bufout[256];
           74 static char bufout2[256];
           75 
           76 size_t ox, oy;
           77 size_t px, py;
           78 
           79 #define MAPHEIGHT (50)
           80 #define MAPWIDTH (160)
           81 struct cell map[MAPHEIGHT][MAPWIDTH];
           82 
           83 enum {
           84         DungeonScreen,
           85         MenuScreen
           86 } screen;
           87 
           88 Item *interactitem(struct cell *);
           89 Item *interactmenu(struct cell *);
           90 
           91 struct tile tile_void = { ' ', Blocks, "Void", "The void. The thing which is everywhere where nothing is.", NULL, NULL };
           92 struct tile tile_floor = { '.', 0, "Floor", "An ordinary stone floor.", NULL, NULL };
           93 struct tile tile_corridor = { '#', 0, "Different Floor", "This floor looks different than the other one.", NULL, NULL };
           94 struct tile tile_verticalwall = { '|', Blocks, "Wall", "Wall.", NULL, NULL };
           95 struct tile tile_horizontalwall = { '-', Blocks, "Wall", "Wall.", NULL, NULL };
           96 struct tile tile_door = { '/', 0, "Door", "A door.", NULL, NULL };
           97 struct tile tile_bookshelf = { 'E', Important, "Bookshelf", "A bookshelf.", "A loading bar?! In a book?!", interactmenu };
           98 struct tile tile_book = { '?', Important, "%s", "A book: '%s'.", "A loading bar?! In a book?!", interactitem };
           99 struct tile tile_portal = { '0', Important, "%s", "A portal: '%s'.", "You are getting transported through time and space.", interactitem };
          100 struct tile tile_portalmachine = { 'O', Important, "Portal Machine", "A portal machine.", "You are getting transported through time and space.", interactmenu };
          101 struct tile tile_heapofstuff = { '%', Important, "Heap", "A heap of stuff.", "The thing you touches glows strangely...", interactmenu };
          102 struct tile tile_elevator = { 'L', Important, "Elevator", "An elevator.", "You hear elevator music...", interactmenu };
          103 struct tile tile_stairsdown = { '>', Important, "'%s'", "A staircase leading down: '%s'.", "Too many stairs...", interactitem };
          104 
          105 struct tile tile_stairsup = { '<', Standout | Important, "'%s'", "A staircase leading up: '%s'.", "Too many stairs...", interactitem };
          106 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 };
          107 
          108 void drawscreen(void);
          109 
          110 int
          111 mygetchar_(void)
          112 {
          113         int r;
          114         fd_set fdset;
          115 
          116         FD_ZERO(&fdset);
          117         FD_SET(0, &fdset);
          118 
          119         if ((r = select(1, &fdset, NULL, NULL, NULL)) == -1) {
          120                 if (errno == EINTR)
          121                         return -1;
          122                 return -2;
          123         }
          124 
          125         return getchar();
          126 }
          127 
          128 volatile sig_atomic_t sigwinch;
          129 
          130 int
          131 mygetchar(void)
          132 {
          133         int r;
          134 
          135         while ((r = mygetchar_()) == -1) {
          136                 if (sigwinch) {
          137                         sigwinch = 0;
          138 
          139                         if (termset == OK)
          140                                 del_curterm(cur_term);
          141                         termset = setupterm(NULL, 1, NULL);
          142 
          143                         drawscreen();
          144                 }
          145         }
          146 
          147         if (r == -2)
          148                 die("mygetchar: %s", strerror(errno));
          149 
          150         return r;
          151 }
          152 
          153 /*
          154         FNV-1a ( http://www.isthe.com/chongo/tech/comp/fnv/ )
          155         FNV was published into the public domain ( https://creativecommons.org/publicdomain/zero/1.0/ )
          156         by Landon Curt Noll: http://www.isthe.com/chongo/tech/comp/fnv/#public_domain
          157 */
          158 uint32_t
          159 fnv1a(int n,...)
          160 {
          161         int i;
          162         char *s;
          163         va_list l;
          164         uint32_t h;
          165 
          166         h = 0x811c9dc5;
          167 
          168         va_start(l, n);
          169         for (i = 0; i < n; i++) {
          170                 for (s = va_arg(l, char*); *s; s++) {
          171                         h ^= *s;
          172                         h *= 0x01000193;
          173                 }
          174         }
          175         va_end(l);
          176 
          177         return h;
          178 }
          179 
          180 /*
          181         An LCG using the constants from "Numerical Recipes".
          182 */
          183 uint16_t
          184 ranqd1(uint32_t *s)
          185 {
          186         return (*s = 1664525 * (*s) + 1013904223) >> 16;
          187 }
          188 
          189 struct rect *
          190 randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng, int (*filter)(struct rect *, struct rect *))
          191 {
          192         struct rect *r, *result;
          193         size_t n;
          194 
          195         n = 0;
          196         result = NULL;
          197         for (r = rs; r; r = r->next) {
          198                 if (r == x)
          199                         continue;
          200                 if (r->y2 < x->y1 || r->y1 > x->y2 || r->x2 < x->x1 || r->x1 > x->x2)
          201                         continue;
          202                 if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r->x1 == x->x2))
          203                         continue;
          204                 if (!filter(x, r))
          205                         continue;
          206                 n++;
          207                 if (ranqd1(prng) / (1. + UINT16_MAX) < 1. / n)
          208                         result = r;
          209         }
          210 
          211         return result;
          212 }
          213 
          214 size_t
          215 min(size_t a, size_t b)
          216 {
          217         if (a < b)
          218                 return a;
          219         return b;
          220 }
          221 
          222 size_t
          223 max(size_t a, size_t b)
          224 {
          225         if (a > b)
          226                 return a;
          227         return b;
          228 }
          229 
          230 /*
          231         Creates an uneven grid by splitting the map recursively.
          232         Returns an array containing the cells (rects) of the grid.
          233 */
          234 struct rect *
          235 generaterects(size_t heightmin, size_t widthmin, uint32_t prng)
          236 {
          237         struct rect *queuehead, *queuetail;
          238         struct rect *r, *t;
          239         struct rect *rects;
          240         size_t w, h;
          241         int vertical, spaceforvertical, spaceforhorizontal;
          242 
          243         r = malloc(sizeof(*r));
          244         memset(r, 0, sizeof(*r));
          245         r->x1 = r->y1 = 0;
          246         r->x2 = MAPWIDTH;
          247         r->y2 = MAPHEIGHT;
          248         r->d = 0;
          249 
          250         queuetail = r;
          251         queuetail->next = NULL;
          252         queuehead = r;
          253 
          254         rects = NULL;
          255 
          256         while (queuehead) {
          257                 r = queuehead;
          258                 if (queuetail == queuehead)
          259                         queuetail = NULL;
          260                 queuehead = queuehead->next;
          261 
          262                 spaceforvertical = r->y2 - r->y1 >= heightmin * 2;
          263                 spaceforhorizontal = r->x2 - r->x1 >= widthmin * 2;
          264 
          265                 if (spaceforhorizontal && spaceforvertical) {
          266                         vertical = ranqd1(&prng) & 1;
          267                 } else if (spaceforhorizontal) {
          268                         vertical = 0;
          269                 } else if (spaceforvertical) {
          270                         vertical = 1;
          271                 } else {
          272                         r->next = rects;
          273                         rects = r;
          274                         continue;
          275                 }
          276 
          277                 if (vertical) {
          278                         w = r->x2 - r->x1;
          279                         h = heightmin + ranqd1(&prng) % (1 + r->y2 - r->y1 - heightmin * 2);
          280                 } else {
          281                         w = widthmin + ranqd1(&prng) % (1 + r->x2 - r->x1 - widthmin * 2);
          282                         h = r->y2 - r->y1;
          283                 }
          284 
          285                 t = malloc(sizeof(*t));
          286                 memset(t, 0, sizeof(*t));
          287                 t->x1 = r->x1;
          288                 t->y1 = r->y1;
          289                 t->x2 = r->x1 + w;
          290                 t->y2 = r->y1 + h;
          291                 t->d = r->d + 1;
          292 
          293                 if (!queuetail) {
          294                         queuehead = t;
          295                         queuetail = t;
          296                 } else {
          297                         queuetail->next = t;
          298                         queuetail = t;
          299                 }
          300 
          301                 t = malloc(sizeof(*t));
          302                 memset(t, 0, sizeof(*t));
          303                 if (vertical) {
          304                         t->x1 = r->x1;
          305                         t->y1 = r->y1 + h;
          306                 } else {
          307                         t->x1 = r->x1 + w;
          308                         t->y1 = r->y1;
          309                 }
          310                 t->x2 = r->x2;
          311                 t->y2 = r->y2;
          312                 t->d = r->d + 1;
          313 
          314                 queuetail->next = t;
          315                 queuetail = t;
          316 
          317                 free(r);
          318         }
          319 
          320         return rects;
          321 }
          322 
          323 void
          324 connectpoints_horizontal(size_t y,
          325                        size_t ax, int ea, struct tile *at,
          326                        size_t bx, int eb, struct tile *bt,
          327                        struct tile *t)
          328 {
          329         size_t i, s, e;
          330         ssize_t ii;
          331 
          332         if (ax < bx)
          333                 ii = 1;
          334         else if (ax > bx)
          335                 ii = -1;
          336         else
          337                 ii = 0;
          338 
          339         s = ax;
          340         if (ea)
          341                 s += ii;
          342         e = bx + ii;
          343         if (eb)
          344                 e -= ii;
          345 
          346         for (i = s; i != e; i += ii)
          347                 map[y][i].tile = t;
          348 
          349         if (e - ii == s) {
          350                 if (at != t)
          351                         map[y][s].tile = at;
          352                 if (bt != t)
          353                         map[y][s].tile = bt;
          354         } else {
          355                 map[y][s].tile = at;
          356                 map[y][e - ii].tile = bt;
          357         }
          358 }
          359 
          360 void
          361 connectpoints_vertical(size_t x,
          362                        size_t ay, int ea, struct tile *at,
          363                        size_t by, int eb, struct tile *bt,
          364                        struct tile *t)
          365 {
          366         size_t i, s, e;
          367         ssize_t ii;
          368 
          369         if (ay < by)
          370                 ii = 1;
          371         else if (ay > by)
          372                 ii = -1;
          373         else
          374                 ii = 0;
          375 
          376         s = ay;
          377         if (ea)
          378                 s += ii;
          379         e = by + ii;
          380         if (eb)
          381                 e -= ii;
          382 
          383         for (i = s; i != e; i += ii)
          384                 map[i][x].tile = t;
          385 
          386         if (e - ii == s) {
          387                 if (at != t)
          388                         map[s][x].tile = at;
          389                 if (bt != t)
          390                         map[s][x].tile = bt;
          391         } else {
          392                 map[s][x].tile = at;
          393                 map[e - ii][x].tile = bt;
          394         }
          395 }
          396 
          397 void
          398 connectpoints(size_t ax, size_t ay, int ea, struct tile *at,
          399               size_t bx, size_t by, int eb, struct tile *bt,
          400               int vertical, struct tile *ct)
          401 {
          402         if (!vertical) {
          403                 connectpoints_horizontal(ay,
          404                                          ax, ea, at,
          405                                          bx, 0, ct,
          406                                          ct);
          407                 connectpoints_vertical(bx,
          408                                        ay, 0, ct,
          409                                        by, eb, bt,
          410                                        ct);
          411         } else {
          412                 connectpoints_vertical(ax,
          413                                        ay, ea, at,
          414                                        by, 0, ct,
          415                                        ct);
          416                 connectpoints_horizontal(by,
          417                                          ax, 0, ct,
          418                                          bx, eb, bt,
          419                                          ct);
          420         }
          421 }
          422 
          423 void
          424 nearestpoints(struct room *a, struct room *b, size_t *ax, size_t *ay, size_t *bx, size_t *by)
          425 {
          426         if (a->y >= b->y && a->y < b->y + b->h) {
          427                 *ay = *by = a->y;
          428         } else if (b->y >= a->y && b->y < a->y + a->h) {
          429                 *ay = *by = b->y;
          430         } else if (a->y >= b->y) {
          431                 *ay = a->y;
          432                 *by = b->y + b->h - 1;
          433         } else if (b->y >= a->y) {
          434                 *ay = a->y + a->h - 1;
          435                 *by = b->y;
          436         }
          437 
          438         if (a->x >= b->x && a->x < b->x + b->w) {
          439                 *ax = *bx = a->x;
          440         } else if (b->x >= a->x && b->x < a->x + a->w) {
          441                 *ax = *bx = b->x;
          442         } else if (a->x >= b->x) {
          443                 *ax = a->x;
          444                 *bx = b->x + b->w - 1;
          445         } else if (b->x >= a->x) {
          446                 *ax = a->x + a->w - 1;
          447                 *bx = b->x;
          448         }
          449 }
          450 
          451 void
          452 connectadjacentrooms(struct rect *a, struct room *ar, struct rect *b, struct room *br)
          453 {
          454         size_t irx1, iry1, irx2, iry2;
          455         size_t rx1, ry1, rx2, ry2;
          456         size_t cx, cy;
          457         struct rect *r1, *r2;
          458         struct room *room1, *room2;
          459         int vertical;
          460 
          461         if (a->x2 == b->x1) {
          462                 r1 = a;
          463                 room1 = ar;
          464                 r2 = b;
          465                 room2 = br;
          466         } else if (b->x2 == a->x1) {
          467                 r1 = b;
          468                 room1 = br;
          469                 r2 = a;
          470                 room2 = ar;
          471         } else if (a->y2 == b->y1) {
          472                 r1 = a;
          473                 room1 = ar;
          474                 r2 = b;
          475                 room2 = br;
          476         } else if (b->y2 == a->y1) {
          477                 r1 = b;
          478                 room1 = br;
          479                 room2 = ar;
          480                 r2 = a;
          481         } else {
          482                 return;
          483         }
          484 
          485         if (r1->y2 == r2->y1) {
          486                 irx1 = max(r1->x1, r2->x1);
          487                 irx2 = min(r1->x2, r2->x2);
          488                 iry1 = r1->y2;
          489                 iry2 = r1->y2 + 1;
          490         } else {
          491                 iry1 = max(r1->y1, r2->y1);
          492                 iry2 = min(r1->y2, r2->y2);
          493                 irx1 = r1->x2;
          494                 irx2 = r1->x2 + 1;
          495         }
          496 
          497         nearestpoints(room1, room2, &rx1, &ry1, &rx2, &ry2);
          498 
          499         if (r1->y2 == r2->y1) {
          500                 /* both points are in the intersection */
          501                 if (rx1 >= irx1 && rx1 < irx2 &&
          502                     rx2 >= irx1 && rx2 < irx2) {
          503                         vertical = 1;
          504                         cx = (rx2 + rx1) / 2;
          505                         cy = (ry2 + ry1) / 2;
          506                 } else
          507                 /* none is in the intersection */
          508                 if (!(rx1 >= irx1 && rx1 < irx2) &&
          509                     !(rx2 >= irx1 && rx2 < irx2)) {
          510                         vertical = 0;
          511                         cx = irx1;
          512                         cy = r1->y2;
          513                 } else if (rx1 >= irx1 && rx1 < irx2) {
          514                         vertical = 1;
          515                         cx = (rx2 + rx1) / 2;
          516                         cy = r1->y2;
          517                 } else if (rx2 >= irx1 && rx2 < irx2) {
          518                         vertical = 1;
          519                         cx = rx2;
          520                         cy = r1->y2 - 1;
          521                 }
          522         } else {
          523                 /* both points are in the intersection */
          524                 if (ry1 >= iry1 && ry1 < iry2 &&
          525                     ry2 >= iry1 && ry2 < iry2) {
          526                         vertical = 0;
          527                         cx = (rx2 + rx1) / 2;
          528                         cy = (ry2 + ry1) / 2;
          529                 } else
          530                 /* none is in the intersection */
          531                 if (!(ry1 >= iry1 && ry1 < iry2) &&
          532                     !(ry2 >= iry1 && ry2 < iry2)) {
          533                         vertical = 1;
          534                         cx = r1->x2;
          535                         cy = iry1;
          536                 } else if (ry1 >= iry1 && ry1 < iry2) {
          537                         vertical = 0;
          538                         cx = r1->x2;
          539                         cy = (ry2 + ry1) / 2;
          540                 } else if (ry2 >= iry1 && ry2 < iry2) {
          541                         vertical = 0;
          542                         cx = r1->x2 - 1;
          543                         cy = ry2;
          544                 }
          545         }
          546 
          547         if (rx1 == rx2) {
          548                 connectpoints_vertical(rx1,
          549                                        ry1, 1, &tile_door,
          550                                        ry2, 1, &tile_door,
          551                                        &tile_corridor);
          552         } else if (ry1 == ry2) {
          553                 connectpoints_horizontal(ry1,
          554                                          rx1, 1, &tile_door,
          555                                          rx2, 1, &tile_door,
          556                                          &tile_corridor);
          557         } else {
          558                 connectpoints(rx1, ry1, 1, &tile_door,
          559                               cx, cy, 0, &tile_corridor,
          560                               vertical, &tile_corridor);
          561                 connectpoints(cx, cy, 1, &tile_corridor,
          562                               rx2, ry2, 1, &tile_door,
          563                               !vertical, &tile_corridor);
          564         }
          565 }
          566 
          567 int
          568 rectisfull(struct rect *x, struct rect *r)
          569 {
          570         return !!r->data.i;
          571 }
          572 
          573 int
          574 rectisempty(struct rect *x, struct rect *r)
          575 {
          576         return !r->data.i;
          577 }
          578 
          579 int
          580 rectisnotp(struct rect *x, struct rect *r)
          581 {
          582         return r->data.p && x->p != r && r->p != x;
          583 }
          584 
          585 int
          586 rectisrandom(struct rect *x, struct rect *r)
          587 {
          588         return 1;
          589 }
          590 
          591 /*
          592         Basically https://www.roguebasin.com/index.php/Diffusion-limited_aggregation
          593         Returns the list of carved rooms.
          594 */
          595 struct rect *
          596 dla(struct rect *rects, size_t l, uint32_t prng) {
          597         size_t rl, i, n;
          598         struct rect *r, *t, *walk, *p;
          599 
          600         for (r = rects, rl = 0; r; r = r->next)
          601                 rl++;
          602 
          603         if (l > rl)
          604                 l = rl;
          605 
          606         /* get the rect which contains the map center */
          607         for (r = rects; r; r = r->next) {
          608                 if (MAPHEIGHT / 2 >= r->y1 && MAPHEIGHT / 2 < r->y2 &&
          609                     MAPWIDTH / 2 >= r->x1 && MAPWIDTH / 2 < r->x2)
          610                         break;
          611         }
          612 
          613         p = NULL;
          614         walk = NULL;
          615         i = 0;
          616         for (;;) {
          617                 r->p = p;
          618                 r->data.i = 1;
          619                 r->next2 = walk;
          620                 walk = r;
          621 
          622                 if (i >= l - 1)
          623                         break;
          624 
          625                 t = NULL;
          626                 for (r = rects, n = 0; r; r = r->next) {
          627                         if (r->data.i)
          628                                 continue;
          629                         n++;
          630                         if (ranqd1(&prng) / (1. + UINT16_MAX) < 1. / n)
          631                                 t = r;
          632                 }
          633 
          634                 /* there is no free rect left */
          635                 if (!t)
          636                         break;
          637 
          638                 /* do a random walk starting from t until the walk collides with a carved room (r) */
          639                 while ((r = randomneighbor(t, rects, &prng, rectisrandom)) && !r->data.i)
          640                         t = r;
          641 
          642                 p = r;
          643                 r = t;
          644 
          645                 i++;
          646         }
          647 
          648         return walk;
          649 }
          650 
          651 void
          652 rendermapchar(size_t i, size_t j) {
          653         if (map[i][j].tile->flags & Standout)
          654                 putp(tiparm(enter_standout_mode));
          655         putchar(map[i][j].tile->c);
          656         if (map[i][j].tile->flags & Standout)
          657                 putp(tiparm(exit_standout_mode));
          658 }
          659 
          660 void
          661 rendermapline(size_t i)
          662 {
          663         size_t j;
          664 
          665         for (j = ox; j < min(MAPWIDTH, ox + columns); j++)
          666                 rendermapchar(i, j);
          667 }
          668 
          669 void
          670 rendermap(void)
          671 {
          672         size_t i;
          673 
          674         if (px < columns / 2 || MAPWIDTH <= columns)
          675                 ox = 0;
          676         else if (px >= MAPWIDTH - columns / 2 - 1)
          677                 ox = MAPWIDTH - columns;
          678         else
          679                 ox = px - columns / 2;
          680 
          681         if (py < maplines / 2 || MAPHEIGHT <= maplines)
          682                 oy = 0;
          683         else if (py >= MAPHEIGHT - maplines / 2 - 1)
          684                 oy = MAPHEIGHT - maplines;
          685         else
          686                 oy = py - maplines / 2;
          687 
          688         for (i = oy; i < min(MAPHEIGHT, oy + (lines - 2)); i++) {
          689                 if (i != oy)
          690                         putp(tiparm(cursor_down));
          691                 rendermapline(i);
          692         }
          693 }
          694 
          695 size_t
          696 placeitems_hash(Item *item, size_t *assocs, size_t k)
          697 {
          698         Dir *dir;
          699         Item *citem;
          700         size_t i;
          701 
          702         dir = item->dat;
          703         for (i = 0; i < dir->nitems; i++) {
          704                 citem = &dir->items[i];
          705                 /* TODO Somewhere else */
          706                 if (!citem->host || !citem->port || !citem->selector)
          707                         continue;
          708                 assocs[i] = fnv1a(6, item->host, item->port, item->selector, citem->host, citem->port, citem->selector) % k;
          709         }
          710 
          711         return k;
          712 }
          713 
          714 #define POSITIONS_LENGTH 5
          715 enum {
          716         Portal,
          717         StaircaseDown,
          718         Bookshelf,
          719         OtherStuff,
          720         Back
          721 };
          722 
          723 enum {
          724         FillEntireCell = 1
          725 };
          726 
          727 #define length(a) (sizeof(a) / sizeof(a[0]))
          728 static struct dungeontype {
          729         char *name;
          730         char flags;
          731         size_t heightmin;
          732         size_t heightmax;
          733         size_t widthmin;
          734         size_t widthmax;
          735         size_t margin;
          736         size_t wiggle;
          737 } dungeontypes[] = {
          738         { "rogueish", 0, 2, 5, 3, 7, 2, 0 },
          739         { "rogueish-wide", 0, 2, 5, 3, 7, 3, 1 },
          740         { "compact", FillEntireCell, 2, 2, 3, 3, 1, 0 },
          741 };
          742 
          743 void
          744 generatemap(Item *item, Item *pitem)
          745 {
          746         Dir *dir;
          747         Item *citem;
          748         size_t l, i, j, k, ir, n, m, x, y;
          749         struct rect *rects, *walk, *tr, *cr;
          750         struct room *rooms, *room;
          751         size_t *cassocs;
          752         int changedlevel, gonedown;
          753         char buffer[10];
          754         uint32_t prng;
          755         struct {
          756                 size_t x, y;
          757         } positions[POSITIONS_LENGTH];
          758         size_t cellwidth, cellheight;
          759         struct dungeontype *type;
          760 
          761         type = &dungeontypes[fnv1a(3, item->host, item->port, "dungeontype") % length(dungeontypes)];
          762 
          763         cellheight = type->heightmin + 2 * type->margin + type->wiggle;
          764         cellwidth = type->widthmin + 2 * type->margin + type->wiggle;
          765 
          766         rects = generaterects(cellheight, cellwidth, fnv1a(4, item->host, item->port, item->selector, "gridseed"));
          767 
          768         dir = item->dat;
          769         for (j = l = 0; j < dir->nitems; j++) {
          770                 if (dir->items[j].type != 0 &&
          771                     dir->items[j].type != 'i' &&
          772                     dir->items[j].type != '3')
          773                         l++;
          774         }
          775 
          776         k = 1 + l / 10;
          777         walk = dla(rects, k, fnv1a(4, item->host, item->port, item->selector, "randomwalkseed"));
          778         for (cr = walk, k = 0; cr; cr = cr->next2, k++);
          779 
          780         for (cr = rects; cr; cr = cr->next)
          781                 cr->data.p = NULL;
          782 
          783         rooms = calloc(k, sizeof(*rooms));
          784         for (cr = walk, i = 0; cr; cr = cr->next2, i++)
          785                 cr->data.p = &rooms[i];
          786 
          787         prng = fnv1a(4, item->host, item->port, item->selector, "roomsseed");
          788         for (cr = walk; cr; cr = cr->next2) {
          789                 room = cr->data.p;
          790 
          791                 if (type->flags & FillEntireCell) {
          792                         room->w = cr->x2 - cr->x1 - 2 * type->margin;
          793                         room->x = cr->x1 + type->margin;
          794                         room->h = cr->y2 - cr->y1 - 2 * type->margin;
          795                         room->y = cr->y1 + type->margin;
          796                 } else {
          797                         room->w = type->widthmin + ranqd1(&prng) % (1 + min(cr->x2 - cr->x1 - type->widthmin - 2 * type->margin, type->widthmax - type->widthmin));
          798                         room->x = cr->x1 + type->margin + ranqd1(&prng) % (1 + cr->x2 - cr->x1 - room->w - 2 * type->margin);
          799                         room->h = type->heightmin + ranqd1(&prng) % (1 + min(cr->y2 - cr->y1 - type->heightmin - 2 * type->margin, type->heightmax - type->heightmin));
          800                         room->y = cr->y1 + type->margin + ranqd1(&prng) % (1 + cr->y2 - cr->y1 - room->h - 2 * type->margin);
          801                 }
          802         }
          803 
          804         for (i = 0; i < MAPHEIGHT; i++) {
          805                 for (j = 0; j < MAPWIDTH; j++) {
          806                         map[i][j].tile = &tile_void;
          807                         free(map[i][j].items);
          808                         map[i][j].items = NULL;
          809                         map[i][j].nitems = 0;
          810                 }
          811         }
          812 
          813         for (cr = walk; cr; cr = cr->next2) {
          814                 room = cr->data.p;
          815 
          816                 for (x = room->x - 1; x < room->x + room->w + 1; x++)
          817                         map[room->y-1][x].tile = &tile_horizontalwall;
          818                 for (y = room->y; y < room->y + room->h; y++) {
          819                         map[y][room->x - 1].tile = &tile_verticalwall;
          820                         for (x = room->x; x < room->x + room->w; x++)
          821                                 map[y][x].tile = &tile_floor;
          822                         map[y][room->x + room->w].tile = &tile_verticalwall;
          823                 }
          824                 for (x = room->x - 1; x < room->x + room->w + 1; x++)
          825                         map[room->y + room->h][x].tile = &tile_horizontalwall;
          826         }
          827 
          828         for (cr = walk; cr; cr = cr->next2) {
          829                 if (cr->p)
          830                         connectadjacentrooms(cr, cr->data.p,
          831                                              cr->p, cr->p->data.p);
          832 
          833                 /* Add some loop possibility */
          834                 if (tr = randomneighbor(cr, rects, &prng, rectisnotp))
          835                         connectadjacentrooms(cr, cr->data.p,
          836                                              tr, tr->data.p);
          837         }
          838 
          839         cassocs = calloc(dir->nitems, sizeof(*cassocs));
          840 
          841         k = placeitems_hash(item, cassocs, k);
          842 
          843         changedlevel = item != pitem;
          844         gonedown = pitem == item->entry;
          845 
          846         /*
          847                 Insert items
          848                 The placement of items affects the initial placement of the
          849                 player, because they could have gone back to this map, so they
          850                 should appear at the elevator/portal/stair they used.
          851         */
          852 
          853         /*
          854                 The initial room is everytime the first one. Reason: The count
          855                 of rooms is based on how many entries are in the gophermap and
          856                 how many rooms can fit on the map. There will be at minimum 1.
          857                 So when more entries get added there will be more rooms but the
          858                 first one stays at the same position. I think about the
          859                 retrying and clownflare things on bitreich.org, the selector
          860                 doesn't change...
          861         */
          862         ir = 0;
          863 
          864         for (i = 0; i < k; i++) {
          865                 /* select random positions for different item types inside the current room */
          866                 snprintf(buffer, sizeof(buffer), "%lu", i);
          867                 prng = fnv1a(4, item->host, item->port, item->selector, buffer);
          868                 for (j = 0, m = rooms[i].h * rooms[i].w; j < m; j++) {
          869                         n = j;
          870                         if (j >= POSITIONS_LENGTH)
          871                                 n *= ranqd1(&prng) / (double)UINT16_MAX;
          872 
          873                         if (n < POSITIONS_LENGTH) {
          874                                 positions[n].x = rooms[i].x + j % rooms[i].w;
          875                                 positions[n].y = rooms[i].y + j / rooms[i].w;
          876                         }
          877                 }
          878 
          879                 for (j = 0; j < dir->nitems; j++) {
          880                         if (cassocs[j] != i)
          881                                 continue;
          882 
          883                         citem = &dir->items[j];
          884                         switch (citem->type) {
          885                         case '0':
          886                                 x = positions[Bookshelf].x;
          887                                 y = positions[Bookshelf].y;
          888                                 if (map[y][x].nitems)
          889                                         map[y][x].tile = &tile_bookshelf;
          890                                 else
          891                                         map[y][x].tile = &tile_book;
          892                                 break;
          893                         case '1':
          894                                 if (strcmp(citem->host, item->host) || strcmp(citem->port, item->port)) {
          895                                         x = positions[Portal].x;
          896                                         y = positions[Portal].y;
          897                                         if (map[y][x].nitems)
          898                                                 map[y][x].tile = &tile_portalmachine;
          899                                         else
          900                                                 map[y][x].tile = &tile_portal;
          901                                 } else {
          902                                         x = positions[StaircaseDown].x;
          903                                         y = positions[StaircaseDown].y;
          904                                         if (map[y][x].nitems)
          905                                                 map[y][x].tile = &tile_elevator;
          906                                         else
          907                                                 map[y][x].tile = &tile_stairsdown;
          908                                 }
          909                                 break;
          910                         case 0:
          911                         case 'i':
          912                         case '3':
          913                                 continue;
          914                                 break;
          915                         default:
          916                                 x = positions[OtherStuff].x;
          917                                 y = positions[OtherStuff].y;
          918                                 map[y][x].tile = &tile_heapofstuff;
          919                                 break;
          920                         }
          921 
          922                         map[y][x].nitems++;
          923                         map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
          924                         map[y][x].items[map[y][x].nitems-1] = citem;
          925 
          926                         if (changedlevel && citem == pitem) {
          927                                 px = x;
          928                                 py = y;
          929                         }
          930                 }
          931 
          932                 if (i == ir && item->entry != item) {
          933                         y = positions[Back].y;
          934                         x = positions[Back].x;
          935                         if (strcmp(item->entry->host, item->host) || strcmp(item->entry->port, item->port))
          936                                 map[y][x].tile = &tile_backportal;
          937                         else
          938                                 map[y][x].tile = &tile_stairsup;
          939                         map[y][x].nitems++;
          940                         map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
          941                         map[y][x].items[map[y][x].nitems-1] = item->entry;
          942                 }
          943 
          944                 if (changedlevel && i == ir && (gonedown || !pitem)) {
          945                         px = positions[Back].x;
          946                         py = positions[Back].y;
          947                 }
          948         }
          949 
          950         free(cassocs);
          951         free(rooms);
          952 
          953         for (cr = rects; cr;) {
          954                 tr = cr;
          955                 cr = cr->next;
          956                 free(tr);
          957         }
          958 }
          959 
          960 void
          961 uisetup(void)
          962 {
          963         tcgetattr(0, &tsave);
          964         tsacc = tsave;
          965         tsacc.c_lflag &= ~(ECHO|ICANON);
          966         tsacc.c_cc[VMIN] = 1;
          967         tsacc.c_cc[VTIME] = 0;
          968         tcsetattr(0, TCSANOW, &tsacc);
          969 
          970         if (termset != OK)
          971                 /* setupterm call exits on error */
          972                 termset = setupterm(NULL, 1, NULL);
          973         putp(tiparm(clear_screen));
          974         fflush(stdout);
          975 }
          976 
          977 void
          978 uicleanup(void)
          979 {
          980         tcsetattr(0, TCSANOW, &tsave);
          981 
          982         if (termset != OK)
          983                 return;
          984 
          985         putp(tiparm(change_scroll_region, 0, lines-1));
          986         putp(tiparm(clear_screen));
          987         fflush(stdout);
          988 }
          989 
          990 char *
          991 uiprompt(char *fmt, ...)
          992 {
          993         va_list ap;
          994         char *input = NULL;
          995         size_t n;
          996         ssize_t r;
          997 
          998         putp(tiparm(save_cursor));
          999 
         1000         putp(tiparm(cursor_address, lines-1, 0));
         1001         putp(tiparm(clr_eol));
         1002 
         1003         va_start(ap, fmt);
         1004         vsnprintf(bufout, sizeof(bufout), fmt, ap);
         1005         va_end(ap);
         1006 
         1007         n = mbsprint(bufout, columns);
         1008 
         1009         putp(tiparm(clr_eol));
         1010 
         1011         putp(tiparm(cursor_address, lines-1, n));
         1012 
         1013         tsacc.c_lflag |= (ECHO|ICANON);
         1014         tcsetattr(0, TCSANOW, &tsacc);
         1015         fflush(stdout);
         1016 
         1017         n = 0;
         1018         r = getline(&input, &n, stdin);
         1019 
         1020         tsacc.c_lflag &= ~(ECHO|ICANON);
         1021         tcsetattr(0, TCSANOW, &tsacc);
         1022         putp(tiparm(restore_cursor));
         1023         fflush(stdout);
         1024 
         1025         if (r == -1 || feof(stdin)) {
         1026                 clearerr(stdin);
         1027                 clear(&input);
         1028         } else if (input[r - 1] == '\n') {
         1029                 input[--r] = '\0';
         1030         }
         1031 
         1032         return input;
         1033 }
         1034 
         1035 void
         1036 displaybar(char *s) {
         1037         size_t n;
         1038 
         1039         putp(tiparm(save_cursor));
         1040 
         1041         putp(tiparm(cursor_address, lines-2, 0));
         1042         putp(tiparm(enter_standout_mode));
         1043 
         1044         n = mbsprint(s, columns);
         1045         for (n = columns - n; n; n--)
         1046                 putchar(' ');
         1047 
         1048         putp(tiparm(exit_standout_mode));
         1049 
         1050         putp(tiparm(restore_cursor));
         1051         fflush(stdout);
         1052 }
         1053 
         1054 void
         1055 vdisplayinfoline(char *fmt, va_list ap)
         1056 {
         1057         putp(tiparm(save_cursor));
         1058 
         1059         putp(tiparm(cursor_address, lines-1, 0));
         1060 
         1061         vsnprintf(bufout, sizeof(bufout), fmt, ap);
         1062 
         1063         mbsprint(bufout, columns);
         1064 
         1065         putp(tiparm(clr_eol));
         1066 
         1067         putp(tiparm(restore_cursor));
         1068         fflush(stdout);
         1069 }
         1070 
         1071 void
         1072 uistatus(char *fmt, ...)
         1073 {
         1074         va_list ap;
         1075         size_t n;
         1076 
         1077         putp(tiparm(save_cursor));
         1078 
         1079         putp(tiparm(cursor_address, lines-1, 0));
         1080 
         1081         va_start(ap, fmt);
         1082         n = vsnprintf(bufout, sizeof(bufout), fmt, ap);
         1083         va_end(ap);
         1084 
         1085         if (n < sizeof(bufout)-1) {
         1086                 snprintf(bufout+n, sizeof(bufout)-n,
         1087                          " [Press a key to continue \xe2\x98\x83]");
         1088         }
         1089 
         1090         mbsprint(bufout, columns);
         1091 
         1092         putp(tiparm(clr_eol));
         1093 
         1094         putp(tiparm(restore_cursor));
         1095         fflush(stdout);
         1096 
         1097         mygetchar();
         1098 }
         1099 
         1100 void
         1101 displayinfoline(char *fmt, ...)
         1102 {
         1103         va_list ap;
         1104 
         1105         va_start(ap, fmt);
         1106         vdisplayinfoline(fmt, ap);
         1107         va_end(ap);
         1108 }
         1109 
         1110 char *menutitle;
         1111 Item **menuitems;
         1112 size_t menunitems;
         1113 volatile size_t menuoffset;
         1114 size_t menuselected;
         1115 
         1116 void
         1117 menudraw(void)
         1118 {
         1119         size_t i, n;
         1120 
         1121         putp(tiparm(change_scroll_region, 1, lines-1));
         1122         putp(tiparm(clear_screen));
         1123 
         1124         putp(tiparm(enter_standout_mode));
         1125         puts(menutitle);
         1126         putp(tiparm(exit_standout_mode));
         1127 
         1128         if (menuselected - menuoffset >= lines - 1)
         1129                 menuoffset = menuselected - (lines - 1) + 1;
         1130 
         1131         for (i = menuoffset, n = 0; i < menunitems && n < lines - 1; i++, n++) {
         1132                 if (i != menuoffset)
         1133                         putp(tiparm(cursor_down));
         1134                 if (i == menuselected)
         1135                         putp(tiparm(enter_standout_mode));
         1136                 mbsprint(menuitems[i]->username, columns);
         1137                 if (i == menuselected)
         1138                         putp(tiparm(exit_standout_mode));
         1139                 putp(tiparm(column_address, 0));
         1140         }
         1141         fflush(stdout);
         1142 }
         1143 
         1144 Item *
         1145 showmenu(char *title, Item **item, size_t l)
         1146 {
         1147         Item *selection;
         1148 
         1149         menutitle = title;
         1150         menuitems = item;
         1151         menunitems = l;
         1152         menuselected = 0;
         1153         menuoffset = 0;
         1154         screen = MenuScreen;
         1155         drawscreen();
         1156 
         1157         selection = NULL;
         1158         for (;;) {
         1159                 switch (mygetchar()) {
         1160                 case 'j':
         1161                         if (menuselected + 1 < menunitems) {
         1162                                 putp(tiparm(cursor_address, 1 + menuselected - menuoffset, 0));
         1163                                 mbsprint(menuitems[menuselected]->username, columns);
         1164                                 menuselected++;
         1165                                 putp(tiparm(column_address, 0));
         1166                                 if (menuselected - menuoffset >= lines - 1) {
         1167                                         menuoffset++;
         1168                                         putp(tiparm(scroll_forward));
         1169                                 } else {
         1170                                         putp(tiparm(cursor_down));
         1171                                 }
         1172                                 putp(tiparm(enter_standout_mode));
         1173                                 mbsprint(menuitems[menuselected]->username, columns);
         1174                                 putp(tiparm(exit_standout_mode));
         1175                         }
         1176                         break;
         1177                 case 'k':
         1178                         if (menuselected > 0) {
         1179                                 putp(tiparm(cursor_address, 1 + menuselected - menuoffset, 0));
         1180                                 mbsprint(menuitems[menuselected]->username, columns);
         1181                                 menuselected--;
         1182                                 putp(tiparm(column_address, 0));
         1183                                 if (menuselected < menuoffset) {
         1184                                         menuoffset = menuselected;
         1185                                         putp(tiparm(scroll_reverse));
         1186                                 } else {
         1187                                         putp(tiparm(cursor_up));
         1188                                 }
         1189                                 putp(tiparm(enter_standout_mode));
         1190                                 mbsprint(menuitems[menuselected]->username, columns);
         1191                                 putp(tiparm(exit_standout_mode));
         1192                         }
         1193                         break;
         1194                 case ' ':
         1195                         selection = menuitems[menuselected];
         1196                 case 0x1b:
         1197                         goto endloop;
         1198                         break;
         1199                 }
         1200                 fflush(stdout);
         1201         }
         1202 
         1203 endloop:
         1204         screen = DungeonScreen;
         1205         drawscreen();
         1206 
         1207         return selection;
         1208 }
         1209 
         1210 Item *
         1211 interactitem(struct cell *c)
         1212 {
         1213         displayinfoline(map[py][px].tile->afterinteract);
         1214 
         1215         return map[py][px].items[0];
         1216 }
         1217 
         1218 Item *
         1219 interactmenu(struct cell *c)
         1220 {
         1221         Item *selection;
         1222 
         1223         if (selection = showmenu(map[py][px].tile->name, map[py][px].items, map[py][px].nitems))
         1224                 displayinfoline(map[py][px].tile->afterinteract);
         1225 
         1226         return selection;
         1227 }
         1228 
         1229 Item *
         1230 interact(Item *item)
         1231 {
         1232         if (map[py][px].tile->interact)
         1233                 return map[py][px].tile->interact(&map[py][px]);
         1234 
         1235         return NULL;
         1236 }
         1237 
         1238 void
         1239 describe(size_t x, size_t y, int verbose)
         1240 {
         1241         char *name;
         1242 
         1243         if (map[y][x].nitems) {
         1244                 if (*map[y][x].items[0]->username) {
         1245                         name = map[y][x].items[0]->username;
         1246                 } else {
         1247                         itemuri(map[y][x].items[0], bufout2, sizeof(bufout2));
         1248                         name = bufout2;
         1249                 }
         1250         } else {
         1251                 name = NULL;
         1252         }
         1253         if (map[y][x].tile->flags & Important || verbose)
         1254                 displayinfoline(map[y][x].tile->description, name);
         1255         else
         1256                 displayinfoline("");
         1257 }
         1258 
         1259 void
         1260 dungeondraw(void)
         1261 {
         1262         putp(tiparm(change_scroll_region, 0, lines-3));
         1263         putp(tiparm(clear_screen));
         1264 
         1265         rendermap();
         1266 
         1267         putp(tiparm(cursor_address, py - oy, px - ox));
         1268         putchar('@');
         1269         putp(tiparm(cursor_address, py - oy, px - ox));
         1270 
         1271         if (curentry->entry != curentry) {
         1272                 displaybar(curentry->username);
         1273         } else {
         1274                 itemuri(curentry, bufout, sizeof(bufout));
         1275                 displaybar(bufout);
         1276         }
         1277 
         1278         describe(px, py, 0);
         1279 }
         1280 
         1281 void
         1282 move(ssize_t dx, ssize_t dy)
         1283 {
         1284         ssize_t i;
         1285         size_t x, y;
         1286         size_t noy, nox;
         1287 
         1288         if ((ssize_t)py + dy >= MAPHEIGHT || (ssize_t)py + dy < 0)
         1289                 return;
         1290         if ((ssize_t)px + dx >= MAPWIDTH || (ssize_t)px + dx < 0)
         1291                 return;
         1292 
         1293         x = px + dx;
         1294         y = py + dy;
         1295 
         1296         if (map[y][x].tile->flags & Blocks)
         1297                 return;
         1298 
         1299         if (dx) {
         1300                 if (x < columns / 2 || MAPWIDTH <= columns)
         1301                         nox = 0;
         1302                 else if (x >= MAPWIDTH - columns / 2 - 1)
         1303                         nox = MAPWIDTH - columns;
         1304                 else
         1305                         nox = x - columns / 2;
         1306 
         1307                 if (ox != nox) {
         1308                         putp(tiparm(cursor_address, 0, 0));
         1309                         rendermap();
         1310                 } else {
         1311                         putp(tiparm(cursor_address, py - oy, px - ox));
         1312                         rendermapchar(py, px);
         1313                 }
         1314         } else if (dy) {
         1315                 putp(tiparm(cursor_address, py - oy, px - ox));
         1316                 rendermapchar(py, px);
         1317 
         1318                 if (y < maplines / 2 || MAPHEIGHT <= maplines) {
         1319                         noy = 0;
         1320                 } else if (y >= MAPHEIGHT - maplines / 2 - 1) {
         1321                         noy = MAPHEIGHT - maplines;
         1322                 } else {
         1323                         noy = y - maplines / 2;
         1324                 }
         1325 
         1326                 if (noy < oy) {
         1327                         putp(tiparm(cursor_address, 0, 0));
         1328                         for (i = (ssize_t)oy - 1; i >= (ssize_t)noy; i--) {
         1329                                 putp(tiparm(scroll_reverse));
         1330                                 rendermapline(i);
         1331                                 putp(tiparm(column_address, 0));
         1332                         }
         1333                 } else if (noy > oy) {
         1334                         putp(tiparm(cursor_address, lines-3, 0));
         1335                         for (i = oy + 1; i <= noy; i++) {
         1336                                 putp(tiparm(scroll_forward));
         1337                                 rendermapline(i + maplines - 1);
         1338                                 putp(tiparm(column_address, 0));
         1339                         }
         1340                 }
         1341                 oy = noy;
         1342         }
         1343 
         1344         py = y;
         1345         px = x;
         1346         putp(tiparm(cursor_address, py - oy, px - ox));
         1347         putchar('@');
         1348         putp(tiparm(cursor_address, py - oy, px - ox));
         1349 
         1350         describe(px, py, 0);
         1351 }
         1352 
         1353 void
         1354 drawscreen(void)
         1355 {
         1356         switch (screen) {
         1357         case DungeonScreen:
         1358                 dungeondraw();
         1359                 break;
         1360         case MenuScreen:
         1361                 menudraw();
         1362                 break;
         1363         }
         1364         fflush(stdout);
         1365 }
         1366 
         1367 void
         1368 uidisplay(Item *entry)
         1369 {
         1370         if (!entry || !(entry->type == '1' || entry->type == '+' || entry->type == '7'))
         1371                 return;
         1372 
         1373         if (entry != curentry) {
         1374                 generatemap(entry, curentry);
         1375                 curentry = entry;
         1376         }
         1377 
         1378         drawscreen();
         1379 }
         1380 
         1381 Item *
         1382 uiselectitem(Item *entry)
         1383 {
         1384         Item *e;
         1385 
         1386         if (!entry || !entry->dat)
         1387                 return NULL;
         1388 
         1389         for (;;) {
         1390                 switch (mygetchar()) {
         1391                 case 'h':
         1392                         move(-1, 0);
         1393                         break;
         1394                 case 'j':
         1395                         move(0, 1);
         1396                         break;
         1397                 case 'k':
         1398                         move(0, -1);
         1399                         break;
         1400                 case 'l':
         1401                         move(1, 0);
         1402                         break;
         1403                 case ' ':
         1404                         if (e = interact(entry))
         1405                                 return e;
         1406                         break;
         1407                 case 'q':
         1408                 case 0x1b:
         1409                         return NULL;
         1410                 }
         1411                 fflush(stdout);
         1412         }
         1413 }
         1414 
         1415 void
         1416 uisigwinch(int signal)
         1417 {
         1418         sigwinch = 1;
         1419 }