#include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <keyboard.h>#include <mouse.h> #include "map.h"#include "bt.h" #include "dat.h"#include "alg.h" static int gameover = 0;static Font *smallfont; enum{ CGREEN, CYELLOW, CORANGE, CRED, CGREY, CMAGENTA, CEND,}; typedef struct KeyMenu KeyMenu;struct KeyMenu{ /* label printed with the menu */ char *label; /* gen is called until it returns Runemax. */ Rune (*gen)(int, char*, int);}; static int menu(Keyboardctl *kc, Mousectl *mc, KeyMenu *km);static int uienter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr); typedef struct Msg Msg;struct Msg{ char msg[256]; ulong color; Msg *next;}; typedef struct UI UI;struct UI{ /* current ui size */ int uisz; /* colors */ Image **cols; /* mouse, keyboard */ Mousectl *mc; Keyboardctl *kc; /* log chan */ Channel *msgc; /* linked list of current msgs */ Msg *msg; int nmsg; /* tileset */ Tileset *tiles; /* level view rectangle, in tiles */ Rectangle viewr; /* ui rectangle, in pixels */ Rectangle uir; Point camp; /* timer tick, see /^tickproc */ Channel *tickcmd; Channel *tick; /* should auto-move? */ int autoidle; /* show far away messages? */ int farmsg;} ui; static voidtickproc(void *v){ USED(v); ulong time; time = 0; enum { ATIME, ATICK, AEND }; Alt a[AEND+1] = { { ui.tickcmd, &time, CHANRCV }, { ui.tick, &time, CHANEND }, { nil, nil, CHANEND }, }; for(;;){ switch(alt(a)){ case ATIME: if(time == 0) a[ATICK].op = CHANEND; else a[ATICK].op = CHANSND; break; case ATICK: sleep(time); break; } }} voiduiinit(char *name){ int tx, ty; char *tileset, *tmp; tx = ty = 0; if(initdraw(nil, nil, name) < 0) sysfatal("initdraw: %r"); if((smallfont = openfont(display, "/lib/font/bit/fixed/unicode.4x6.font")) == nil) sysfatal("openfont: %r"); if((ui.mc = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((ui.kc = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); if((ui.tickcmd = chancreate(sizeof(ulong), 0)) == nil) sysfatal("chancreate: %r"); if((ui.tick = chancreate(sizeof(ulong), 0)) == nil) sysfatal("chancreate: %r"); proccreate(tickproc, nil, 4096); if((ui.msgc = chancreate(sizeof(Msg*), 1000)) == nil) sysfatal("chancreate: %r"); tileset = getenv("tileset"); if(tileset != nil){ if((tmp = getenv("tilesetx")) == nil || (tx = atoi(tmp)) == 0) sysfatal("zero tilesetx"); free(tmp); if((tmp = getenv("tilesety")) == nil || (ty = atoi(tmp)) == 0) sysfatal("zero tilesety"); free(tmp); } else { if(access("nethack.32x32", AREAD) == 0) tileset = strdup("nethack.32x32"); else tileset = strdup("/lib/hack9/nethack.32x32"); tx = 32; ty = 32; } if((ui.tiles = opentile(tileset, tx, ty)) == nil) sysfatal("opentile %s @ %dx%d: %r", tileset, tx, ty); free(tileset); if((ui.cols = mallocz(CEND * sizeof(Image*), 1)) == nil) sysfatal("malloc cols: %r"); ui.cols[CGREEN] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen); ui.cols[CYELLOW] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellow); ui.cols[CORANGE] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFA500FF); ui.cols[CRED] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed); ui.cols[CGREY] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF); ui.cols[CMAGENTA] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMagenta); ui.uisz = 6;} static voidtmsg(void *v){ Msg *l; l = v; while(nbsendp(ui.msgc, l) == 0) yield();} static voiduimsg(uint color, char *fmt, va_list arg){ Msg *l; l = mallocz(sizeof(Msg), 1); assert(l != nil); l->color = color; vsnprint(l->msg, sizeof(l->msg)-1, fmt, arg); if(nbsendp(ui.msgc, l) == 0) threadcreate(tmsg, l, 4096);} voidmsg(char *fmt, ...){ va_list arg; va_start(arg, fmt); uimsg(CGREY, fmt, arg); va_end(arg);} voidgood(char *fmt, ...){ va_list arg; va_start(arg, fmt); uimsg(CGREEN, fmt, arg); va_end(arg);} voidwarn(char *fmt, ...){ va_list arg; va_start(arg, fmt); uimsg(CYELLOW, fmt, arg); va_end(arg);} voidbad(char *fmt, ...){ va_list arg; va_start(arg, fmt); uimsg(CRED, fmt, arg); va_end(arg);} voiddbg(char *fmt, ...){ va_list arg; char tfmt[256]; if(debug < 1) return; snprint(tfmt, sizeof(tfmt), "T:%ld %s", turn, fmt); va_start(arg, fmt); uimsg(CMAGENTA, tfmt, arg); va_end(arg);} static Image*hpcolor(int hp, int maxhp){ Image *c; if(hp > (maxhp/4)*3) c = ui.cols[CGREEN]; else if(hp > maxhp/2) c = ui.cols[CYELLOW]; else if(hp > maxhp/4) c = ui.cols[CORANGE]; else c = ui.cols[CRED]; return c;} /* draw the whole level on the screen using tileset ts. * r.min is the upper left visible tile, and r.max is * the bottom right visible tile. * */static voiddrawlevel(Level *l, Tileset *ts, Rectangle r){ int what; double hp, maxhp; char buf[16]; Point p, p2, sp, bot; Image *c; Tile *t; Monster *m; bot = (Point){0, ts->height - smallfont->height}; for(p.x = r.min.x; p.x < r.max.x; p.x++){ for(p.y = r.min.y; p.y < r.max.y; p.y++){ /* screen space */ sp = p; sp = addpt(sp, ui.camp); sp.x *= ts->width; sp.y *= ts->height; sp = addpt(sp, screen->r.min); t = tileat(l, p); if(t){ what = t->terrain; if(t->unit) what = t->unit; else if(t->feat) what = t->feat; drawtile(ts, screen, sp, what); if(t->monst != nil){ m = t->monst; hp = m->hp, maxhp = m->maxhp; snprint(buf, sizeof(buf), "%d/%d HP", (int)hp, (int)maxhp); c = hpcolor(hp, maxhp); p2 = addpt(sp, bot); string(screen, p2, c, ZP, smallfont, buf); p2.y -= smallfont->height; snprint(buf, sizeof(buf), "L%d", m->xpl); stringbg(screen, p2, display->white, ZP, smallfont, buf, display->black, ZP); /* debugging the ai state */ if(debug){/* if(m->ai != nil){ stringbg(screen, sp, display->white, ZP, smallfont, m->ai->name, display->black, ZP); } else { stringbg(screen, sp, display->white, ZP, smallfont, m->aglobal->name, display->black, ZP); }*/ } } else if(hasflagat(l, p, Fhasitem)){ drawtile(ts, screen, sp, ilnth(&t->items, 0)->id->tile); } } } }} static voiddrawui(Rectangle r){ int i, logline, sw; double hp, maxhp; Point p; Image *c; char buf[256]; Msg *m; sw = stringwidth(font, " "); draw(screen, r, display->black, nil, ZP); logline = Dy(r) - (font->height*2); p = r.min; p.y += logline-font->height; m = ui.msg; for(i = logline/font->height; m != nil && i > 0; i--){ snprint(buf, sizeof(buf), "%s", m->msg); string(screen, p, ui.cols[m->color], ZP, font, buf); p.y -= font->height; m = m->next; } p = r.min; p.y += logline; line(screen, p, Pt(screen->r.max.x, p.y), Enddisc, Enddisc, 0, display->white, ZP); snprint(buf, sizeof(buf), "%s the %s", user, player->md->name); p = string(screen, p, display->white, ZP, font, buf); p.x += sw*2; snprint(buf, sizeof(buf), "%d kills", player->kills); p = string(screen, p, display->white, ZP, font, buf); p.x = r.min.x; p.y += font->height; snprint(buf, sizeof(buf), "T:%ld", turn); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; snprint(buf, sizeof(buf), "L:%d XP:%d/%d", player->xpl, player->xp, xpcalc(player->xpl)); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; hp = player->hp, maxhp = player->maxhp; snprint(buf, sizeof(buf), "HP:%d/%d", (int)hp, (int)maxhp); if(hp > (maxhp/4)*3) c = ui.cols[CGREEN]; else if(hp > maxhp/2) c = ui.cols[CYELLOW]; else if(hp > maxhp/4) c = ui.cols[CORANGE]; else c = ui.cols[CRED]; p = string(screen, p, c, ZP, font, buf); p.x += sw; snprint(buf, sizeof(buf), "AC:%d", player->ac); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; if(player->flags & Mdead){ p = stringbg(screen, p, display->black, ZP, font, "dead", ui.cols[CRED], ZP); p.x += sw; }} static Rectangleview(Point pos, Level *l, int scrwidth, int scrheight){ Point p; Rectangle r; /* compute vieweable area of the map */ p = Pt((scrwidth/2)+2, (scrheight/2)); r = Rpt(subpt(pos, p), addpt(pos, p)); rectclip(&r, Rect(0, 0, l->width, l->height)); return r;} static voidredraw(UI *ui, int new, int justui){ int width, height; if(new){ if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); /* height should be big enough for 5 tiles and the ui size */ if(Dx(screen->r) < 300 || Dy(screen->r) < (ui->tiles->height*5 + font->height*ui->uisz)){ fprint(2, "window %R too small\n", screen->r); return; } } ui->uir = Rpt(Pt(screen->r.min.x, screen->r.max.y-(font->height*ui->uisz)), screen->r.max); if(!justui){ width = Dx(screen->r) / ui->tiles->width; height = (Dy(screen->r) - (font->height*ui->uisz)) / ui->tiles->height; ui->viewr = view(player->pt, player->l, width, height+2); ui->camp = subpt(Pt(width/2, height/2), player->pt); draw(screen, screen->r, display->black, nil, ZP); drawlevel(player->l, ui->tiles, ui->viewr); } drawui(ui->uir); flushimage(display, 1);} voiduiredraw(int justui){ redraw(&ui, 0, justui);} static Runedbgmenu(int idx, char *s, int sz){ switch(idx){ case 0: snprint(s, sz, "toggle debug flag: %d", debug); return 'd'; case 1: snprint(s, sz, "toggle far messages: %d", farmsg); return 'f'; case 2: snprint(s, sz, "revive and gain max hp"); return 'r'; case 3: snprint(s, sz, "wish for an item"); return 'w'; } return Runemax;} static Runeinventorymenu(int idx, char *s, int sz){ Item *i; if(player->inv.count <= 0 || idx > player->inv.count-1) return Runemax; i = ilnth(&player->inv, idx); snprint(s, sz, "%i", i); return idx+'a';} static Runeequipmenu(int idx, char *s, int sz){ Item *ptr; Rune key[NEQUIP] = { 'w', 'c', 'h', 's', 'a', 'g', 'b' }; if(idx >= NEQUIP) return Runemax; ptr = player->armor[idx]; if(ptr != nil){ snprint(s, sz, "%s: %i", itypetoname(ptr->id->type), ptr);Lines 1–500 of 1286. Load more
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <keyboard.h> #include <mouse.h> #include "map.h" #include "bt.h" #include "dat.h" #include "alg.h" static int gameover = 0; static Font *smallfont; enum { CGREEN, CYELLOW, CORANGE, CRED, CGREY, CMAGENTA, CEND, }; typedef struct KeyMenu KeyMenu; struct KeyMenu { /* label printed with the menu */ char *label; /* gen is called until it returns Runemax. */ Rune (*gen)(int, char*, int); }; static int menu(Keyboardctl *kc, Mousectl *mc, KeyMenu *km); static int uienter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr); typedef struct Msg Msg; struct Msg { char msg[256]; ulong color; Msg *next; }; typedef struct UI UI; struct UI { /* current ui size */ int uisz; /* colors */ Image **cols; /* mouse, keyboard */ Mousectl *mc; Keyboardctl *kc; /* log chan */ Channel *msgc; /* linked list of current msgs */ Msg *msg; int nmsg; /* tileset */ Tileset *tiles; /* level view rectangle, in tiles */ Rectangle viewr; /* ui rectangle, in pixels */ Rectangle uir; Point camp; /* timer tick, see /^tickproc */ Channel *tickcmd; Channel *tick; /* should auto-move? */ int autoidle; /* show far away messages? */ int farmsg; } ui; static void tickproc(void *v) { USED(v); ulong time; time = 0; enum { ATIME, ATICK, AEND }; Alt a[AEND+1] = { { ui.tickcmd, &time, CHANRCV }, { ui.tick, &time, CHANEND }, { nil, nil, CHANEND }, }; for(;;){ switch(alt(a)){ case ATIME: if(time == 0) a[ATICK].op = CHANEND; else a[ATICK].op = CHANSND; break; case ATICK: sleep(time); break; } } } void uiinit(char *name) { int tx, ty; char *tileset, *tmp; tx = ty = 0; if(initdraw(nil, nil, name) < 0) sysfatal("initdraw: %r"); if((smallfont = openfont(display, "/lib/font/bit/fixed/unicode.4x6.font")) == nil) sysfatal("openfont: %r"); if((ui.mc = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((ui.kc = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); if((ui.tickcmd = chancreate(sizeof(ulong), 0)) == nil) sysfatal("chancreate: %r"); if((ui.tick = chancreate(sizeof(ulong), 0)) == nil) sysfatal("chancreate: %r"); proccreate(tickproc, nil, 4096); if((ui.msgc = chancreate(sizeof(Msg*), 1000)) == nil) sysfatal("chancreate: %r"); tileset = getenv("tileset"); if(tileset != nil){ if((tmp = getenv("tilesetx")) == nil || (tx = atoi(tmp)) == 0) sysfatal("zero tilesetx"); free(tmp); if((tmp = getenv("tilesety")) == nil || (ty = atoi(tmp)) == 0) sysfatal("zero tilesety"); free(tmp); } else { if(access("nethack.32x32", AREAD) == 0) tileset = strdup("nethack.32x32"); else tileset = strdup("/lib/hack9/nethack.32x32"); tx = 32; ty = 32; } if((ui.tiles = opentile(tileset, tx, ty)) == nil) sysfatal("opentile %s @ %dx%d: %r", tileset, tx, ty); free(tileset); if((ui.cols = mallocz(CEND * sizeof(Image*), 1)) == nil) sysfatal("malloc cols: %r"); ui.cols[CGREEN] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen); ui.cols[CYELLOW] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellow); ui.cols[CORANGE] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFA500FF); ui.cols[CRED] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed); ui.cols[CGREY] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF); ui.cols[CMAGENTA] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMagenta); ui.uisz = 6; } static void tmsg(void *v) { Msg *l; l = v; while(nbsendp(ui.msgc, l) == 0) yield(); } static void uimsg(uint color, char *fmt, va_list arg) { Msg *l; l = mallocz(sizeof(Msg), 1); assert(l != nil); l->color = color; vsnprint(l->msg, sizeof(l->msg)-1, fmt, arg); if(nbsendp(ui.msgc, l) == 0) threadcreate(tmsg, l, 4096); } void msg(char *fmt, ...) { va_list arg; va_start(arg, fmt); uimsg(CGREY, fmt, arg); va_end(arg); } void good(char *fmt, ...) { va_list arg; va_start(arg, fmt); uimsg(CGREEN, fmt, arg); va_end(arg); } void warn(char *fmt, ...) { va_list arg; va_start(arg, fmt); uimsg(CYELLOW, fmt, arg); va_end(arg); } void bad(char *fmt, ...) { va_list arg; va_start(arg, fmt); uimsg(CRED, fmt, arg); va_end(arg); } void dbg(char *fmt, ...) { va_list arg; char tfmt[256]; if(debug < 1) return; snprint(tfmt, sizeof(tfmt), "T:%ld %s", turn, fmt); va_start(arg, fmt); uimsg(CMAGENTA, tfmt, arg); va_end(arg); } static Image* hpcolor(int hp, int maxhp) { Image *c; if(hp > (maxhp/4)*3) c = ui.cols[CGREEN]; else if(hp > maxhp/2) c = ui.cols[CYELLOW]; else if(hp > maxhp/4) c = ui.cols[CORANGE]; else c = ui.cols[CRED]; return c; } /* draw the whole level on the screen using tileset ts. * r.min is the upper left visible tile, and r.max is * the bottom right visible tile. * */ static void drawlevel(Level *l, Tileset *ts, Rectangle r) { int what; double hp, maxhp; char buf[16]; Point p, p2, sp, bot; Image *c; Tile *t; Monster *m; bot = (Point){0, ts->height - smallfont->height}; for(p.x = r.min.x; p.x < r.max.x; p.x++){ for(p.y = r.min.y; p.y < r.max.y; p.y++){ /* screen space */ sp = p; sp = addpt(sp, ui.camp); sp.x *= ts->width; sp.y *= ts->height; sp = addpt(sp, screen->r.min); t = tileat(l, p); if(t){ what = t->terrain; if(t->unit) what = t->unit; else if(t->feat) what = t->feat; drawtile(ts, screen, sp, what); if(t->monst != nil){ m = t->monst; hp = m->hp, maxhp = m->maxhp; snprint(buf, sizeof(buf), "%d/%d HP", (int)hp, (int)maxhp); c = hpcolor(hp, maxhp); p2 = addpt(sp, bot); string(screen, p2, c, ZP, smallfont, buf); p2.y -= smallfont->height; snprint(buf, sizeof(buf), "L%d", m->xpl); stringbg(screen, p2, display->white, ZP, smallfont, buf, display->black, ZP); /* debugging the ai state */ if(debug){ /* if(m->ai != nil){ stringbg(screen, sp, display->white, ZP, smallfont, m->ai->name, display->black, ZP); } else { stringbg(screen, sp, display->white, ZP, smallfont, m->aglobal->name, display->black, ZP); } */ } } else if(hasflagat(l, p, Fhasitem)){ drawtile(ts, screen, sp, ilnth(&t->items, 0)->id->tile); } } } } } static void drawui(Rectangle r) { int i, logline, sw; double hp, maxhp; Point p; Image *c; char buf[256]; Msg *m; sw = stringwidth(font, " "); draw(screen, r, display->black, nil, ZP); logline = Dy(r) - (font->height*2); p = r.min; p.y += logline-font->height; m = ui.msg; for(i = logline/font->height; m != nil && i > 0; i--){ snprint(buf, sizeof(buf), "%s", m->msg); string(screen, p, ui.cols[m->color], ZP, font, buf); p.y -= font->height; m = m->next; } p = r.min; p.y += logline; line(screen, p, Pt(screen->r.max.x, p.y), Enddisc, Enddisc, 0, display->white, ZP); snprint(buf, sizeof(buf), "%s the %s", user, player->md->name); p = string(screen, p, display->white, ZP, font, buf); p.x += sw*2; snprint(buf, sizeof(buf), "%d kills", player->kills); p = string(screen, p, display->white, ZP, font, buf); p.x = r.min.x; p.y += font->height; snprint(buf, sizeof(buf), "T:%ld", turn); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; snprint(buf, sizeof(buf), "L:%d XP:%d/%d", player->xpl, player->xp, xpcalc(player->xpl)); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; hp = player->hp, maxhp = player->maxhp; snprint(buf, sizeof(buf), "HP:%d/%d", (int)hp, (int)maxhp); if(hp > (maxhp/4)*3) c = ui.cols[CGREEN]; else if(hp > maxhp/2) c = ui.cols[CYELLOW]; else if(hp > maxhp/4) c = ui.cols[CORANGE]; else c = ui.cols[CRED]; p = string(screen, p, c, ZP, font, buf); p.x += sw; snprint(buf, sizeof(buf), "AC:%d", player->ac); p = string(screen, p, display->white, ZP, font, buf); p.x += sw; if(player->flags & Mdead){ p = stringbg(screen, p, display->black, ZP, font, "dead", ui.cols[CRED], ZP); p.x += sw; } } static Rectangle view(Point pos, Level *l, int scrwidth, int scrheight) { Point p; Rectangle r; /* compute vieweable area of the map */ p = Pt((scrwidth/2)+2, (scrheight/2)); r = Rpt(subpt(pos, p), addpt(pos, p)); rectclip(&r, Rect(0, 0, l->width, l->height)); return r; } static void redraw(UI *ui, int new, int justui) { int width, height; if(new){ if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); /* height should be big enough for 5 tiles and the ui size */ if(Dx(screen->r) < 300 || Dy(screen->r) < (ui->tiles->height*5 + font->height*ui->uisz)){ fprint(2, "window %R too small\n", screen->r); return; } } ui->uir = Rpt(Pt(screen->r.min.x, screen->r.max.y-(font->height*ui->uisz)), screen->r.max); if(!justui){ width = Dx(screen->r) / ui->tiles->width; height = (Dy(screen->r) - (font->height*ui->uisz)) / ui->tiles->height; ui->viewr = view(player->pt, player->l, width, height+2); ui->camp = subpt(Pt(width/2, height/2), player->pt); draw(screen, screen->r, display->black, nil, ZP); drawlevel(player->l, ui->tiles, ui->viewr); } drawui(ui->uir); flushimage(display, 1); } void uiredraw(int justui) { redraw(&ui, 0, justui); } static Rune dbgmenu(int idx, char *s, int sz) { switch(idx){ case 0: snprint(s, sz, "toggle debug flag: %d", debug); return 'd'; case 1: snprint(s, sz, "toggle far messages: %d", farmsg); return 'f'; case 2: snprint(s, sz, "revive and gain max hp"); return 'r'; case 3: snprint(s, sz, "wish for an item"); return 'w'; } return Runemax; } static Rune inventorymenu(int idx, char *s, int sz) { Item *i; if(player->inv.count <= 0 || idx > player->inv.count-1) return Runemax; i = ilnth(&player->inv, idx); snprint(s, sz, "%i", i); return idx+'a'; } static Rune equipmenu(int idx, char *s, int sz) { Item *ptr; Rune key[NEQUIP] = { 'w', 'c', 'h', 's', 'a', 'g', 'b' }; if(idx >= NEQUIP) return Runemax; ptr = player->armor[idx]; if(ptr != nil){ snprint(s, sz, "%s: %i", itypetoname(ptr->id->type), ptr);