#include #define NCURSES_WIDECHAR 1 #include #include #include #include #include #include #include #include #include #include static PANEL *stdpanel = NULL; static WINDOW *logbox = NULL; static WINDOW *logwin = NULL; static WINDOW *charbox = NULL; static WINDOW *charwin = NULL; static WINDOW *mapbox = NULL; static WINDOW *mapwin = NULL; static bool initialized = false; static bool setup_log(struct winsize w) { int ystart = 0; int xstart = w.ws_col/2+1; int ysize = w.ws_row; int xsize = w.ws_col/2-1; logbox = subwin(stdscr, ysize, xsize, ystart, xstart); if(!logbox) return false; box(logbox,'|', '-'); wmove(logbox, 0,1); wprintw(logbox, "Log"); logwin = subwin(logbox, ysize-2, xsize-2, ystart+1, xstart+1); if(!logwin) return false; // scrollok(logwin, true); return true; } static bool setup_char(struct winsize w) { int ystart = 0; int xstart = 0; int ysize = w.ws_row/3; int xsize = w.ws_col/2; charbox = subwin(stdscr, ysize, xsize, ystart, xstart); if(!charbox) return false; box(charbox,'|', '-'); wmove(charbox, 0,1); wprintw(charbox, "Character"); charwin = subwin(charbox, ysize-2, xsize-2, ystart+1, xstart+1); if(!charwin) return false; return true; } static bool setup_map(struct winsize w) { int ystart = w.ws_row/3+1; int xstart = 0; int ysize = w.ws_row-w.ws_row/3-1; int xsize = w.ws_col/2; mapbox = subwin(stdscr, ysize, xsize, ystart, xstart); if(!mapbox) return false; box(mapbox,'|', '-'); wmove(mapbox, 0,1); wprintw(mapbox, "Map"); mapwin = subwin(mapbox, ysize-2, xsize-2, ystart+1, xstart+1); if(!mapwin) return false; return true; } void quit(void) { endwin(); } bool setup() { if(initialized) { erase(); if(logwin != NULL) delwin(logwin); if(logbox != NULL) delwin(logbox); if(charwin != NULL) delwin(charwin); if(charbox != NULL) delwin(charbox); if(mapwin != NULL) delwin(mapwin); if(mapbox != NULL) delwin(mapbox); } else { atexit(quit); setlocale(LC_ALL,""); stdscr = initscr(); stdpanel = new_panel(stdscr); assert(stdpanel != NULL); noecho(); cbreak(); curs_set(0); keypad(stdscr, true); initialized = true; } struct winsize w; assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1); if(w.ws_col < 25 || w.ws_row < 10) { endwin(); puts("Terminal too small"); abort(); } if(!setup_char(w)) { fprintf(stderr, "char\n"); return false; } if(!setup_log(w)) { fprintf(stderr, "log\n"); return false; } if(!setup_map(w)) { fprintf(stderr, "map\n"); return false; } update_panels(); doupdate(); return true; } size_t logindex = 0; size_t loglength; char **logs = NULL; void printlog() //TODO: fix logs if they go multiline { werase(logwin); wmove(logwin, 0, 0); int ymax = getmaxy(logwin); for(int i = 0; i < ymax && i+logindex < loglength; i++) { waddstr(logwin, logs[i+logindex]); waddstr(logwin, "\n"); } wrefresh(logwin); } void logstr(const char *format, ...) { static size_t capacity; if(logs == NULL) { logs = xmalloc(8*sizeof(*logs)); capacity = 8; loglength = 0; } if(capacity == loglength++) { logs = xrealloc(logs, (capacity*=2) * sizeof(*logs)); } va_list ap; va_start(ap, format); va_list ap2; va_copy(ap2, ap); int bufsz = vsnprintf(NULL, 0, format, ap); va_end(ap); struct tm mytime = *localtime(&(time_t){time(NULL)}); logs[loglength-1] = xmalloc(bufsz+11+2); //11 == size needed for the time header; strftime(logs[loglength-1], 11, "%T> ", &mytime); vsprintf(logs[loglength-1]+10, format, ap2); va_end(ap2); if(loglength - logindex == getmaxy(logwin)) logindex++; printlog(); } void charclear() { werase(charwin); wrefresh(charwin); } void charprint(const char* format, ...) { va_list ap; va_start(ap, format); vw_printw(charwin, format, ap); wprintw(charwin, "\n"); wrefresh(charwin); va_end(ap); } void log_scroll(enum direction d) { if(d == UP && logindex != 0) logindex--; else if(d == DOWN && logindex != loglength-1) logindex++; printlog(); wrefresh(logwin); } void draw_map(const struct game *g, struct point p) { int x, y; getmaxyx(mapwin, y, x); size_t starty = MAX(0, p.y-y/2); size_t endy = MIN(starty + y, g->ys); size_t startx = MAX(0, p.x-x/2); size_t endx = MIN(startx + x, g->xs); werase(mapwin); int line = 0; for(size_t i = starty; i < endy; i++) { wmove(mapwin, line++, 0); for(size_t j = startx; j < endx; j++) { waddch(mapwin, game_at(g, (struct point){.y = i, .x = j})->symbol); } } wrefresh(mapwin); } bool messagebox(const char *str, int y, int x) { int yorig = y, xorig = x; struct winsize w; assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1); const char *returnstr = "Press return to continue"; x = MAX(strlen(returnstr), x)+2; y += 4; if(y > w.ws_row || x > w.ws_col) return false; int ystart = w.ws_row/2-y/2; int xstart = w.ws_col/2-x/2; WINDOW *msgbox = newwin(y, x, ystart, xstart); PANEL *msgboxpanel = new_panel(msgbox); box(msgbox, '|', '-'); WINDOW *msgwin = newwin(y-2, x-2, ystart+1, xstart+1); PANEL *msgwinpanel = new_panel(msgwin); waddstr(msgwin, str); waddstr(msgwin, "\n\n"); waddstr(msgwin, returnstr); update_panels(); doupdate(); int c; while((c = getch()) != '\n' && c != KEY_RESIZE); del_panel(msgwinpanel); del_panel(msgboxpanel); delwin(msgbox); delwin(msgwin); update_panels(); doupdate(); if(c == KEY_RESIZE) { while(!setup()); return messagebox(str, yorig, xorig); } printlog(); //incase we resized return true; } void itemselect(struct monster *m, itemselectfn f) { WINDOW *menu = newwin(0, 0, 0, 0); PANEL *menupanel = new_panel(menu); size_t i = 0; //we put the index before the label so we keep it between redraws redraw:; struct winsize w; assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1); for(size_t i = 0; i < m->items.size; i++) { struct item *it = &darray_item(m->items, i); if(it->type != NOITEM) { if(it->type == EQUIP) { mvwprintw(menu, i, 0, "%.*s (%c)", w.ws_col, it->name, it->u.e.isequipped ? '*' : ' '); } else { mvwprintw(menu, i, 0, "%.*s", w.ws_col, it->name); } } else { mvwaddstr(menu, i, 0, "EMPTY"); } } mvwchgat(menu, i, 0, -1, A_REVERSE, 0, NULL); update_panels(); doupdate(); int c; while((c = getch()) != 'i' && c != 'q') { switch(c) { case ' ': if(f(m, i)) i = 0; werase(menu); goto redraw; //items might have changed, so we redraw the list break; case 's': if(i != (m->items.size == 0 ? 0 :m->items.size-1)) { wchgat(menu , -1, A_NORMAL, 0, NULL); mvwchgat(menu, ++i, 0, -1, A_REVERSE, 0, NULL); } break; case 'w': if(i != 0) { wchgat(menu, -1, A_NORMAL, 0, NULL); mvwchgat(menu, --i, 0, -1, A_REVERSE, 0, NULL); } break; case KEY_RESIZE: werase(menu); goto redraw; break; } update_panels(); doupdate(); } del_panel(menupanel); delwin(menu); update_panels(); doupdate(); }