ui_rogue.c - brcon2024-hackathons - Bitreichcon 2024 Hackathons
 (HTM) git clone git://bitreich.org/brcon2024-hackathons git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/brcon2024-hackathons
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) Submodules
       ---
       ui_rogue.c (17863B)
       ---
            1 #include <stdarg.h>
            2 #include <stdint.h>
            3 #include <stdio.h>
            4 #include <stdlib.h>
            5 #include <string.h>
            6 #include <term.h>
            7 #include <termios.h>
            8 #include <unistd.h>
            9 #include <sys/types.h>
           10 
           11 #include "common.h"
           12 #include "config.h"
           13 
           14 #define C(c) #c
           15 #define S(c) C(c)
           16 
           17 /* ncurses doesn't define those in term.h, where they're used */
           18 #ifndef OK
           19 #define OK (0)
           20 #endif
           21 #ifndef ERR
           22 #define ERR (-1)
           23 #endif
           24 
           25 static struct termios tsave;
           26 static struct termios tsacc;
           27 static Item *curentry;
           28 static int termset = ERR;
           29 static char bufout[256];
           30 static char bufout2[256];
           31 
           32 void drawscreen(void);
           33 
           34 uint32_t
           35 fnv1a(int n,...)
           36 {
           37         int i;
           38         char *s;
           39         va_list l;
           40         uint32_t h;
           41 
           42         h = 0x811c9dc5;
           43 
           44         va_start(l, n); 
           45         for (i = 0; i < n; i++) {
           46                 for (s = va_arg(l, char*); *s; s++) {
           47                         h ^= *s;
           48                         h *= 0x01000193;
           49                 }
           50         }
           51         va_end(l);
           52 
           53         return h;
           54 }
           55 
           56 uint32_t
           57 xorshift(uint32_t *s)
           58 {
           59         *s ^= *s << 13;
           60         *s ^= *s >> 17;
           61         *s ^= *s << 5;
           62         return *s;
           63 }
           64 
           65 struct cell {
           66         char c;
           67         size_t nitems;
           68         Item **items;
           69 };
           70 
           71 #define MAPHEIGHT (25)
           72 #define MAPWIDTH (80)
           73 struct cell map[MAPHEIGHT][MAPWIDTH];
           74 
           75 struct room {
           76         struct room *p;
           77         void *d;
           78         size_t x, y;
           79         size_t w, h;
           80 };
           81 
           82 struct rect {
           83         struct rect *next, *next2;
           84         struct room *room;
           85         size_t x1, y1;
           86         size_t x2, y2;
           87         size_t d;
           88 };
           89 
           90 struct rect *
           91 randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng)
           92 {
           93         struct rect *r, *result;
           94         size_t n;
           95 
           96         n = 0;
           97         result = NULL;
           98         for (r = rs; r; r = r->next) {
           99                 if (r->y2 < x->y1 || r->y1 > x->y2 || r->x2 < x->x1 || r->x1 > x->x2)
          100                         continue;
          101                 if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r->x1 == x->x2))
          102                         continue;
          103                 n++;
          104                 if (xorshift(prng) / (1. + UINT32_MAX) < 1. / n)
          105                         result = r;
          106         }
          107 
          108         return result;
          109 }
          110 
          111 #define ROOM_HEIGHT_MIN 3
          112 #define ROOM_WIDTH_MIN 5
          113 #define ROOM_MARGIN_MIN 1
          114 #define CELL_HEIGHT_MIN (ROOM_HEIGHT_MIN + ROOM_MARGIN_MIN + 3)
          115 #define CELL_WIDTH_MIN (ROOM_WIDTH_MIN + ROOM_MARGIN_MIN + 3)
          116 size_t
          117 generaterooms_gnarf(uint32_t prng, struct room *rs, size_t l)
          118 {
          119         struct rect *queuehead, *queuetail;
          120         struct rect *r, *t;
          121         struct rect *rects, *walk;
          122         size_t w, h, i, j, rl, n;
          123         int vertical;
          124         struct room *room;
          125 
          126         r = malloc(sizeof(*r));
          127         r->x1 = r->y1 = ROOM_MARGIN_MIN;
          128         r->x2 = MAPWIDTH;
          129         r->y2 = MAPHEIGHT;
          130         r->d = 0;
          131 
          132         queuetail = r;
          133         queuetail->next = NULL;
          134         queuehead = r;
          135 
          136         rects = NULL;
          137         rl = 0;
          138 
          139         while (queuehead) {
          140                 r = queuehead;
          141                 if (queuetail == queuehead)
          142                         queuetail = NULL;
          143                 queuehead = queuehead->next;
          144 
          145                 if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2 && r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) {
          146                         vertical = xorshift(&prng) & 1;
          147                 } else if (r->x2 - r->x1 >= CELL_WIDTH_MIN * 2) {
          148                         vertical = 0;
          149                 } else if (r->y2 - r->y1 >= CELL_HEIGHT_MIN * 2) {
          150                         vertical = 1;
          151                 } else {
          152                         r->next = rects;
          153                         rects = r;
          154                         rl++;
          155                         continue;
          156                 }
          157 
          158                 if (vertical) {
          159                         w = r->x2 - r->x1;
          160                         h = CELL_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - CELL_HEIGHT_MIN * 2);
          161                 } else {
          162                         w = CELL_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - CELL_WIDTH_MIN * 2);
          163                         h = r->y2 - r->y1;
          164                 }
          165 
          166                 t = malloc(sizeof(*t));
          167                 t->x1 = r->x1;
          168                 t->y1 = r->y1;
          169                 t->x2 = r->x1 + w;
          170                 t->y2 = r->y1 + h;
          171                 t->d = r->d + 1;
          172                 t->next = NULL;
          173                 t->room = NULL;
          174 
          175                 if (!queuetail) {
          176                         queuehead = t;
          177                         queuetail = t;
          178                 } else {
          179                         queuetail->next = t;
          180                         queuetail = t;
          181                 }
          182 
          183                 t = malloc(sizeof(*t));
          184                 if (vertical) {
          185                         t->x1 = r->x1;
          186                         t->y1 = r->y1 + h;
          187                 } else {
          188                         t->x1 = r->x1 + w;
          189                         t->y1 = r->y1;
          190                 }
          191                 t->x2 = r->x2;
          192                 t->y2 = r->y2;
          193                 t->d = r->d + 1;
          194                 t->next = NULL;
          195                 t->room = NULL;
          196 
          197                 queuetail->next = t;
          198                 queuetail = t;
          199 
          200                 free(r);
          201         }
          202 
          203         if (l > rl)
          204                 l = rl;
          205 
          206         for (r = rects; r; r = r->next) {
          207                 if (MAPHEIGHT / 2 >= r->y1 && MAPHEIGHT / 2 < r->y2 &&
          208                     MAPWIDTH / 2 >= r->x1 && MAPWIDTH / 2 < r->x2)
          209                         break;
          210         }
          211         
          212         i = 0;
          213         rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_MIN - ROOM_WIDTH_MIN);
          214         rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN);
          215         rs[i].x = r->x1 + xorshift(&prng) % (1 + r->x2 - r->x1 - ROOM_MARGIN_MIN - rs[i].w);
          216         rs[i].y = r->y1 + xorshift(&prng) % (1 + r->y2 - r->y1 - ROOM_MARGIN_MIN - rs[i].h);
          217         rs[i].p = NULL;
          218         r->room = &rs[i];
          219 
          220         walk = r;
          221         walk->next2 = NULL;
          222 
          223         i++;
          224         for (; i < l;) {
          225                 t = randomneighbor(r, rects, &prng);
          226                 if (!t || t->room) {
          227                         n = 0;
          228                         for (t = walk; t; t = t->next2) {
          229                                 n++;
          230                                 if (xorshift(&prng) / (1. + UINT32_MAX) < 1. / n)
          231                                         r = t;
          232                                 
          233                         }
          234                         continue;
          235                 }
          236                 rs[i].w = ROOM_WIDTH_MIN + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - ROOM_WIDTH_MIN);
          237                 rs[i].h = ROOM_HEIGHT_MIN + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - ROOM_HEIGHT_MIN);
          238                 rs[i].x = t->x1 + xorshift(&prng) % (1 + t->x2 - t->x1 - ROOM_MARGIN_MIN - rs[i].w);
          239                 rs[i].y = t->y1 + xorshift(&prng) % (1 + t->y2 - t->y1 - ROOM_MARGIN_MIN - rs[i].h);
          240                 rs[i].p = r->room;
          241                 t->room = &rs[i];
          242                 i++;
          243                 r = t;
          244                 r->next2 = walk;
          245                 walk = r;
          246         }
          247 
          248         for (r = rects; r;) {
          249                 t = r->next;
          250                 free(r);
          251                 r = t;
          252         }
          253 
          254         return l;
          255 }
          256 
          257 size_t
          258 distance(size_t x1, size_t y1, size_t x2, size_t y2)
          259 {
          260         size_t d;
          261 
          262         if (y1 < y2)
          263                 d = y2 - y1;
          264         else
          265                 d = y1 - y2;
          266         if (x1 < x2)
          267                 d += x2 - x1;
          268         else
          269                 d += x1 - x2;
          270 
          271         return d;
          272 }
          273 
          274 void
          275 nearestpoints(struct room *a, struct room *b, size_t *ax, size_t *ay, size_t *bx, size_t *by)
          276 {
          277         if (a->y >= b->y && a->y < b->y + b->h) {
          278                 *ay = *by = a->y;
          279         } else if (b->y >= a->y && b->y < a->y + a->h) {
          280                 *ay = *by = b->y;
          281         } else if (a->y >= b->y) {
          282                 *ay = a->y;
          283                 *by = b->y + b->h - 1;
          284         } else if (b->y >= a->y) {
          285                 *ay = a->y + a->h - 1;
          286                 *by = b->y;
          287         }
          288 
          289         if (a->x >= b->x && a->x < b->x + b->w) {
          290                 *ax = *bx = a->x;
          291         } else if (b->x >= a->x && b->x < a->x + a->w) {
          292                 *ax = *bx = b->x;
          293         } else if (a->x >= b->x) {
          294                 *ax = a->x;
          295                 *bx = b->x + b->w - 1;
          296         } else if (b->x >= a->x) {
          297                 *ax = a->x + a->w - 1;
          298                 *bx = b->x;
          299         }
          300 }
          301 
          302 void
          303 connectrooms(struct room *a, struct room *b)
          304 {
          305         size_t i, j;
          306         ssize_t ii;
          307         size_t x1, y1;
          308         size_t x2, y2;
          309 
          310         nearestpoints(a, b, &x1, &y1, &x2, &y2);
          311 
          312         if (y1 > y2) {
          313                 ii = -1;
          314         } else if (y2 > y1) {
          315                 ii = 1;
          316         } else {
          317                 ii = 0;
          318         }
          319 
          320 /*
          321 printf("%lu\t%lu\t%d\n", y1, y2, ii);
          322 */
          323         for (i = y1; i != y2; i += ii)
          324                 map[i][x1].c = '.';
          325 
          326         if (x1 > x2) {
          327                 ii = -1;
          328         } else if (x2 > x1) {
          329                 ii = 1;
          330         } else {
          331                 ii = 0;
          332         }
          333 
          334         for (i = x1; i != x2; i += ii)
          335                 map[y2][i].c = '.';
          336 }
          337 
          338 void
          339 rendermap(void)
          340 {
          341         size_t i, j;
          342 
          343         for (i = 0; i < MAPHEIGHT; i++) {
          344                 for (j = 0; j < MAPWIDTH; j++)
          345                         putchar(map[i][j].c);
          346                 putchar('\n');
          347         }
          348 }
          349 
          350 size_t
          351 placeitems_hash(Item *item, size_t *assocs, size_t k)
          352 {
          353         Dir *dir;
          354         Item *citem;
          355         size_t i;
          356 
          357         dir = item->dat;
          358         for (i = 0; i < dir->nitems; i++) {
          359                 citem = &dir->items[i];
          360                 /* TODO Somewhere else */
          361                 if (!citem->host || !citem->port || !citem->selector)
          362                         continue;
          363                 assocs[i] = fnv1a(6, item->host, item->port, item->selector, citem->host, citem->port, citem->selector) % k;
          364         }
          365 
          366         return k;
          367 }
          368 
          369 #define POSITIONS_LENGTH 4
          370 enum {
          371         Portal,
          372         StaircaseDown,
          373         Bookshelf,
          374         Back
          375 };
          376 
          377 size_t px, py;
          378 
          379 void
          380 generatemap(Item *item, int new)
          381 {
          382         Dir *dir;
          383         Item *citem;
          384         size_t i, j, k, l, ir;
          385         size_t x, y;
          386         ssize_t n, m;
          387         size_t *cassocs;
          388         struct room *rooms, *r;
          389         struct {
          390                 unsigned char x, y;
          391         } positions[POSITIONS_LENGTH];
          392         uint32_t prng;
          393         char buffer[3];
          394 
          395         for (i = 0; i < MAPHEIGHT; i++) {
          396                 for (j = 0; j < MAPWIDTH; j++) {
          397                         map[i][j].c = '#';
          398                         free(map[i][j].items);
          399                         map[i][j].items = NULL;
          400                         map[i][j].nitems = 0;
          401                 }
          402         }
          403 
          404         dir = item->dat;
          405         for (j = l = 0; j < dir->nitems; j++) {
          406                 if (dir->items[j].type == '0' ||
          407                     dir->items[j].type == '1')
          408                         l++;
          409         }
          410 
          411         k = 1 + l / 10;
          412         rooms = calloc(k, sizeof(*rooms));
          413         if (!rooms)
          414                 return;
          415         k = generaterooms_gnarf(fnv1a(3, item->host, item->port, item->selector), rooms, k);
          416 
          417         cassocs = calloc(dir->nitems, sizeof(*cassocs));
          418         if (!cassocs)
          419                 goto cleanup;
          420 
          421         k = placeitems_hash(item, cassocs, k);
          422 
          423         /* Insert rooms */
          424         for (i = 0; i < k; i++) {
          425                 for (y = rooms[i].y; y < rooms[i].y + rooms[i].h; y++) {
          426                         for (x = rooms[i].x; x < rooms[i].x + rooms[i].w; x++)
          427                                 map[y][x].c = '.';
          428                 } 
          429         }
          430 
          431         /* Insert connections */
          432         for (i = 0; i < k; i++) {
          433                 if (rooms[i].p)
          434                         connectrooms(&rooms[i], rooms[i].p);
          435         }
          436 
          437         /*
          438                 Insert items
          439                 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.
          440         */
          441         ir = fnv1a(4, item->host, item->port, item->selector, "initial_room") % k;
          442 
          443         for (i = 0; i < k; i++) {
          444                 snprintf(buffer, sizeof(buffer), "%d", i);
          445                 prng = fnv1a(4, item->host, item->port, item->selector, buffer);
          446                 for (j = 0, n = 0, m = rooms[i].h * rooms[i].w; j < m; j++) {
          447                         if ((m - j) * (xorshift(&prng) / (double)UINT32_MAX) < POSITIONS_LENGTH - n) {
          448                                 positions[n].x = rooms[i].x + j % rooms[i].w;
          449                                 positions[n].y = rooms[i].y + j / rooms[i].w;
          450                                 n++;
          451                         }
          452                         if (n == POSITIONS_LENGTH)
          453                                 break;
          454                 }
          455                 for (j = 0; j < dir->nitems; j++) {
          456                         if (cassocs[j] != i)
          457                                 continue;
          458 
          459                         citem = &dir->items[j];
          460                         switch (citem->type) {
          461                         case '0':
          462                                 x = positions[Bookshelf].x;
          463                                 y = positions[Bookshelf].y;
          464                                 if (map[y][x].nitems)
          465                                         map[y][x].c = 'E';
          466                                 else
          467                                         map[y][x].c = '?';
          468                                 break;
          469                         case '1':
          470                                 if (strcmp(citem->host, item->host) || strcmp(citem->port, item->port)) {
          471                                         x = positions[Portal].x;
          472                                         y = positions[Portal].y;
          473                                         if (map[y][x].nitems)
          474                                                 map[y][x].c = 'O';
          475                                         else
          476                                                 map[y][x].c = '0';
          477                                 } else {
          478                                         x = positions[StaircaseDown].x;
          479                                         y = positions[StaircaseDown].y;
          480                                         if (map[y][x].nitems)
          481                                                 map[y][x].c = 'L';
          482                                         else
          483                                                 map[y][x].c = '>';
          484                                 }
          485                                 break;
          486                         default:
          487                                 continue;
          488                         }
          489                         map[y][x].nitems++;
          490                         map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
          491                         map[y][x].items[map[y][x].nitems-1] = citem;
          492 
          493                         if (new && j == dir->curline && citem->raw) {
          494                                 px = x;
          495                                 py = y;
          496                         }
          497                 }
          498 
          499                 if (i == ir && item->entry != item) {
          500                         y = positions[Back].y;
          501                         x = positions[Back].x;
          502                         if (strcmp(item->entry->host, item->host) || strcmp(item->entry->port, item->port))
          503                                 map[y][x].c = '0';
          504                         else
          505                                 map[y][x].c = '<';
          506                         map[y][x].nitems++;
          507                         map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
          508                         map[y][x].items[map[y][x].nitems-1] = item->entry;
          509                 }
          510 
          511                 if (i == ir && new && !dir->items[dir->curline].raw) {
          512                         px = positions[Back].x;
          513                         py = positions[Back].y;
          514                 }
          515         }
          516         free(cassocs);
          517 
          518 cleanup:
          519         free(rooms);
          520 }
          521 
          522 void
          523 uisetup(void)
          524 {
          525         tcgetattr(0, &tsave);
          526         tsacc = tsave;
          527         tsacc.c_lflag &= ~(ECHO|ICANON);
          528         tsacc.c_cc[VMIN] = 1;
          529         tsacc.c_cc[VTIME] = 0;
          530         tcsetattr(0, TCSANOW, &tsacc);
          531 
          532         if (termset != OK)
          533                 /* setupterm call exits on error */
          534                 termset = setupterm(NULL, 1, NULL);
          535         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          536         fflush(stdout);
          537 }
          538 
          539 void
          540 uicleanup(void)
          541 {
          542         tcsetattr(0, TCSANOW, &tsave);
          543 
          544         if (termset != OK)
          545                 return;
          546 
          547         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          548         fflush(stdout);
          549 }
          550 
          551 char *
          552 uiprompt(char *fmt, ...)
          553 {
          554         va_list ap;
          555         char *input = NULL;
          556         size_t n;
          557         ssize_t r;
          558 
          559         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          560 
          561         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          562         putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          563 
          564         va_start(ap, fmt);
          565         vsnprintf(bufout, sizeof(bufout), fmt, ap);
          566         va_end(ap);
          567 
          568         n = mbsprint(bufout, columns);
          569 
          570         putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          571 
          572         putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0));
          573 
          574         tsacc.c_lflag |= (ECHO|ICANON);
          575         tcsetattr(0, TCSANOW, &tsacc);
          576         fflush(stdout);
          577 
          578         n = 0;
          579         r = getline(&input, &n, stdin);
          580 
          581         tsacc.c_lflag &= ~(ECHO|ICANON);
          582         tcsetattr(0, TCSANOW, &tsacc);
          583         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          584         fflush(stdout);
          585 
          586         if (r == -1) {
          587                 clearerr(stdin);
          588                 clear(&input);
          589         } else if (input[r - 1] == '\n') {
          590                 input[--r] = '\0';
          591         }
          592 
          593         return input;
          594 }
          595 
          596 void
          597 displaybar(char *s) {
          598         size_t n;
          599 
          600         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          601 
          602         putp(tparm(cursor_address, lines-2, 0, 0, 0, 0, 0, 0, 0, 0));
          603         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          604 
          605         n = mbsprint(s, columns);
          606         for (n = columns - n; n; n--)
          607                 putchar(' ');
          608 
          609         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          610 
          611         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          612         fflush(stdout);
          613 }
          614 
          615 void
          616 vdisplayinfoline(char *fmt, va_list ap)
          617 {
          618         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          619 
          620         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          621 
          622         vsnprintf(bufout, sizeof(bufout), fmt, ap);
          623 
          624         mbsprint(bufout, columns);
          625 
          626         putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          627 
          628         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          629         fflush(stdout);
          630 }
          631 
          632 void
          633 uistatus(char *fmt, ...)
          634 {
          635         va_list ap;
          636         size_t n;
          637 
          638         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          639 
          640         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          641 
          642         va_start(ap, fmt);
          643         n = vsnprintf(bufout, sizeof(bufout), fmt, ap);
          644         va_end(ap);
          645 
          646         if (n < sizeof(bufout)-1) {
          647                 snprintf(bufout+n, sizeof(bufout)-n,
          648                          " [Press a key to continue \xe2\x98\x83]");
          649         }
          650 
          651         mbsprint(bufout, columns);
          652 
          653         putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          654 
          655         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          656         fflush(stdout);
          657 
          658         getchar();
          659 }
          660 
          661 void
          662 displayinfoline(char *fmt, ...)
          663 {
          664         va_list ap;
          665 
          666         va_start(ap, fmt);
          667         vdisplayinfoline(fmt, ap);
          668         va_end(ap);
          669 }
          670 
          671 Item *
          672 showmenu(char *title, Item **item, size_t l)
          673 {
          674         size_t i;
          675 
          676         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          677 
          678         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          679         printf("%s\n", title);
          680         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          681         for (i = 0; i < l; i++)
          682                 printf("%lu\t%s\n", i, item[i]->username);
          683 
          684         if (!scanf("%lu", &i) || i >= l)
          685                 return NULL;
          686         fflush(stdout);
          687 
          688         return item[i];
          689 }
          690 
          691 Item *
          692 prompt(char *text, Item *item)
          693 {
          694         displayinfoline(text, item->username);
          695         getchar();
          696         return item;
          697 }
          698 
          699 Item *
          700 interact(Item *item)
          701 {
          702         Item *selection;
          703 
          704         selection = NULL;
          705 
          706         switch (map[py][px].c) {
          707         case '?':
          708         case '0':
          709         case '>':
          710         case '<':
          711                 selection = map[py][px].items[0];
          712                 break;
          713         case 'E':
          714                 selection = showmenu("Bookshelf", map[py][px].items, map[py][px].nitems);
          715                 break;
          716         case 'O':
          717                 selection = showmenu("Portal machine", map[py][px].items, map[py][px].nitems);
          718                 break;
          719         case 'L':
          720                 selection = showmenu("Elevator", map[py][px].items, map[py][px].nitems);
          721                 break;
          722         }
          723 
          724         drawscreen();
          725 
          726         if (selection) {
          727                 switch (map[py][px].c) {
          728                 case '?':
          729                 case 'E':
          730                         displayinfoline("A loading bar?! In a book?!");
          731                         break;
          732                 case 'O':
          733                 case '0':
          734                         displayinfoline("You are getting transported through time and space.");
          735                         break;
          736                 case 'L':
          737                         displayinfoline("You hear elevator music...");
          738                         break;
          739                 case '<':
          740                 case '>':
          741                         displayinfoline("Too many stairs...");
          742                         break;
          743                 }
          744         }
          745 
          746         return selection;
          747 }
          748 
          749 void
          750 describe(size_t x, size_t y, int verbose)
          751 {
          752         switch (map[y][x].c) {
          753         case 'E':
          754                 displayinfoline("A bookshelf.");
          755                 break;
          756         case 'O':
          757                 displayinfoline("A portal machine.");
          758                 break;
          759         case 'L':
          760                 displayinfoline("An elevator.");
          761                 break;
          762         case '?':
          763         case '>':
          764         case '<':
          765         case '0':
          766                 if (*map[y][x].items[0]->username) {
          767                         displayinfoline("'%s'.", map[y][x].items[0]->username);
          768                 } else {
          769                         itemuri(map[y][x].items[0], bufout2, sizeof(bufout2));
          770                         displayinfoline("'%s'.", bufout2);
          771                 }
          772                 break;
          773         default:
          774                 if (verbose) {
          775                         switch (map[y][x].c) {
          776                         case '.':
          777                                 displayinfoline("Floor.");
          778                                 break;
          779                         case '#':
          780                                 displayinfoline("Wall.");
          781                                 break;
          782                         }
          783                 } else {
          784                         displayinfoline("");
          785                 }
          786                 break;
          787         }
          788 }
          789 
          790 void
          791 move(ssize_t dx, ssize_t dy)
          792 {
          793         size_t x, y;
          794 
          795         /* allow wraparound of the world for the lulz, even if it's not happening */
          796         y = (MAPHEIGHT + py + dy) % MAPHEIGHT;
          797         x = (MAPWIDTH + px + dx) % MAPWIDTH;
          798 
          799         if (map[y][x].c == '#')
          800                 return;
          801 
          802         putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 ));
          803         putchar(map[py][px].c);
          804 
          805         py = y;
          806         px = x;
          807         putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 ));
          808         putchar('@');
          809         putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 ));
          810 
          811         describe(x, y, 0);
          812         putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0 ));
          813 }
          814 
          815 void
          816 drawscreen(void)
          817 {
          818         Dir *dir;
          819 
          820         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          821         rendermap();
          822 
          823         if (curentry->entry != curentry && (dir = curentry->entry->dat)) {
          824                 displaybar(dir->items[dir->curline].username);
          825         } else {
          826                 itemuri(curentry, bufout, sizeof(bufout));
          827                 displaybar(bufout);
          828         }
          829 
          830         move(0, 0);
          831 
          832 }
          833 
          834 void
          835 uidisplay(Item *entry)
          836 {
          837         if (!entry || entry->type != '1')
          838                 return;
          839 
          840         generatemap(entry, curentry != entry);
          841 
          842         curentry = entry;
          843         drawscreen();
          844 }
          845 
          846 void
          847 lookmode(void)
          848 {
          849         size_t x, y;
          850 
          851         x = px;
          852         y = py;
          853 
          854         for (;;) {
          855                 switch (getchar()) {
          856                 case 0x1B:
          857                 case 'q':
          858                         putp(tparm(cursor_address, py, px, 0, 0, 0, 0, 0, 0, 0));
          859                         return;
          860                 case 'h':
          861                         x = (MAPWIDTH + x - 1) % MAPWIDTH;
          862                         break;
          863                 case 'j':
          864                         y = (y + 1) % MAPHEIGHT;
          865                         break;
          866                 case 'k':
          867                         y = (MAPHEIGHT + y - 1) % MAPHEIGHT;
          868                         break;
          869                 case 'l':
          870                         x = (x + 1) % MAPWIDTH;
          871                         break;
          872                 }
          873                 putp(tparm(cursor_address, y, x, 0, 0, 0, 0, 0, 0, 0));
          874                 describe(x, y, 1);
          875         }
          876 }
          877 
          878 Item *
          879 uiselectitem(Item *entry)
          880 {
          881         Dir *dir;
          882         Item *e;
          883         size_t i;
          884 
          885         if (!entry || !(dir = entry->dat))
          886                 return NULL;
          887 
          888         for (;;) {
          889                 switch (getchar()) {
          890                 case 'h':
          891                         move(-1, 0);
          892                         break;
          893                 case 'j':
          894                         move(0, 1);
          895                         break;
          896                 case 'k':
          897                         move(0, -1);
          898                         break;
          899                 case 'l':
          900                         move(1, 0);
          901                         break;
          902                 case 'L':
          903                         lookmode();
          904                         break;
          905                 case ' ':
          906                         /* Portals, stairs, bookshelfs */
          907                         if (e = interact(entry)) {
          908                                 if (e->type == '1') {
          909                                         for (i = 0; i < dir->nitems; i++) {
          910                                                 if (e == &dir->items[i]) {
          911                                                         dir->curline = i;
          912                                                         break;
          913                                                 }
          914                                         }
          915                                 }
          916                                 return e;
          917                         }
          918                         break;
          919                 case 'q':
          920                         return NULL;
          921                 }
          922         }
          923 }
          924 
          925 void
          926 uisigwinch(int signal)
          927 {
          928         Dir *dir;
          929 
          930         if (termset == OK)
          931                 del_curterm(cur_term);
          932         termset = setupterm(NULL, 1, NULL);
          933 
          934         if (!curentry || !(dir = curentry->dat))
          935                 return;
          936 
          937         uidisplay(curentry);
          938 }