ref: 07469015a833ea56db430aed28f004e205549360
parent: ef9b582868430fefaf733b7dc13ed6e9dc303bac
author: qwx <qwx@sciops.net>
date: Sat Nov 26 17:53:05 EST 2022
implement menus + build menu big commit; a few things left to fix
--- /dev/null
+++ b/build.c
@@ -1,0 +1,84 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include "dat.h"
+#include "fns.h"
+
+extern Mousectl *mc;
+
+static Tile *site;
+
+int
+couldbuild(Building *b)
+{
+ int i;
+
+ for(i=0; i<nelem(b->buildcost); i++)
+ if(b->buildcost[i] > stock[i])
+ return 0;
+ return 1;
+}
+
+int
+isbuildable(Tile *t)
+{
+ return t->b == nil && t->t == terrains + Tplain;
+}
+
+static char *
+genmenu(int n)
+{
+ switch(n){
+ case 0: return "build";
+ default: return nil;
+ }
+}
+
+static Menu menu = {0, genmenu};
+
+static int
+select(Tile *t)
+{
+ Building *b;
+
+ b = buildings + (t - map);
+ return b >= buildings + nelem(buildings) ? -1 : 0;
+}
+
+static int
+action(Tile *t)
+{
+ int i;
+ Building *b;
+
+ b = buildings + (t - map);
+ if(b >= buildings + nelem(buildings))
+ return -1;
+ selected = t;
+ if(menuhit(3, mc, &menu, nil) < 0)
+ return -1;
+ /* FIXME: show some error message in status bar */
+ if(!couldbuild(b))
+ return 0;
+ for(i=0; i<nelem(b->buildcost); i++){
+ stock[i] -= b->buildcost[i];
+ assert(stock[i] >= 0);
+ }
+ spawn(site, b - buildings);
+ deselect();
+ return 0;
+}
+
+Menuptr
+buildmenu(Tile *t)
+{
+ site = t;
+ selected = nil;
+ gsetcursor(Curstarget);
+ mapdrawfn = drawbuildmenu;
+ selectfn = select;
+ return action;
+}
--- a/city.c
+++ b/city.c
@@ -7,9 +7,10 @@
#include "dat.h"
#include "fns.h"
+Mousectl *mc;
+Keyboardctl *kc;
+
static int tdiv;
-static Keyboardctl *kc;
-static Mousectl *mc;
static Channel *tmc;
void *
@@ -48,8 +49,10 @@
void
threadmain(int argc, char **argv)
{
+ uint b, bo;
Rune r;
Mouse mo;
+ Point p;
ARGBEGIN{
}ARGEND
@@ -66,7 +69,9 @@
readfs();
resetdraw();
startsim();
- mo.xy = ZP;
+ mo.xy = EP;
+ memset(&mo, 0, sizeof mo);
+ bo = 0;
enum{
Aresize,
Amouse,
@@ -88,11 +93,31 @@
mo = mc->Mouse;
resetdraw();
break;
+ /* FIXME: clean up */
case Amouse:
- if(eqpt(mo.xy, ZP))
+ if(eqpt(mo.xy, EP))
mo = mc->Mouse;
- if(mc->buttons & 1<<0)
- mouseselect(mc->xy);
+ b = mc->buttons;
+ p = s2p(mc->xy);
+ if(eqpt(p, EP) || b == 0){
+ //if(b != 0)
+ // deselect();
+ mo = mc->Mouse;
+ bo = b;
+ break;
+ }
+ if((b & (1<<0|1<<2)) != 0
+ && (b & (1<<0|1<<2) != (bo & (1<<0|1<<2)))){
+ mouseselect(p);
+ updatedraw(1);
+ }
+ if((mc->buttons & 1<<2) != 0
+ && (b & 1<<2) != (bo & 1<<2)){
+ actionmenu(p);
+ updatedraw(1);
+ }
+ mo = mc->Mouse;
+ bo = mo.buttons;
break;
case Akbd:
switch(r){
@@ -115,7 +140,7 @@
}
break;
case Aanim:
- updatedraw();
+ updatedraw(0);
break;
}
}
--- a/dat.h
+++ b/dat.h
@@ -126,6 +126,21 @@
};
extern Tile *map;
extern int mapwidth, mapheight;
+extern Tile *selected;
+
+extern int stock[Gtot], rstock[Rtot];
+
+typedef int (*Menuptr)(Tile*);
+enum{
+ Cursdef,
+ Curstarget,
+};
+extern void (*mapdrawfn)(void);
+extern int (*selectfn)(Tile*);
+extern int repaint;
+extern Menuptr menufn;
+
+extern Point EP;
enum{
Te9 = 1000000000,
--- /dev/null
+++ b/data.c
@@ -1,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <cursor.h>
+#include <mouse.h>
+#include "dat.h"
+#include "fns.h"
+
+Cursor targetcursor = {
+ {-7, -7},
+ {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+ 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+ 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, },
+ {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+ 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+ 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, }
+};
--- a/defs.c
+++ b/defs.c
@@ -1,7 +1,9 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
+#include <mouse.h>
#include "dat.h"
+#include "fns.h"
Good goods[] = {
[Gfish]{
--- a/drw.c
+++ b/drw.c
@@ -4,12 +4,14 @@
#include "dat.h"
#include "fns.h"
+Point EP = {-1,-1};
+
int scale = 1;
Point pan;
+void (*mapdrawfn)(void);
QLock drwlock;
+int repaint;
-static Tile *selected;
-
enum{
Cbg,
Ctext,
@@ -17,11 +19,11 @@
};
static Image *cols[Cend];
-static Rectangle fbr, scrwin, hudr;
+Rectangle scrwin;
+static Rectangle fbr, hudr;
static Point scrofs, tlwin, fbbufr;
static Image *fb;
static u32int *fbbuf;
-static int doupdate;
static Image *
eallocimage(Rectangle r, ulong chan, int repl, ulong col)
@@ -33,6 +35,30 @@
return i;
}
+Point
+s2p(Point p)
+{
+ if(!ptinrect(p, scrwin))
+ return EP;
+ return divpt(subpt(p, scrwin.min), scale);
+}
+
+Tile *
+p2t(Point p)
+{
+ p = divpt(p, Tilesz);
+ assert(p.x >= 0 && p.x < mapwidth && p.y >= 0 && p.y < mapheight);
+ return map + p.y * mapwidth + p.x;
+}
+
+Tile *
+s2t(Point p)
+{
+ if(eqpt(p, EP))
+ return nil;
+ return p2t(p);
+}
+
static int
clippic(Point pp, Pic *pic, Point *scpp, Point *ofs, Point *sz)
{
@@ -105,32 +131,32 @@
}
}
-static Point
-tile2xy(Tile *m)
+void
+composeat(Tile *m, u32int c)
{
- Point p;
+ int k, n, x, w;
+ u32int v, *pp;
- p.x = ((m - map) % mapwidth) * Tilesz;
- p.y = ((m - map) / mapwidth) * Tilesz;
- return p;
+ w = fbr.max.x * fbr.max.y / scale;
+ pp = fbbuf + (m-map) / mapwidth * w
+ + (m-map) % mapwidth * Tilesz * scale;
+ n = Tilesz;
+ w = (fbr.max.x / scale - Tilesz) * scale;
+ while(n-- > 0){
+ x = Tilesz;
+ while(x-- > 0){
+ v = *pp;
+ v = (v & 0xff0000) + (c & 0xff0000) >> 1 & 0xff0000
+ | (v & 0xff00) + (c & 0xff00) >> 1 & 0xff00
+ | (v & 0xff) + (c & 0xff) >> 1 & 0xff;
+ k = scale;
+ while(k-- > 0)
+ *pp++ = v;
+ }
+ pp += w;
+ }
}
-static Point
-scr2tilexy(Point p)
-{
- p = divpt(subpt(p, scrwin.min), scale * Tilesz);
- assert(p.x < tlwin.x && p.x >= 0 && p.y < tlwin.y && p.y >= 0);
- return p;
-}
-
-static Tile *
-scr2tile(Point p)
-{
- assert(ptinrect(p, scrwin));
- p = scr2tilexy(p);
- return map + p.y * mapwidth + p.x;
-}
-
static void
drawhud(void)
{
@@ -144,6 +170,42 @@
}
static void
+drawmenuselected(void)
+{
+ int i, n;
+ char s[256], *sp;
+ Point p;
+ Building *b;
+
+ if(selected == nil)
+ return;
+ assert(selected >= map && selected < map + mapwidth * mapheight);
+ b = buildings + (selected - map);
+ if(b >= buildings + nelem(buildings)){
+ fprint(2, "nope\n");
+ return;
+ }
+ p = addpt(hudr.min, Pt(0, font->height));
+ sp = s;
+ sp = seprint(sp, s+sizeof s, "%s time %d cost ",
+ b->name, b->buildtime);
+ for(i=0, n=0; i<nelem(b->product); i++)
+ if(b->buildcost[i] > 0){
+ sp = seprint(sp, s+sizeof s, "%s%d %s",
+ n > 0 ? "," : "", b->buildcost[i], goods[i].name);
+ n++;
+ }
+ sp = seprint(sp, s+sizeof s, " product ");
+ for(i=0, n=0; i<nelem(b->product); i++)
+ if(b->product[i] > 0){
+ sp = seprint(sp, s+sizeof s, "%s%d %s",
+ n > 0 ? "," : "", b->product[i], goods[i].name);
+ n++;
+ }
+ string(screen, p, cols[Ctext], ZP, font, s);
+}
+
+static void
drawtile(Tile *m)
{
Pic *pic;
@@ -153,62 +215,85 @@
}
void
-updatedraw(void)
+drawbuildings(void)
{
int x, y;
Tile *m;
+ Building *b;
- qlock(&drwlock);
+ for(y=0, m=map, b=buildings; y<tlwin.y; y++){
+ for(x=0; x<tlwin.x; x++, m++, b++){
+ if(b >= buildings + nelem(buildings))
+ return;
+ drawpic(tile2xy(m), &b->Pic);
+ if(!couldbuild(b))
+ composeat(m, 0x555555);
+ if(m->stale){
+ m->stale = 0;
+ repaint = 1;
+ }
+ }
+ m += mapwidth - tlwin.x;
+ }
+}
+
+static void
+drawmap(void)
+{
+ int x, y;
+ Tile *m;
+
for(y=0, m=map; y<tlwin.y; y++){
for(x=0; x<tlwin.x; x++, m++)
if(m->stale){
drawtile(m);
m->stale = 0;
- doupdate = 1;
+ repaint = 1;
}
m += mapwidth - tlwin.x;
}
- qunlock(&drwlock);
- if(!doupdate)
- return;
- flushfb();
- doupdate = 0;
}
void
-mouseselect(Point p)
+drawbuildmenu(void)
{
- doupdate = 1;
- if(!ptinrect(p, scrwin)){
- selected = nil;
- return;
- }
- selected = scr2tile(p);
- doupdate = 1;
- updatedraw();
+ drawbuildings();
+ drawmenuselected();
}
static void
-redraw(void)
+redrawcanvas(void)
{
int x, y;
Tile *m;
- draw(fb, fb->r, display->black, nil, ZP);
+ memset(fbbuf, 0, fbbufr.x * fbbufr.y * sizeof *fbbuf);
for(y=0, m=map; y<tlwin.y; y++){
for(x=0; x<tlwin.x; x++, m++)
m->stale = 1;
m += mapwidth - tlwin.x;
}
- updatedraw();
}
void
+updatedraw(int all)
+{
+ qlock(&drwlock);
+ if(all || repaint)
+ redrawcanvas();
+ (mapdrawfn != nil ? mapdrawfn : drawmap)();
+ qunlock(&drwlock);
+ flushfb();
+ repaint = 0;
+}
+
+void
flushfb(void)
{
uchar *p;
Rectangle r, r2;
+ lockdisplay(display);
p = (uchar *)fbbuf;
if(scale == 1){
loadimage(fb, fb->r, p, fbbufr.x * fbbufr.y * sizeof *fbbuf);
@@ -225,6 +310,7 @@
}
drawhud();
flushimage(display, 1);
+ unlockdisplay(display);
}
void
@@ -261,8 +347,7 @@
fbbuf = emalloc(fbbufr.x * fbbufr.y * sizeof *fbbuf);
if(!eqpt(scrofs, ZP))
draw(screen, screen->r, cols[Cbg], nil, ZP);
- doupdate = 1;
- redraw();
+ updatedraw(1);
}
void
@@ -270,6 +355,8 @@
{
if(initdraw(nil, nil, "city") < 0)
sysfatal("initdraw: %r");
+ display->locking = 1;
+ unlockdisplay(display);
cols[Cbg] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
cols[Ctext] = display->black;
fmtinstall('P', Pfmt);
--- a/fns.h
+++ b/fns.h
@@ -2,12 +2,24 @@
void init(void);
void initmap(void);
int mhdist(int, int, int, int);
+Point tile2xy(Tile*);
+Point s2p(Point);
+Tile* p2t(Point);
+Tile* s2t(Point);
void startsim(void);
+void spawn(Tile*, int);
void readfs(void);
void initdrw(void);
void resetdraw(void);
-void updatedraw(void);
+void updatedraw(int);
void flushfb(void);
+void drawbuildmenu(void);
void mouseselect(Point);
+void actionmenu(Point);
+void deselect(void);
+void gsetcursor(int);
int min(int, int);
int max(int, int);
+int couldbuild(Building*);
+Menuptr buildmenu(Tile*);
+int isbuildable(Tile*);
--- a/fs.c
+++ b/fs.c
@@ -53,8 +53,8 @@
for(t=terrains; t<terrains+nelem(terrains); t++)
loadpic(t->name, Tilesz, &t->Pic);
- b = buildings + Btownhall;
- loadpic(b->name, Tilesz, &b->Pic);
+ for(b=buildings; b<buildings+nelem(buildings); b++)
+ loadpic(b->name, Tilesz, &b->Pic);
}
void
--- a/map.c
+++ b/map.c
@@ -7,6 +7,16 @@
int mapwidth, mapheight;
Tile *map;
+Point
+tile2xy(Tile *m)
+{
+ Point p;
+
+ p.x = ((m - map) % mapwidth) * Tilesz;
+ p.y = ((m - map) / mapwidth) * Tilesz;
+ return p;
+}
+
int
mhdist(int x, int y, int x´, int y´)
{
--- /dev/null
+++ b/menu.c
@@ -1,0 +1,111 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <cursor.h>
+#include "dat.h"
+#include "fns.h"
+
+extern Mousectl *mc;
+extern Cursor targetcursor;
+
+/* FIXME: safer: Menu struct with menu state: default, build, etc. */
+
+Tile *selected;
+int (*selectfn)(Tile *);
+Menuptr menufn;
+
+typedef struct Gmenu Gmenu;
+struct Gmenu{
+ char *name;
+ int (*checkfn)(Tile*);
+ Menuptr (*initfn)(Tile*);
+ int (*selectfn)(Tile*);
+};
+enum{
+ Gmenubuild,
+ Gmenuend,
+};
+static Gmenu actions[Gmenuend] = {
+ [Gmenubuild] {"build", isbuildable, buildmenu},
+};
+static Gmenu *favail[nelem(actions)];
+static int navail;
+
+extern Rectangle scrwin;
+
+void
+gsetcursor(int curs)
+{
+ Cursor *c;
+
+ switch(curs){
+ case Curstarget: c = &targetcursor; break;
+ case Cursdef: /* wet floor */
+ default: c = nil; break;
+ }
+ setcursor(mc, c);
+}
+
+static char *
+genmenu(int n)
+{
+ return n < navail ? favail[n]->name : nil;
+}
+
+static Menu menu = {0, genmenu};
+
+static Menuptr
+setmenu(Tile *t)
+{
+ int n;
+ Gmenu *f;
+
+ memset(favail, 0, sizeof favail);
+ navail = 0;
+ for(f=actions; f<actions+nelem(actions); f++)
+ if(f->checkfn == nil || f->checkfn(t))
+ favail[navail++] = f;
+ if(navail == 0)
+ return nil;
+ if((n = menuhit(3, mc, &menu, nil)) < 0)
+ return nil;
+ return favail[n]->initfn(t);
+}
+
+void
+actionmenu(Point p)
+{
+ Tile *t;
+
+ t = p2t(p);
+ assert(t != nil);
+ selected = t;
+ if(menufn == nil){
+ menufn = setmenu(t);
+ if(menufn == nil)
+ deselect();
+ }else if(menufn(t) < 0)
+ deselect();
+}
+
+void
+mouseselect(Point p)
+{
+ selected = p2t(p);
+ if(selectfn != nil && selectfn(selected) < 0)
+ deselect();
+}
+
+void
+deselect(void)
+{
+ selected = nil;
+ menufn = nil;
+ mapdrawfn = nil;
+ selectfn = nil;
+ gsetcursor(Cursdef);
+ repaint = 1;
+}
--- a/mkfile
+++ b/mkfile
@@ -2,11 +2,14 @@
BIN=$home/bin/$objtype
TARG=city
OFILES=\
+ build.$O\
city.$O\
+ data.$O\
defs.$O\
drw.$O\
fs.$O\
map.$O\
+ menu.$O\
sim.$O\
HFILES=dat.h fns.h
--- a/sim.c
+++ b/sim.c
@@ -10,21 +10,22 @@
int paused;
vlong clock;
+enum{
+ UpkeepΔt = 150,
+};
+int stock[Gtot], rstock[Rtot];
+
static int tdiv;
static Tile **objs, **objhead;
static int maxobj;
-enum{
- UpkeepΔt = 150,
-};
-static int stock[Gtot], rstock[Rtot];
-
-static void
+void
spawn(Tile *m, int n)
{
Tile **o;
+ assert(m != nil);
if(objhead - objs >= maxobj)
sysfatal("spawn: out of bounds");
m->t = terrains + Tplain;