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 }