ref: 38d8044975f8467c5ac4662259a89d6ea59d45bc
author: qwx <qwx@sciops.net>
date: Mon May 27 19:55:46 EDT 2019
initial import
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,107 @@
+typedef struct Unit Unit;
+typedef struct Munit Munit;
+typedef struct Tunit Tunit;
+typedef struct Terrain Terrain;
+typedef struct Map Map;
+typedef struct Team Team;
+
+extern char **mov;
+extern int nmov;
+
+struct Unit{
+ char *name;
+ u32int **pic;
+ int atk;
+ int Δatk;
+ int rmin;
+ int rmax;
+ int def;
+ int *move;
+ int mp;
+ int vis;
+ int cost;
+ int unique;
+};
+extern Unit *unit;
+extern int nunit;
+
+struct Munit{
+ Unit *u;
+ int team;
+ int atkm;
+ int defm;
+ int eatk;
+ int edef;
+ int ecost;
+ int xp;
+ int hp;
+ int done;
+ int decaydt;
+};
+
+struct Tunit{
+ Unit **u;
+ Unit **e;
+};
+
+struct Terrain{
+ char *name;
+ u32int **pic;
+ int move;
+ int def;
+ int income;
+ Tunit spawn;
+ Tunit occupy;
+ Tunit *resupply;
+};
+extern Terrain *terrain;
+extern int nterrain;
+
+struct Map{
+ Terrain *t;
+ int team;
+ Munit *u;
+ int movep;
+ int canmove;
+ int atkp;
+ int canatk;
+};
+extern Map *map, *mape, *selected, *selected2, *saved, *unique;
+extern int mapwidth, mapheight;
+
+enum{
+ Nteam = 64,
+};
+
+struct Team{
+ int money;
+ int income;
+ int nunit;
+ int nbuild;
+ int nprod;
+ Munit *unique;
+};
+extern Team team[Nteam];
+extern int nteam, curteam;
+extern int turn, gameover;
+extern int initmoney, unitcap, firstturnnoinc, nocorpse;
+
+enum{
+ Pcur,
+ P1,
+ P2,
+ P3,
+ P4,
+ P5,
+ P6,
+ P7,
+ P8,
+ P9,
+ Pend
+};
+
+extern void (*selectfn)(void);
+extern int scale;
+extern int menuon;
+
+extern char *progname, *prefix, *dbname, *mapname;
--- /dev/null
+++ b/db.c
@@ -1,0 +1,509 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ndb.h>
+#include "dat.h"
+#include "fns.h"
+
+char **mov;
+int nmov;
+Terrain *terrain;
+int nterrain;
+Unit *unit;
+int nunit;
+Map *map, *mape, *selected, *selected2;
+int mapwidth, mapheight;
+
+typedef struct Stackenblochen Stackenblochen;
+struct Stackenblochen{
+ char *name;
+ int v;
+ char *s;
+ Stackenblochen *b;
+ Stackenblochen *l;
+ Stackenblochen *r;
+};
+static Stackenblochen stack0 = {.l = &stack0, .r = &stack0}, *stack = &stack0;
+
+static Stackenblochen *
+allocblochen(char *name)
+{
+ Stackenblochen *b;
+
+ b = emalloc(sizeof *b);
+ if(name[0] != 0)
+ b->name = estrdup(name);
+ return b;
+}
+
+static void
+freeblochen(Stackenblochen *b)
+{
+ free(b->name);
+ free(b->s);
+ free(b);
+}
+
+static void
+popstackenblochen(Stackenblochen *stack)
+{
+ Stackenblochen *b, *p, *q;
+
+ for(b=stack->l; b!=stack; b=q){
+ for(p=b->r; p!=b; p=q){
+ q = p->r;
+ freeblochen(p);
+ }
+ q = b->l;
+ freeblochen(b);
+ }
+ for(b=stack; b->r!=stack; b=b->r)
+ ;
+ b->r = b->r->r;
+ freeblochen(stack);
+}
+
+static Stackenblochen *
+pushstackenblochen(char *name, Stackenblochen *stack)
+{
+ Stackenblochen *b, *p;
+
+ b = allocblochen(name);
+ b->r = b;
+ b->l = stack;
+ b->v = stack->v++;
+ p = stack;
+ while(p->l != stack)
+ p = p->l;
+ p->l = b;
+ return b;
+}
+
+static Stackenblochen *
+stackenblochen(char *name, Stackenblochen *stack)
+{
+ Stackenblochen *b;
+
+ b = allocblochen(name);
+ b->l = b;
+ b->r = stack->r;
+ stack->r = b;
+ return b;
+}
+
+static Stackenblochen *
+findblochen(char *name, Stackenblochen *stack, int dir)
+{
+ Stackenblochen *b;
+
+ b = dir ? stack->l : stack->r;
+ while(b != stack){
+ if(b->name != nil && strcmp(b->name, name) == 0)
+ return b;
+ b = dir ? b->l : b->r;
+ }
+ return nil;
+}
+
+static void
+getblochen(Ndbtuple *t, Stackenblochen *stack)
+{
+ char *s;
+ Stackenblochen *b;
+
+ if((b = findblochen(t->attr, stack, 0)) == nil)
+ b = stackenblochen(t->attr, stack);
+ b = pushstackenblochen(t->val, b);
+ for(t=t->entry; t!=nil; t=t->entry){
+ b = stackenblochen(t->attr, b);
+ b->v = strtol(t->val, &s, 0);
+ b->s = estrdup(t->val);
+ }
+}
+
+static void
+loaddb(char *name, Stackenblochen *stack)
+{
+ char *s;
+ Ndb *db;
+ Ndbtuple *t;
+
+ if((db = ndbopen(name)) == nil){
+ s = smprint("%s/%s", prefix, name);
+ db = ndbopen(s);
+ free(s);
+ if(db == nil)
+ sysfatal("ndbopen: %r");
+ }
+ while((t = ndbparse(db)) != nil){
+ getblochen(t, stack);
+ ndbfree(t);
+ }
+ ndbclose(db);
+}
+
+static void
+initrules(void)
+{
+ Stackenblochen *b;
+
+ unitcap = 25;
+ if((b = findblochen("initmoney", stack, 0)) != nil){
+ initmoney = strtol(b->l->name, nil, 0);
+ if(initmoney < 0)
+ initmoney = 0;
+ popstackenblochen(b);
+ }
+ if((b = findblochen("unitcap", stack, 0)) != nil){
+ unitcap = strtol(b->l->name, nil, 0);
+ if(unitcap <= 0)
+ unitcap = mapwidth * mapheight / nteam;
+ popstackenblochen(b);
+ }
+ if((b = findblochen("firstturnnoinc", stack, 0)) != nil){
+ firstturnnoinc = 1;
+ popstackenblochen(b);
+ }
+ if((b = findblochen("nocorpse", stack, 0)) != nil){
+ nocorpse = 1;
+ popstackenblochen(b);
+ }
+}
+
+static void
+initmap(void)
+{
+ int i, l, n;
+ Map *m;
+ Unit *u;
+ Stackenblochen *b, *p, *q;
+
+ if((b = findblochen("mapwidth", stack, 0)) != nil){
+ mapwidth = strtol(b->l->name, nil, 0);
+ if(mapwidth <= 0)
+ mapwidth = 1;
+ popstackenblochen(b);
+ }
+ if((b = findblochen("map", stack, 0)) != nil){
+ if(mapwidth <= 0)
+ mapwidth = sqrt(b->v);
+ mapheight = b->v / mapwidth;
+ }else{
+ if(mapwidth <= 0)
+ mapwidth = 8;
+ mapheight = mapwidth;
+ }
+ n = mapwidth * mapheight;
+ map = emalloc(n * sizeof *map);
+ mape = map + n;
+ for(m=map; m<map+n; m++)
+ m->t = terrain;
+ if(b == nil)
+ return;
+ for(p=b->l, m=map; m<map+n; m++, p=p->l){
+ for(i=0; i<nterrain; i++){
+ l = strlen(terrain[i].name);
+ if(strncmp(p->name, terrain[i].name, l) == 0){
+ m->t = terrain + i;
+ if(strlen(p->name) > l)
+ m->team = strtol(p->name+l, nil, 10);
+ if(m->team < 0 || m->team > nelem(team))
+ m->team = 0;
+ else if(m->team > nteam)
+ nteam = m->team;
+ break;
+ }
+ }
+ if((q = findblochen("unit", p, 0)) != nil)
+ for(u=unit; u<unit+nunit; u++){
+ l = strlen(u->name);
+ if(strncmp(q->s, u->name, l) == 0){
+ i = 0;
+ if(strlen(q->s) > l)
+ i = strtol(q->s+l, nil, 10);
+ if(i < 0)
+ i = 0;
+ spawnunit(m, u, i);
+ break;
+ }
+ }
+ }
+ popstackenblochen(b);
+ for(m=map; m<map+mapwidth*mapheight; m++){
+ team[m->team].income += m->t->income;
+ team[m->team].nbuild++;
+ if(m->t->spawn.u != nil)
+ team[m->team].nprod++;
+ }
+}
+
+static void
+initoccupy(void)
+{
+ int n;
+ Terrain *t;
+ Unit *u;
+ Stackenblochen *b, *p, *q;
+
+ if((b = findblochen("occupy", stack, 0)) == nil)
+ return;
+ for(p=b->l; p!=b; p=p->l){
+ for(t=terrain; t<terrain+nterrain; t++)
+ if(strcmp(p->name, t->name) == 0)
+ break;
+ if(t == terrain + nterrain){
+ fprint(2, "initoccupy: unknown terrain %s\n", p->name);
+ continue;
+ }
+ for(q=p->r, n=0; q!=p; q=q->r)
+ n++;
+ t->occupy.u = emalloc(n * sizeof *t->occupy.u);
+ t->occupy.e = t->occupy.u + n;
+ for(q=p->r, n=0; q!=p; q=q->r){
+ for(u=unit; u<unit+nunit; u++)
+ if(strcmp(q->s, u->name) == 0)
+ break;
+ if(u == unit + nunit){
+ fprint(2, "initoccupy: unknown unit %s\n", q->s);
+ t->occupy.e--;
+ continue;
+ }
+ t->occupy.u[n++] = u;
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initresupply(void)
+{
+ Terrain *t, *r;
+ Stackenblochen *b, *p, *q;
+
+ if((b = findblochen("resupply", stack, 0)) == nil)
+ return;
+ for(p=b->l; p!=b; p=p->l){
+ for(t=terrain; t<terrain+nterrain; t++)
+ if(strcmp(p->name, t->name) == 0)
+ break;
+ if(t == terrain + nterrain){
+ fprint(2, "initresupply: unknown terrain %s\n", p->name);
+ continue;
+ }
+ for(q=p->r; q!=p; q=q->r){
+ for(r=terrain; r<terrain+nterrain; r++)
+ if(strcmp(q->s, r->name) == 0)
+ break;
+ if(r == terrain + nterrain){
+ fprint(2, "initresupply: unknown terrain %s\n", q->s);
+ continue;
+ }
+ t->resupply = &r->spawn;
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initspawn(void)
+{
+ int n;
+ Terrain *t;
+ Unit *u;
+ Stackenblochen *b, *p, *q;
+
+ if((b = findblochen("spawn", stack, 0)) == nil)
+ return;
+ for(p=b->l; p!=b; p=p->l){
+ for(t=terrain; t<terrain+nterrain; t++)
+ if(strcmp(p->name, t->name) == 0)
+ break;
+ if(t == terrain + nterrain){
+ fprint(2, "initspawn: unknown terrain %s\n", p->name);
+ continue;
+ }
+ for(q=p->r, n=0; q!=p; q=q->r)
+ n++;
+ t->spawn.u = emalloc(n * sizeof *t->spawn.u);
+ t->spawn.e = t->spawn.u + n;
+ for(q=p->r, n=0; q!=p; q=q->r){
+ for(u=unit; u<unit+nunit; u++)
+ if(strcmp(q->s, u->name) == 0)
+ break;
+ if(u == unit + nunit){
+ fprint(2, "initspawn: unknown unit %s\n", q->s);
+ t->spawn.e--;
+ continue;
+ }
+ t->spawn.u[n++] = u;
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initterrain(void)
+{
+ int n;
+ Terrain *t;
+ char **s;
+ Stackenblochen *b, *p, *q;
+
+ n = 1 + ((b = findblochen("terrain", stack, 0)) != nil ? b->v : 0);
+ terrain = emalloc(n * sizeof *terrain);
+ nterrain = n;
+ t = terrain;
+ t->name = "void";
+ t++;
+ if(n <= 1)
+ return;
+ for(p=b->l; t<terrain+n; t++, p=p->l){
+ t->name = estrdup(p->name);
+ for(q=p->r; q!=p; q=q->r){
+ if(strcmp(q->name, "def") == 0){
+ if((t->def = q->v) < 0)
+ t->def = 0;
+ }else if(strcmp(q->name, "income") == 0){
+ if((t->income = q->v) < 0)
+ t->income = 0;
+ }else if(strcmp(q->name, "move") == 0){
+ for(s=mov; s<mov+nmov; s++)
+ if(strcmp(*s, q->s) == 0)
+ break;
+ if(s != mov + nmov)
+ t->move = s - mov;
+ else
+ fprint(2, "initterrain: unknown move type %s\n", q->s);
+ }
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initmove(void)
+{
+ int n;
+ char **s;
+ Unit *u;
+ Stackenblochen *b, *p, *q;
+
+ n = 1 + ((b = findblochen("move", stack, 0)) != nil ? b->v : 0);
+ mov = emalloc(n * sizeof *mov);
+ nmov = n;
+ s = mov;
+ *s++ = "void";
+ for(u=unit; u<unit+nunit; u++)
+ u->move = emalloc(n * sizeof *u->move);
+ if(n <= 1)
+ return;
+ for(p=b->l, n=1; p!=b; p=p->l, n++){
+ *s++ = estrdup(p->name);
+ for(q=p->r; q!=p; q=q->r){
+ for(u=unit; u<unit+nunit; u++)
+ if(strcmp(q->name, u->name) == 0)
+ break;
+ if(u == unit + nunit){
+ fprint(2, "initmove: unknown unit %s\n", q->name);
+ continue;
+ }
+ u->move[n] = q->v;
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initunit(void)
+{
+ int n;
+ char *s;
+ Unit *u;
+ Stackenblochen *b, *p, *q;
+ static Unit u0 = {
+ .name = "void",
+ .atk = 100,
+ .Δatk = 10,
+ .rmin = 1,
+ .rmax = 1,
+ .vis = 2,
+ .mp = 3,
+ .cost = 1000,
+ };
+
+ n = 1 + ((b = findblochen("unit", stack, 0)) != nil ? b->v : 0);
+ unit = emalloc(n * sizeof *unit);
+ nunit = n;
+ u = unit;
+ *u++ = u0;
+ if(n <= 1)
+ return;
+ for(p=b->l; u<unit+n; u++, p=p->l){
+ *u = u0;
+ u->name = estrdup(p->name);
+ for(q=p->r; q!=p; q=q->r){
+ if(strcmp(q->name, "atk") == 0){
+ if((u->atk = q->v) < 0)
+ u->atk = 0;
+ }else if(strcmp(q->name, "Δatk") == 0){
+ if((u->Δatk = q->v) < 0)
+ u->Δatk = 0;
+ }else if(strcmp(q->name, "def") == 0){
+ if((u->def = q->v) < 0)
+ u->def = 0;
+ }else if(strcmp(q->name, "mp") == 0){
+ if((u->mp = q->v) < 0)
+ u->mp = 0;
+ }else if(strcmp(q->name, "vis") == 0){
+ if((u->vis = q->v) < 0)
+ u->vis = 0;
+ }else if(strcmp(q->name, "cost") == 0){
+ if((u->cost = q->v) < 0)
+ u->cost = 0;
+ }else if(strcmp(q->name, "range") == 0){
+ u->rmin = q->v;
+ if((s = strrchr(q->s, ',')) != nil)
+ u->rmax = strtol(s+1, nil, 10);
+ if(u->rmax < u->rmin)
+ u->rmax = u->rmin;
+ }else if(strcmp(q->name, "unique") == 0)
+ u->unique = q->v;
+ else
+ fprint(2, "initunit: unknown attr %s\n", q->name);
+ }
+ }
+ popstackenblochen(b);
+}
+
+static void
+initdb(void)
+{
+ initunit();
+ initmove();
+ initterrain();
+ initspawn();
+ initresupply();
+ initoccupy();
+ initmap();
+ initrules();
+}
+
+void
+init(void)
+{
+ Stackenblochen *b, *p, *q;
+
+ loaddb(dbname, stack);
+ loaddb(mapname, stack);
+ initdb();
+ initgame();
+ for(b=stack->r; b!=stack; b=b->r){
+ fprint(2, "stack %s\n", b->name);
+ for(p=b->l; p!=b; p=p->l){
+ fprint(2, "%s: ", p->name);
+ for(q=p->r; q!=p; q=q->r)
+ fprint(2, "%s=%s ", q->name, q->s);
+ }
+ fprint(2, "\n");
+ }
+}
--- /dev/null
+++ b/drw.c
@@ -1,0 +1,443 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+Point p0, pan;
+int scale;
+int menuon;
+void (*selectfn)(void);
+
+static int fbsz, fbh, fbw;
+static u32int *fb;
+static Image *fbi;
+static Rectangle fbr;
+static int tlwidth, tlheight;
+static u32int *pics[Pend];
+static u32int bgcol = 0x00ffff;
+
+void
+dopan(int dx, int dy)
+{
+ Rectangle r;
+
+ r = rectaddpt(fbr, pan);
+ if(dx > 0){
+ r.max.x = r.min.x + dx;
+ draw(screen, r, display->black, nil, ZP);
+ }else if(dx < 0){
+ r.min.x = r.max.x + dx;
+ draw(screen, r, display->black, nil, ZP);
+ }
+ r = rectaddpt(fbr, pan);
+ if(dy > 0){
+ r.max.y = r.min.y + dy;
+ draw(screen, r, display->black, nil, ZP);
+ }else if(dy < 0){
+ r.min.y = r.max.y + dy;
+ draw(screen, r, display->black, nil, ZP);
+ }
+ pan.x += dx;
+ pan.y += dy;
+ redraw(0);
+}
+
+void
+select(Point m)
+{
+ int n;
+ Rectangle r;
+
+ r = rectaddpt(fbr, pan);
+ if(r.max.y > p0.y)
+ r.max.y = p0.y;
+ if(!ptinrect(m, r))
+ return;
+ m = subpt(m, r.min);
+ n = m.y / (scale * tlheight) * mapwidth + m.x / (scale * tlwidth);
+ selected2 = map + n;
+ selectfn();
+ selected = selected2;
+ redraw(0);
+}
+
+static void
+drawscaled(void)
+{
+ Rectangle r, r2;
+ uchar *p;
+
+ r = rectaddpt(fbr, pan);
+ if(r.max.y > p0.y)
+ r.max.y = p0.y;
+ r2 = r;
+ p = (uchar *)fb;
+ if(scale == 1){
+ loadimage(fbi, fbi->r, p, fbsz);
+ draw(screen, r, fbi, nil, ZP);
+ }else{
+ while(r.min.y < r2.max.y){
+ r.max.y = r.min.y + scale;
+ p += loadimage(fbi, fbi->r, p, fbsz / fbh);
+ draw(screen, r, fbi, nil, ZP);
+ r.min.y = r.max.y;
+ }
+ }
+}
+
+static void
+drawpic(u32int *pp, u32int **pic)
+{
+ int n, m, w, x;
+ u32int v, *ip;
+
+ if(pic == nil){
+ fprint(2, "drawpic: missing pic\n");
+ return;
+ }
+ n = tlheight;
+ ip = *pic;
+ w = (fbw - tlwidth) * scale;
+ while(n-- > 0){
+ m = tlwidth;
+ while(m-- > 0){
+ v = *ip++;
+ if(v & 0xff << 24)
+ for(x=0; x<scale; x++)
+ *pp++ = v;
+ else
+ pp += scale;
+ }
+ pp += w;
+ }
+}
+
+static void
+drawextra(void)
+{
+ Map *m;
+
+ for(m=map; m<mape; m++)
+ if(m->u != nil){
+ if(m->u->hp < 99)
+ drawpicat(m, pics + m->u->hp / 11 + 1);
+ if(m->u->done)
+ composeat(m, 0x555555);
+ }
+}
+
+static void
+drawmenuselected(void)
+{
+ int cost;
+ char s[256];
+ Point sp;
+ Unit *u;
+ Tunit *tu;
+
+ tu = &saved->t->spawn;
+ if(selected == nil || tu->u + (selected - map) >= tu->e)
+ return;
+ u = tu->u[selected - map];
+ if(team[curteam].unique != nil && u == team[curteam].unique->u){
+ if(unique != nil)
+ return;
+ cost = team[curteam].unique->ecost;
+ }else
+ cost = u->cost;
+ sp = p0;
+ sp.y += font->height;
+ snprint(s, sizeof s, "unit: %s, atk %d±%d, def %d rng %d-%d, mp %d, cost %d", u->name, u->atk, u->Δatk, u->def, u->rmin, u->rmax, u->mp, cost);
+ string(screen, sp, display->white, ZP, font, s);
+}
+
+static void
+drawspawn(void)
+{
+ Tunit *tu;
+ Unit **u;
+ Map *m;
+
+ tu = &saved->t->spawn;
+ for(u=tu->u, m=map; u<tu->e; u++, m++){
+ drawpicat(m, (*u)->pic);
+ if(unique != nil && *u == team[curteam].unique->u)
+ composeat(m, 0x555555);
+ }
+}
+
+static void
+drawselected(void)
+{
+ char s[256];
+ Point sp;
+ Munit *mu;
+ Unit *u;
+
+ if(selected == nil)
+ return;
+ drawpicat(selected, pics + Pcur);
+ sp = p0;
+ sp.y += font->height;
+ snprint(s, sizeof s, "terrain: %s (+%d)", selected->t->name, selected->t->def);
+ string(screen, sp, display->white, ZP, font, s);
+ if(selected->u == nil)
+ return;
+ sp.y += font->height;
+ mu = selected->u;
+ u = mu->u;
+ snprint(s, sizeof s, "unit: %s, rank %d, atk %d±%d, def %d, rng %d-%d, mp %d", u->name, mu->xp / 100, mu->eatk, u->Δatk, mu->edef, u->rmin, u->rmax, u->mp);
+ string(screen, sp, display->white, ZP, font, s);
+ flushimage(display, 1);
+}
+
+static void
+drawlog(void)
+{
+ char s[64];
+
+ if(gameover)
+ snprint(s, sizeof s, "GAME OVER: team %d wins at turn %d", curteam, turn+1);
+ else
+ snprint(s, sizeof s, "turn %d, team %d: %d (+%d) gold, %d units %d buildings", turn+1, curteam, team[curteam].money, team[curteam].income, team[curteam].nunit, team[curteam].nbuild);
+ string(screen, p0, display->white, ZP, font, s);
+}
+
+static void
+drawmap(void)
+{
+ int n, w;
+ u32int *pp;
+ Map *m;
+
+ pp = fb;
+ n = 0;
+ w = tlwidth * mapwidth * (tlheight - 1) * scale;
+ for(m=map; m<mape; m++){
+ drawpic(pp, m->t->pic + m->team);
+ if(m->u != nil)
+ drawpic(pp, m->u->u->pic + m->u->team);
+ if(m->canmove)
+ composeat(m, 0xff0f0f);
+ if(m->canatk)
+ composeat(m, 0xf0ff0f);
+ pp += tlwidth * scale;
+ if(++n == mapwidth){
+ n = 0;
+ pp += w;
+ }
+ }
+}
+
+void
+composeat(Map *m, u32int c)
+{
+ int k, n, x, w;
+ u32int v, *pp;
+
+ w = fbw * scale * tlheight;
+ pp = fb + (m-map) / mapwidth * w
+ + (m-map) % mapwidth * tlwidth * scale;
+ n = tlheight;
+ w = (fbw - tlwidth) * scale;
+ while(n-- > 0){
+ x = tlwidth;
+ 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;
+ }
+}
+
+void
+drawpicat(Map *m, u32int **pic)
+{
+ int w;
+ u32int *pp;
+
+ w = fbw * scale * tlheight;
+ pp = fb + (m-map) / mapwidth * w + (m-map) % mapwidth * tlwidth * scale;
+ drawpic(pp, pic);
+}
+
+void
+redraw(int all)
+{
+ if(all)
+ draw(screen, screen->r, display->black, nil, ZP);
+ else
+ draw(screen, Rpt(p0, screen->r.max), display->black, nil, ZP);
+ if(!menuon){
+ drawmap();
+ drawselected();
+ drawextra();
+ }else{
+ memset(fb, 0, mapwidth * tlwidth * scale * mapheight * tlheight * sizeof *fb);
+ drawspawn();
+ drawmenuselected();
+ }
+ drawlog();
+ drawscaled();
+ flushimage(display, 1);
+}
+
+void
+resetdraw(void)
+{
+ Point p, d;
+
+ if(scale < 1)
+ scale = 1;
+ else if(scale > 16)
+ scale = 16;
+ fbw = mapwidth * tlwidth;
+ fbh = mapheight * tlheight;
+ p = divpt(addpt(screen->r.min, screen->r.max), 2);
+ d = Pt(fbw * scale / 2, fbh * scale / 2);
+ fbr = Rpt(subpt(p, d), addpt(p, d));
+ p0 = Pt(screen->r.min.x + 8, screen->r.max.y - 3 * font->height);
+ fbsz = fbw * scale * fbh * sizeof *fb;
+ free(fb);
+ freeimage(fbi);
+ fb = emalloc(fbsz);
+ if((fbi = allocimage(display, Rect(0,0,fbw*scale,scale==1 ? fbh : 1), XRGB32, scale>1, 0)) == nil)
+ sysfatal("allocimage: %r");
+ redraw(1);
+}
+
+static Image*
+iconv(Image *i)
+{
+ Image *ni;
+
+ if(i->chan == RGB24)
+ return i;
+ if((ni = allocimage(display, i->r, RGB24, 0, DNofill)) == nil)
+ sysfatal("allocimage: %r");
+ draw(ni, ni->r, i, nil, ZP);
+ freeimage(i);
+ return ni;
+}
+
+static int
+openimage(char *name)
+{
+ int fd;
+ char *s;
+
+ if((fd = open(name, OREAD)) < 0){
+ s = smprint("%s/%s", prefix, name);
+ fd = open(s, OREAD);
+ free(s);
+ if(fd < 0)
+ fprint(2, "openimage: %r\n");
+ }
+ return fd;
+}
+
+static u32int *
+loadpic(char *name)
+{
+ int fd, n, m, dx, dy;
+ Image *i;
+ uchar *b, *s;
+ u32int v, *p, *pic;
+
+ if(name == nil || strlen(name) == 0)
+ sysfatal("loadpic: invalid name");
+ if((fd = openimage(name)) < 0)
+ return nil;
+ if((i = readimage(display, fd, 0)) == nil)
+ sysfatal("readimage: %r");
+ close(fd);
+ i = iconv(i);
+ dx = Dx(i->r);
+ dy = Dy(i->r);
+ if(tlwidth == 0 || tlheight == 0){
+ tlwidth = dx;
+ tlheight = dy;
+ }else if(tlwidth != dx || tlheight != dy)
+ sysfatal("loadpic: unequal tile size %dx%d not %dx%d",
+ dx, dy, tlwidth, tlheight);
+ n = dx * dy;
+ m = n * i->depth / 8;
+ b = emalloc(m);
+ unloadimage(i, i->r, b, m);
+ freeimage(i);
+ p = emalloc(n * sizeof *p);
+ pic = p;
+ s = b;
+ while(n-- > 0){
+ v = s[2] << 16 | s[1] << 8 | s[0];
+ if(v != bgcol)
+ v |= 0xff << 24;
+ *p++ = v;
+ s += 3;
+ }
+ free(b);
+ return pic;
+}
+
+static void
+initbaseimages(void)
+{
+ char *tab[] = {
+ [Pcur] "cursor.bit",
+ [P1] "1.bit",
+ [P2] "2.bit",
+ [P3] "3.bit",
+ [P4] "4.bit",
+ [P5] "5.bit",
+ [P6] "6.bit",
+ [P7] "7.bit",
+ [P8] "8.bit",
+ [P9] "9.bit",
+ };
+ int i;
+
+ for(i=0; i<nelem(tab); i++)
+ pics[i] = loadpic(tab[i]);
+}
+
+void
+initimages(void)
+{
+ int i, n;
+ char s[64];
+ Terrain *t;
+ Unit *u;
+
+ initbaseimages();
+ for(t=terrain; t<terrain+nterrain; t++){
+ n = t->occupy.u != nil ? nteam+1 : 1;
+ t->pic = emalloc(n * sizeof *t->pic);
+ if(strcmp(t->name, "void") == 0)
+ continue;
+ snprint(s, sizeof s, "%s.bit", t->name);
+ t->pic[0] = loadpic(s);
+ for(i=1; i<n; i++){
+ snprint(s, sizeof s, "%s%d.bit", t->name, i);
+ t->pic[i] = loadpic(s);
+ }
+ }
+ for(u=unit; u<unit+nunit; u++){
+ u->pic = emalloc((nteam+1) * sizeof *u->pic);
+ if(strcmp(u->name, "void") == 0)
+ continue;
+ snprint(s, sizeof s, "%s.bit", u->name);
+ u->pic[0] = loadpic(s);
+ if(strcmp(u->name, "corpse") == 0)
+ continue;
+ for(i=1; i<=nteam; i++){
+ snprint(s, sizeof s, "%s%d.bit", u->name, i);
+ u->pic[i] = loadpic(s);
+ }
+ }
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,14 @@
+void dopan(int, int);
+void composeat(Map*, u32int);
+void drawpicat(Map*, u32int**);
+void redraw(int);
+void resetdraw(void);
+void initimages(void);
+void init(void);
+void spawnunit(Map*, Unit*, int);
+void endturn(void);
+void initgame(void);
+void lmenu(char**, void(*[])(void));
+char* estrdup(char*);
+void* emalloc(ulong);
+void* erealloc(void*, ulong, ulong);
--- /dev/null
+++ b/game.c
@@ -1,0 +1,676 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+/* FIXME:
+ * - selectmove/selectspawnmove: manage corpses again
+ * - db: get rid of void types
+ * - fix selection/showmoverange
+ * - tchs: global rule: only one move per turn, no more (and no less?)
+ * - tchs: implement custom move/attack ranges and checks
+ * - tchs: implement no attack (attack -> move onto unit)
+ * - tchs: moving onto existing unit makes it disappear (cleanly)
+ * - resupply cost (default 0)
+ * - ratio, repair, destroy, launch, food, ammo, cloak lists
+ * - food/fuel consumption rate list
+ * - alternate attack mode ammo/...
+ * - bless, curse/slow, raise lists
+ * - terrain bonuses lists: atk, def, vis
+ * -> tatkm=water elemental=15
+ * -> tdefm=water elemental=3
+ * -> tvism=mountain heavy=2 (and inf=2? or any unit?)
+ * - tbs2: unit and terrain sprites
+ * - tbs1: sprites for rest of units
+ * - manpages: tbs(1), tbs(6)?
+ * - proper victory screen
+ * - win condition: last team left standing (next turn -> current team)
+ * - quit: confirm with changed cursor
+ * - move validation and networking model
+ * - ai: optional plugin for an artificial networked player
+ * - savegames
+ * - fow
+ * - weather
+ * - diplomacy/trading
+ * - superteams (factions/groups), shared economies
+ */
+
+Team team[Nteam];
+int nteam, curteam;
+int turn, gameover;
+int initmoney, unitcap, firstturnnoinc, nocorpse;
+Map *saved, *unique;
+
+typedef struct Fn Fn;
+struct Fn{
+ char *name;
+ int (*check)(void);
+ void (*fn)(void);
+};
+
+static Munit *old, *new, *savedcorpse;
+
+static void selectmenu(void);
+static void selecttile(void);
+static void selectaction(void);
+static void selectspawnmove(void);
+static void selectmove(void);
+static void confirmmove(void);
+
+static void
+freeunit(Map *m)
+{
+ free(m->u);
+ m->u = nil;
+}
+
+static void
+updatecorpses(void)
+{
+ Map *m;
+
+ for(m=map; m<mape; m++)
+ if(m->u != nil && m->u->decaydt && --m->u->decaydt == 0)
+ freeunit(m);
+}
+
+static int
+iscorpse(Map *m)
+{
+ Munit *mu;
+
+ mu = m->u;
+ if(mu != nil && strcmp(mu->u->name, "corpse") == 0)
+ return 1;
+ return 0;
+}
+
+static void
+canmove_(Map *m, Map *ref, Unit *u, int mp)
+{
+ if(m->movep >= mp)
+ return;
+ if(m != ref){
+ if(m->u != nil && m->u->team != curteam && !iscorpse(m))
+ return;
+ if(mp < u->move[m->t->move] + 1)
+ return;
+ mp -= u->move[m->t->move] + 1;
+ }
+ m->canmove = 1;
+ if(mp <= 0)
+ return;
+ m->movep = mp;
+ if((m+1-map) % mapwidth != 0)
+ canmove_(m+1, ref, u, mp);
+ if(m+mapwidth < mape)
+ canmove_(m+mapwidth, ref, u, mp);
+ if(m-mapwidth >= map)
+ canmove_(m-mapwidth, ref, u, mp);
+ if((m-map) % mapwidth != 0)
+ canmove_(m-1, ref, u, mp);
+}
+
+static void
+canmove(void)
+{
+ canmove_(selected2, selected2, selected2->u->u, selected2->u->u->mp);
+}
+
+static void
+canattack_(Map *m, int r1, int r2)
+{
+ if(m->canatk)
+ return;
+ if(r1-- <= 0 && m != selected2){
+ m->canatk = 1;
+ if(m->u != nil
+ && m->u->team != curteam
+ && !iscorpse(m)
+ && selected2->u->team == curteam)
+ m->atkp = 1;
+ }
+ if(r2-- == 0)
+ return;
+ if((m+1-map) % mapwidth != 0)
+ canattack_(m+1, r1, r2);
+ if(m+mapwidth < mape)
+ canattack_(m+mapwidth, r1, r2);
+ if(m-mapwidth >= map)
+ canattack_(m-mapwidth, r1, r2);
+ if((m-map) % mapwidth != 0)
+ canattack_(m-1, r1, r2);
+}
+
+static void
+canattack(void)
+{
+ canattack_(selected2, selected2->u->u->rmin, selected2->u->u->rmax);
+}
+
+static void
+clrmoverange(int domove)
+{
+ Map *m;
+
+ for(m=map; m<mape; m++){
+ if(domove){
+ m->movep = 0;
+ m->canmove = 0;
+ }
+ m->atkp = 0;
+ m->canatk = 0;
+ }
+}
+
+static void
+showmoverange(int domove)
+{
+ clrmoverange(domove);
+ if(selected2->u == nil)
+ return;
+ if(domove)
+ canmove();
+ canattack();
+}
+
+static void
+exitmenu(int domove)
+{
+ selected2 = saved;
+ menuon = 0;
+ if(domove)
+ selectfn = selectspawnmove;
+ else{
+ saved = nil;
+ selectfn = selecttile;
+ }
+}
+
+static void
+spawn(void)
+{
+ Unit *u;
+
+ u = saved->t->spawn.u[selected2 - map];
+ old = saved->u;
+ spawnunit(saved, u, curteam);
+ new = saved->u;
+ team[curteam].money -= unique != nil && team[curteam].unique->u == u ? team[curteam].unique->ecost : u->cost;
+ exitmenu(1);
+}
+
+static void
+selectmenu(void)
+{
+ int cost;
+ char *i[] = {"spawn", nil};
+ void (*fn[])(void) = {spawn};
+ Tunit *tu;
+ Unit *u;
+ Map *m;
+
+ tu = &saved->t->spawn;
+ if(tu->u + (selected2 - map) >= tu->e){
+ exitmenu(0);
+ return;
+ }
+ if(selected2 != selected)
+ return;
+ u = tu->u[selected2 - map];
+ if(u->unique){
+ if(unique != nil)
+ return;
+ else
+ cost = team[curteam].unique->ecost;
+ }else
+ cost = u->cost;
+ selectfn = selectmenu;
+ clrmoverange(1);
+ canmove_(saved, saved, u, u->mp);
+ for(m=map; m<mape; m++)
+ if(m->canmove)
+ break;
+ if(m == mape || team[curteam].money < cost)
+ clrmoverange(1);
+ else
+ lmenu(i, fn);
+}
+
+static void
+spawnlist(void)
+{
+ Map *m;
+
+ unique = nil;
+ if(team[curteam].unique != nil)
+ for(m=map; m<mape; m++)
+ if(m->u == team[curteam].unique){
+ unique = m;
+ break;
+ }
+ saved = selected2;
+ selected2 = nil;
+ selectfn = selectmenu;
+ menuon = 1;
+}
+
+static int
+checkspawn(void)
+{
+ if(saved != nil
+ || team[curteam].nunit >= unitcap
+ || selected2->team != curteam
+ || selected2->t->spawn.u == nil)
+ return 0;
+ return 1;
+}
+
+static void
+endmove(void)
+{
+ clrmoverange(1);
+ selected2->u->done = 1;
+ selected2->u->decaydt = 0;
+ selected2 = nil;
+ if(old != nil){
+ saved->u = old;
+ old = nil;
+ }
+ new = nil;
+ saved = nil;
+ selectfn = selecttile;
+}
+
+static int
+checkend(void)
+{
+ if(selected2->u == nil
+ || selected2->u->team != curteam)
+ return 0;
+ return 1;
+}
+
+static void
+selectspawnmove(void)
+{
+ if(selected2 == nil || selected2 == saved && old != nil)
+ goto again;
+ showmoverange(0);
+ redraw(0);
+ if(selected2 == selected){
+ selectaction();
+ return;
+ }else if(selected2->canmove
+ && (selected2->u == nil || iscorpse(selected2))
+ && (old == nil || selected2 != saved)){
+ if(iscorpse(selected2))
+ savedcorpse = selected2->u;
+ selected2->u = selected->u;
+ selected->u = nil;
+ return;
+ }
+again:
+ if(selected != nil && selected->u == new)
+ selected->u = nil;
+ saved->u = new;
+ selected2 = saved;
+}
+
+static void
+commitmove(void)
+{
+ if(saved == nil){
+ selected2->u = selected->u;
+ selected->u = nil;
+ saved = selected;
+ }else{
+ saved->u = selected->u;
+ selected->u = nil;
+ saved = nil;
+ }
+}
+
+static void
+cancelmove(void)
+{
+ if(saved != nil){
+ commitmove();
+ if(savedcorpse != nil){
+ selected2->u = savedcorpse;
+ savedcorpse = nil;
+ }
+ if(old != nil){
+ saved->u = old;
+ old = nil;
+ }
+ saved = nil;
+ selectfn = selecttile;
+ }else
+ selectfn = selectmove;
+ selected2 = nil;
+ clrmoverange(1);
+}
+
+static void
+confirmmove(void)
+{
+ if(selected2 == selected){
+ showmoverange(1);
+ redraw(0);
+ selectaction();
+ }else
+ cancelmove();
+}
+
+static void
+selectmove(void)
+{
+ if(selected2 == nil)
+ return;
+ /* FIXME: manage corpses */
+ if(selected2->canmove && selected2->u == nil){
+ commitmove();
+ selectfn = confirmmove;
+ }else{
+ selectfn = selecttile;
+ selected2 = nil;
+ }
+}
+
+static void
+move(void)
+{
+ selectfn = selectmove;
+}
+
+static int
+checkmove(void)
+{
+ if(saved != nil
+ || selected2->u == nil
+ || selected2->u->team != curteam)
+ return 0;
+ return 1;
+}
+
+static void
+die(Map *m)
+{
+ Munit *mu;
+ Unit *u;
+
+ mu = m->u;
+ team[mu->team].nunit--;
+ if(mu == team[mu->team].unique){
+ mu->ecost += mu->u->cost;
+ m->u = nil;
+ return;
+ }
+ if(!nocorpse)
+ for(u=unit; u<unit+nunit; u++)
+ if(strcmp(u->name, "corpse") == 0){
+ mu->u = u;
+ mu->decaydt = 2;
+ }
+}
+
+static void
+rankup(Munit *mu)
+{
+ mu->eatk += 2;
+ mu->edef += 2;
+}
+
+static int
+attackunit(Map *to, Map *from)
+{
+ int a, d, r, n;
+ Munit *mu, *mv;
+
+ mu = from->u;
+ mv = to->u;
+ r = 100; /* FIXME: damage ratio */
+ a = (mu->eatk + mu->atkm + nrand(mu->u->Δatk)) * r / 100 * mu->hp / 100;
+ if(a <= 0)
+ return 0;
+ d = mv->edef + mu->defm + to->t->def * 5;
+ n = a - d;
+ if(n < 0)
+ n = 0;
+ if(mv->hp < n)
+ n = mv->hp;
+ mv->hp -= n;
+ if(r = mv->hp == 0)
+ die(to);
+ n = mu->xp / 100;
+ mu->xp += a;
+ if(mu->xp / 100 > n)
+ rankup(mu);
+ return r;
+}
+
+static void
+selectattack(void)
+{
+ if(!selected2->atkp)
+ goto nope;
+ if(attackunit(selected2, selected) == 0
+ && selected2->u->u->rmin == 1
+ && (selected2 - selected == 1
+ || selected2 - selected == -1
+ || selected2 - selected == mapwidth
+ || selected2 - selected == -mapwidth))
+ attackunit(selected, selected2);
+ selected2 = selected;
+ endmove();
+ return;
+nope:
+ selected2 = nil;
+ selectfn = selecttile;
+}
+
+static void
+attack(void)
+{
+ selectfn = selectattack;
+}
+
+static int
+checkattack(void)
+{
+ Map *m;
+
+ if(saved != nil && selected->u->u->rmin > 1
+ || selected2->u == nil
+ || selected2->u->team != curteam)
+ return 0;
+ for(m=map; m<mape; m++)
+ if(m->atkp)
+ return 1;
+ return 0;
+}
+
+static void
+occupy(void)
+{
+ team[selected2->team].nbuild--;
+ team[selected2->u->team].nbuild++;
+ if(selected2->t->spawn.u != nil){
+ team[selected2->team].nprod--;
+ team[selected2->u->team].nprod++;
+ }
+ selected2->team = selected2->u->team;
+ team[selected2->team].income += selected2->t->income;
+ endmove();
+}
+
+static int
+checkoccupy(void)
+{
+ Unit **u;
+ Tunit *tu;
+
+ if(selected2->u == nil
+ || selected2->team == curteam)
+ return 0;
+ tu = &selected2->t->occupy;
+ if(selected2->u->team == curteam){
+ for(u=tu->u; u<tu->e; u++)
+ if(*u == selected2->u->u)
+ return 1;
+ }
+ return 0;
+}
+
+static Fn fn[] = {
+ {"spawn", checkspawn, spawnlist},
+ {"occupy", checkoccupy, occupy},
+ {"attack", checkattack, attack},
+ {"move", checkmove, move},
+ {"end", checkend, endmove},
+};
+
+static void
+selectaction(void)
+{
+ char *i[nelem(fn)+1] = {nil}, **ip = i;
+ void (*fns[nelem(fn)])(void) = {nil}, (**fp)(void) = fns;
+ Fn *f;
+
+ if(selected2 != selected)
+ goto end;
+ for(f=fn; f<fn+nelem(fn); f++)
+ if(f->check()){
+ *ip++ = f->name;
+ *fp++ = f->fn;
+ }
+ if(ip > i)
+ lmenu(i, fns);
+ else{
+end:
+ clrmoverange(1);
+ selectfn = selecttile;
+ }
+}
+
+static void
+selecttile(void)
+{
+ selectfn = selectaction;
+ showmoverange(1);
+}
+
+void
+spawnunit(Map *m, Unit *u, int t)
+{
+ Munit *mu;
+
+ if(t < 0 || t > nelem(team))
+ t = 0;
+ if(u->unique && curteam != 0 && team[curteam].unique != nil){
+ mu = team[curteam].unique;
+ }else{
+ mu = emalloc(sizeof *m->u);
+ if(t > nteam)
+ nteam = t;
+ if(u->unique){
+ if(team[t].unique != nil)
+ sysfatal("spawnunit: team %d uniqueness violation\n", t);
+ team[t].unique = mu;
+ }
+ mu->u = u;
+ mu->team = t;
+ mu->ecost = u->cost;
+ mu->eatk = u->atk;
+ mu->edef = u->def;
+ }
+ mu->hp = 100;
+ team[t].nunit++;
+ m->u = mu;
+}
+
+static void
+resupply(void)
+{
+ Map *m;
+ Unit **u;
+ Munit *mu;
+ Terrain *t;
+ Tunit *tu;
+
+ for(m=map; m<mape; m++){
+ mu = m->u;
+ t = m->t;
+ tu = t->resupply;
+ if(mu == nil
+ || mu->team != curteam
+ || mu->hp == 100
+ || tu == nil)
+ continue;
+ for(u=tu->u; u<tu->e; u++)
+ if(mu->u == *u)
+ break;
+ if(u == tu->e)
+ continue;
+ mu->hp += 20;
+ if(mu->hp > 100)
+ mu->hp = 100;
+ }
+}
+
+static void
+newturn(void)
+{
+ if(turn > 0 || !firstturnnoinc)
+ team[curteam].money += team[curteam].income;
+ resupply();
+}
+
+void
+endturn(void)
+{
+ Team *t;
+ Map *m;
+
+ if(saved != nil)
+ cancelmove();
+ menuon = 0;
+ selectfn = selecttile;
+ selected = selected2 = nil;
+ for(m=map; m<mape; m++)
+ if(m->u != nil)
+ m->u->done = 0;
+ for(t=team+curteam+1; t<=team+nteam; t++)
+ if(t->nunit > 0 || t->nprod > 0)
+ break;
+ if(t == team+nteam+1){
+ turn++;
+ updatecorpses();
+ for(t=team+1; t<team+curteam; t++)
+ if(t->nunit > 0 || t->nprod > 0)
+ break;
+ }
+ if(t == team+curteam)
+ gameover = 1;
+ curteam = t - team;
+ newturn();
+ redraw(0);
+}
+
+void
+initgame(void)
+{
+ Team *t;
+
+ for(t=team; t<=team+nteam; t++){
+ t->money = initmoney;
+ if(t > team && curteam == 0
+ && (t->nunit > 0 || t->nprod > 0))
+ curteam = t - team;
+ }
+ if(curteam == 0)
+ sysfatal("initgame: the only winning move is not to play");
+ newturn();
+ selectfn = selecttile;
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,30 @@
+</$objtype/mkfile
+TARG=\
+ tbs1\
+ tbs2\
+ tchs\
+
+OFILES=\
+ db.$O\
+ drw.$O\
+ game.$O\
+ tbs.$O\
+
+HFILES=dat.h fns.h
+</sys/src/cmd/mkmany
+BIN=$home/bin/$objtype
+
+$O.tbs1: $OFILES tbs1.$O
+ $LD -o $target $prereq
+
+$O.tbs2: $OFILES tbs2.$O
+ $LD -o $target $prereq
+
+$O.tchs: $OFILES tchs.$O
+ $LD -o $target $prereq
+
+sysinstall:V:
+ mkdir -p /sys/games/lib/^(tbs1 tbs2 tchs)
+ dircp tbs1 /sys/games/lib/tbs1
+ dircp tbs2 /sys/games/lib/tbs2
+ dircp tchs /sys/games/lib/tchs
--- /dev/null
+++ b/tbs.c
@@ -1,0 +1,171 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+extern Point pan;
+void select(Point);
+
+char *dbname, *prefix, *mapname = "map1.db";
+
+static Keyboardctl *kctl;
+static Mousectl *mctl;
+
+void
+lmenu(char **i, void (*fn[])(void))
+{
+ int n;
+ static Menu m;
+
+ m.item = i;
+ if((n = menuhit(1, mctl, &m, nil)) >= 0)
+ fn[n]();
+}
+
+static void
+mmenu(void)
+{
+ enum{
+ END,
+ SPC,
+ QUIT
+ };
+ static char *i[] = {
+ [END] "end turn",
+ [SPC] "",
+ [QUIT] "quit",
+ nil
+ };
+ Menu m = {
+ .item = i
+ };
+
+ if(gameover)
+ m.item += 2;
+ switch(menuhit(2, mctl, &m, nil)){
+ case END: if(!gameover){ endturn(); break; } /* wet floor */
+ case QUIT: threadexitsall(nil);
+ }
+}
+
+char *
+estrdup(char *s)
+{
+ if((s = strdup(s)) == nil)
+ sysfatal("estrdup: %r");
+ setmalloctag(s, getcallerpc(&s));
+ return s;
+}
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ if((p = mallocz(n, 1)) == nil)
+ sysfatal("emalloc: %r");
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+void *
+erealloc(void *p, ulong n, ulong on)
+{
+ void *q;
+
+ q = emalloc(n);
+ if(p != nil && on > 0)
+ memmove(q, p, on);
+ free(p);
+ return q;
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-d db] [-m map]\n", argv0);
+ threadexits("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ Rune r;
+ Mouse m, om;
+
+ ARGBEGIN{
+ case 'd': dbname = EARGF(usage()); break;
+ case 'm': mapname = EARGF(usage()); break;
+ default: usage();
+ }ARGEND
+ if(dbname == nil)
+ dbname = smprint("%s.db", progname);
+ if(prefix == nil)
+ prefix = smprint("/sys/games/lib/%s", progname);
+ srand(time(nil));
+ init();
+ if(initdraw(nil, nil, progname) < 0)
+ sysfatal("initdraw: %r");
+ if((kctl = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
+ if((mctl = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+ initimages();
+ resetdraw();
+ Alt a[] = {
+ {mctl->c, &m, CHANRCV},
+ {mctl->resizec, nil, CHANRCV},
+ {kctl->c, &r, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ for(;;){
+ switch(alt(a)){
+ default:
+ sysfatal("alt: %r");
+ case 0:
+ if((m.buttons & 1) == 1 && (om.buttons & 1) == 0)
+ select(m.xy);
+ if(m.buttons & 2)
+ mmenu();
+ if(m.buttons & 4)
+ dopan(m.xy.x - om.xy.x, m.xy.y - om.xy.y);
+ om = m;
+ break;
+ case 1:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ resetdraw();
+ break;
+ case 2:
+ switch(r){
+ case '+':
+ case '=':
+ if(scale < 16){
+ scale++;
+ resetdraw();
+ }
+ break;
+ case '-':
+ if(scale > 1){
+ scale--;
+ resetdraw();
+ }
+ break;
+ case 'r':
+ scale = 1;
+ pan = ZP;
+ resetdraw();
+ break;
+ case Kesc:
+ selected = nil;
+ redraw(0);
+ break;
+ }
+ break;
+ }
+ }
+}
--- /dev/null
+++ b/tbs1.c
@@ -1,0 +1,6 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+char *progname = "tbs1";
binary files /dev/null b/tbs1/1.bit differ
binary files /dev/null b/tbs1/2.bit differ
binary files /dev/null b/tbs1/3.bit differ
binary files /dev/null b/tbs1/4.bit differ
binary files /dev/null b/tbs1/5.bit differ
binary files /dev/null b/tbs1/6.bit differ
binary files /dev/null b/tbs1/7.bit differ
binary files /dev/null b/tbs1/8.bit differ
binary files /dev/null b/tbs1/9.bit differ
binary files /dev/null b/tbs1/castle.bit differ
binary files /dev/null b/tbs1/castle1.bit differ
binary files /dev/null b/tbs1/castle2.bit differ
binary files /dev/null b/tbs1/castle3.bit differ
binary files /dev/null b/tbs1/castle4.bit differ
binary files /dev/null b/tbs1/city.bit differ
binary files /dev/null b/tbs1/city1.bit differ
binary files /dev/null b/tbs1/city2.bit differ
binary files /dev/null b/tbs1/city3.bit differ
binary files /dev/null b/tbs1/city4.bit differ
binary files /dev/null b/tbs1/corpse.bit differ
binary files /dev/null b/tbs1/cursor.bit differ
binary files /dev/null b/tbs1/elemental.bit differ
binary files /dev/null b/tbs1/elemental1.bit differ
binary files /dev/null b/tbs1/elemental2.bit differ
binary files /dev/null b/tbs1/elemental3.bit differ
binary files /dev/null b/tbs1/elemental4.bit differ
binary files /dev/null b/tbs1/fighter.bit differ
binary files /dev/null b/tbs1/fighter1.bit differ
binary files /dev/null b/tbs1/fighter2.bit differ
binary files /dev/null b/tbs1/fighter3.bit differ
binary files /dev/null b/tbs1/fighter4.bit differ
binary files /dev/null b/tbs1/fort.bit differ
binary files /dev/null b/tbs1/hill.bit differ
--- /dev/null
+++ b/tbs1/map1.db
@@ -1,0 +1,17 @@
+unitcap=10
+map=castle1 unit=paladin1
+map=plain
+map=plain
+map=plain
+map=plain
+map=city
+map=plain
+map=plain
+map=plain
+map=plain
+map=city
+map=plain
+map=plain
+map=plain
+map=plain
+map=castle2 unit=paladin2
--- /dev/null
+++ b/tbs1/map2.db
@@ -1,0 +1,225 @@
+map=mountain
+map=plain
+map=road
+map=hill
+map=water
+map=water
+map=water
+map=wood
+map=hill
+map=mountain
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=castle1 unit=paladin1
+map=road
+map=city
+map=water
+map=water
+map=wood
+map=plain
+map=city
+map=wood
+map=wood
+map=mountain
+map=water
+map=water
+map=water
+map=road
+map=road
+map=road
+map=road
+map=road # bridge
+map=road # bridge
+map=road
+map=road
+map=plain
+map=hill
+map=mountain
+map=wood
+map=mountain
+map=hill
+map=wood
+map=mountain
+map=city1
+map=road
+map=wood
+map=water
+map=water
+map=mountain
+map=road
+map=mountain
+map=wood
+map=wood
+map=plain
+map=wood
+map=plain
+map=hill
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=water
+map=road
+map=plain
+map=wood
+map=mountain
+map=hill
+map=plain
+map=castle
+map=mountain
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=water
+map=road
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=plain
+map=wood
+map=hill
+map=plain
+map=wood
+map=mountain
+map=water
+map=water
+map=road
+map=city
+map=road
+map=plain
+map=city
+map=plain
+map=wood
+map=wood
+map=wood
+map=plain
+map=wood
+map=hill
+map=plain
+map=road
+map=road
+map=road
+map=road
+map=road
+map=hill
+map=wood
+map=wood
+map=wood
+map=mountain
+map=hill
+map=plain
+map=plain
+map=city
+map=mountain
+map=road
+map=city
+map=road
+map=water
+map=water
+map=plain
+map=wood
+map=mountain
+map=water
+map=wood
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=plain
+map=road
+map=water
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=wood
+map=castle
+map=mountain
+map=plain
+map=hill
+map=wood
+map=wood
+map=road
+map=water
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=mountain
+map=hill
+map=plain
+map=wood
+map=mountain
+map=wood
+map=mountain
+map=road
+map=mountain
+map=water
+map=water
+map=wood
+map=road
+map=city2
+map=mountain
+map=wood
+map=mountain
+map=hill
+map=mountain
+map=hill
+map=mountain
+map=plain
+map=road
+map=road
+map=road # bridge
+map=road # bridge
+map=road
+map=road
+map=road
+map=road
+map=water
+map=water
+map=water
+map=wood
+map=wood
+map=hill
+map=city
+map=plain
+map=wood
+map=water
+map=water
+map=city2
+map=road
+map=castle2 unit=paladin2
+map=road
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=wood
+map=plain
+map=wood
+map=water
+map=water
+map=water
+map=hill
+map=road
+map=plain
+map=mountain
--- /dev/null
+++ b/tbs1/map3.db
@@ -1,0 +1,161 @@
+mapwidth=16
+map=wood
+map=mountain
+map=hill
+map=wood
+map=water
+map=water
+map=water
+map=wood
+map=wood
+map=mountain
+map=castle
+map=plain
+map=mountain
+map=wood
+map=wood
+map=mountain
+map=mountain
+map=castle1 unit=paladin1
+map=plain
+map=city
+map=water
+map=water
+map=water
+map=wood
+map=plain
+map=road
+map=road
+map=road
+map=road
+map=hill
+map=mountain
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=water
+map=water
+map=wood
+map=plain
+map=city
+map=road
+map=hill
+map=wood
+map=road
+map=plain
+map=plain
+map=wood
+map=wood
+map=hill
+map=wood
+map=road
+map=water
+map=water
+map=mountain
+map=hill
+map=plain
+map=road
+map=mountain
+map=plain
+map=road
+map=city
+map=hill
+map=mountain
+map=plain
+map=mountain
+map=city
+map=road
+map=water
+map=water
+map=plain
+map=road
+map=road
+map=road
+map=plain
+map=road
+map=road
+map=plain
+map=wood
+map=wood
+map=plain
+map=hill
+map=wood
+map=road
+map=wood
+map=wood
+map=plain
+map=road
+map=city
+map=water
+map=water
+map=road
+map=hill
+map=plain
+map=mountain
+map=wood
+map=wood
+map=plain
+map=hill
+map=road
+map=wood
+map=plain
+map=city
+map=road
+map=hill
+map=water
+map=water
+map=road
+map=city
+map=hill
+map=castle2 unit=paladin2
+map=mountain
+map=mountain
+map=plain
+map=mountain
+map=road
+map=hill
+map=wood
+map=mountain
+map=road
+map=water
+map=water
+map=water
+map=road
+map=road
+map=road
+map=road
+map=road
+map=wood
+map=hill
+map=plain
+map=road
+map=castle
+map=road
+map=road
+map=road
+map=water
+map=water
+map=water
+map=wood
+map=plain
+map=hill
+map=plain
+map=wood
+map=wood
+map=wood
+map=mountain
+map=road
+map=road
+map=road
+map=hill
+map=mountain
+map=water
+map=water
+map=water
+map=mountain
+map=wood
+map=mountain
+map=wood
+map=wood
--- /dev/null
+++ b/tbs1/map4.db
@@ -1,0 +1,210 @@
+mapwidth=11
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=wood
+map=mountain
+map=mountain
+map=wood
+map=water
+map=water
+map=water
+map=wood
+map=castle2 unit=paladin2
+map=hill
+map=plain
+map=wood
+map=mountain
+map=mountain
+map=wood
+map=plain
+map=castle3 unit=paladin3
+map=hill
+map=road
+map=road
+map=wood
+map=hill
+map=mountain
+map=mountain
+map=mountain
+map=road
+map=road
+map=road
+map=road
+map=city2
+map=road
+map=road
+map=road
+map=mountain
+map=mountain
+map=wood
+map=road
+map=mountain
+map=wood
+map=city3
+map=mountain
+map=plain
+map=wood
+map=road
+map=wood
+map=mountain
+map=wood
+map=road
+map=hill
+map=plain
+map=hill
+map=wood
+map=mountain
+map=plain
+map=road
+map=plain
+map=city
+map=hill
+map=road
+map=plain
+map=mountain
+map=wood
+map=water
+map=water
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=hill
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=city
+map=wood
+map=road
+map=wood
+map=city
+map=mountain
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=wood
+map=water
+map=water
+map=mountain
+map=mountain
+map=plain
+map=road
+map=plain
+map=city
+map=plain
+map=road
+map=road
+map=road
+map=mountain
+map=hill
+map=plain
+map=wood
+map=road
+map=hill
+map=mountain
+map=wood
+map=hill
+map=wood
+map=road
+map=hill
+map=city4
+map=hill
+map=mountain
+map=road
+map=wood
+map=mountain
+map=wood
+map=mountain
+map=wood
+map=road
+map=city1
+map=road
+map=road
+map=road
+map=road
+map=mountain
+map=mountain
+map=mountain
+map=hill
+map=plain
+map=road
+map=road
+map=wood
+map=castle4 unit=paladin4
+map=hill
+map=plain
+map=wood
+map=mountain
+map=mountain
+map=wood
+map=plain
+map=castle1 unit=paladin1
+map=wood
+map=water
+map=water
+map=water
+map=wood
+map=mountain
+map=mountain
+map=wood
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
--- /dev/null
+++ b/tbs1/map5.db
@@ -1,0 +1,225 @@
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=hill
+map=plain
+map=wood
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=hill
+map=plain
+map=city
+map=castle1 unit=paladin1
+map=plain
+map=wood
+map=mountain
+map=hill
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=mountain
+map=water
+map=water
+map=water
+map=water
+map=plain
+map=road
+map=city
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=city
+map=road
+map=plain
+map=water
+map=water
+map=water
+map=wood
+map=hill
+map=road
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=road
+map=hill
+map=wood
+map=water
+map=water
+map=hill
+map=plain
+map=road
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=road
+map=city
+map=plain
+map=water
+map=water
+map=plain
+map=castle3 unit=paladin3
+map=road
+map=road # bridge
+map=road # bridge
+map=road # bridge
+map=city
+map=road # bridge
+map=road # bridge
+map=road # bridge
+map=road
+map=castle4 unit=paladin4
+map=hill
+map=water
+map=water
+map=hill
+map=city
+map=road
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=road
+map=plain
+map=mountain
+map=water
+map=water
+map=wood
+map=wood
+map=road
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=road
+map=hill
+map=wood
+map=water
+map=water
+map=water
+map=mountain
+map=road
+map=city
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=city
+map=road
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=plain
+map=hill
+map=plain
+map=castle2 unit=paladin2
+map=city
+map=hill
+map=plain
+map=mountain
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=hill
+map=wood
+map=plain
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
--- /dev/null
+++ b/tbs1/map6.db
@@ -1,0 +1,225 @@
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=wood
+map=hill
+map=wood
+map=wood
+map=wood
+map=mountain
+map=hill
+map=wood
+map=plain
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=city1
+map=plain
+map=mountain
+map=plain
+map=castle1 unit=paladin1
+map=hill
+map=plain
+map=hill
+map=city1
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=hill
+map=water
+map=water
+map=water
+map=water
+map=water
+map=road
+map=hill
+map=mountain
+map=mountain
+map=mountain
+map=mountain
+map=mountain
+map=hill
+map=road
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=road
+map=wood
+map=mountain
+map=city
+map=hill
+map=city
+map=mountain
+map=wood
+map=road
+map=city
+map=water
+map=water
+map=water
+map=water
+map=city
+map=road
+map=hill
+map=plain
+map=plain
+map=wood
+map=plain
+map=hill
+map=plain
+map=road
+map=water
+map=water
+map=water
+map=water
+map=water
+map=plain
+map=road
+map=wood
+map=mountain
+map=city
+map=plain
+map=city
+map=mountain
+map=wood
+map=road
+map=water
+map=water
+map=water
+map=water
+map=water
+map=hill
+map=road
+map=plain
+map=mountain
+map=mountain
+map=mountain
+map=mountain
+map=mountain
+map=plain
+map=road
+map=water
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=road
+map=water
+map=water
+map=water
+map=water
+map=water
+map=plain
+map=city2
+map=plain
+map=mountain
+map=hill
+map=castle2 unit=paladin2
+map=plain
+map=wood
+map=hill
+map=city2
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=water
+map=water
+map=wood
+map=wood
+map=plain
+map=wood
+map=mountain
+map=plain
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
--- /dev/null
+++ b/tbs1/map7.db
@@ -1,0 +1,225 @@
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=hill
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=hill
+map=mountain
+map=water
+map=water
+map=wood
+map=castle1 unit=paladin1
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=wood
+map=mountain
+map=water
+map=water
+map=mountain
+map=plain
+map=plain
+map=city
+map=water
+map=water
+map=water
+map=water
+map=water
+map=city
+map=plain
+map=castle4 unit=paladin4
+map=hill
+map=water
+map=water
+map=water
+map=city
+map=hill
+map=wood
+map=mountain
+map=water
+map=water
+map=water
+map=wood
+map=road
+map=road
+map=road
+map=wood
+map=water
+map=water
+map=water
+map=water
+map=road
+map=road
+map=road
+map=road
+map=road # bridge
+map=road # bridge
+map=road
+map=road
+map=wood
+map=road
+map=city
+map=water
+map=water
+map=water
+map=water
+map=road
+map=mountain
+map=wood
+map=mountain
+map=water
+map=water
+map=water
+map=water
+map=wood
+map=road
+map=plain
+map=water
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=city
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=road
+map=mountain
+map=water
+map=water
+map=water
+map=water
+map=road # bridge
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=hill
+map=road
+map=wood
+map=water
+map=water
+map=city
+map=road
+map=road
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=city
+map=road
+map=hill
+map=water
+map=water
+map=mountain
+map=road
+map=city
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=road
+map=city
+map=water
+map=water
+map=hill
+map=road
+map=road
+map=road # bridge
+map=road # bridge
+map=road
+map=road
+map=road # bridge
+map=road # bridge
+map=road # bridge
+map=road
+map=road
+map=plain
+map=water
+map=water
+map=wood
+map=castle2 unit=paladin2
+map=wood
+map=water
+map=water
+map=mountain
+map=city
+map=water
+map=water
+map=water
+map=hill
+map=castle3 unit=paladin3
+map=wood
+map=water
+map=water
+map=mountain
+map=wood
+map=plain
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=mountain
+map=wood
+map=hill
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
+map=water
binary files /dev/null b/tbs1/mountain.bit differ
binary files /dev/null b/tbs1/paladin.bit differ
binary files /dev/null b/tbs1/paladin1.bit differ
binary files /dev/null b/tbs1/paladin2.bit differ
binary files /dev/null b/tbs1/paladin3.bit differ
binary files /dev/null b/tbs1/paladin4.bit differ
binary files /dev/null b/tbs1/plain.bit differ
binary files /dev/null b/tbs1/ranger.bit differ
binary files /dev/null b/tbs1/ranger1.bit differ
binary files /dev/null b/tbs1/ranger2.bit differ
binary files /dev/null b/tbs1/ranger3.bit differ
binary files /dev/null b/tbs1/ranger4.bit differ
binary files /dev/null b/tbs1/road.bit differ
binary files /dev/null b/tbs1/ruin.bit differ
--- /dev/null
+++ b/tbs1/tbs1.db
@@ -1,0 +1,56 @@
+initmoney=500
+firstturnnoinc=1
+
+unit=paladin atk=55 Δatk=10 def=20 mp=4 cost=200 unique=1
+unit=fighter atk=50 Δatk=5 def=5 mp=4 cost=150
+unit=ranger atk=50 Δatk=5 range=1,2 mp=4 cost=250
+unit=elemental atk=50 Δatk=5 mp=4 cost=300
+ # damage + defence bonus when on water
+ # (if move == water and move == terrain type?)
+#unit=necromancer atk=40 Δatk=5 def=5 mp=4 cost=400
+# # animate dead
+#unit=priest atk=35 Δatk=5 def=10 mp=4 cost=500
+# # bless units in 2 square grid
+#unit=mage atk=60 Δatk=5 def=15 mp=5 cost=600
+# # slow/curse attacked unit
+#unit=golem atk=60 Δatk=10 def=30 mp=4 cost=600
+#unit=catapult atk=50 Δatk=20 range=2,4 def=10 mp=3 cost=700
+#unit=dragon atk=70 Δatk=10 def=25 mp=6 cost=1000
+#unit=risen atk=40 Δatk=10 def=2 mp=4
+unit=corpse
+# # dies on the 2 turn after spawn, without spawning another
+# # players can move on top of it, destroying it
+
+# FIXME: damage ratios, 100 by default
+#ratio=ranger dragon=150
+
+# FIXME: terrain bonuses
+
+move=road
+move=hill paladin=1 fighter=1 ranger=1 elemental=1
+move=mountain paladin=2 fighter=2 ranger=2 elemental=2
+move=water paladin=2 fighter=2 ranger=2
+
+terrain=road move=road
+terrain=plain def=1 move=road
+terrain=hill def=2 move=hill
+terrain=wood def=2 move=hill
+terrain=mountain def=3 move=mountain
+terrain=water move=water
+terrain=castle def=3 move=road income=50
+terrain=city def=3 move=road income=30
+terrain=ruin def=3 move=road
+terrain=fort def=3 move=road
+
+spawn=castle unit=paladin unit=fighter unit=ranger unit=elemental
+
+resupply=castle spawn=castle
+resupply=city spawn=castle
+resupply=fort spawn=castle
+
+occupy=castle unit=paladin
+occupy=city unit=paladin unit=fighter
+
+#destroy=city terrain=ruin unit=catapult
+
+#repair=ruin terrain=city unit=paladin unit=fighter
binary files /dev/null b/tbs1/water.bit differ
binary files /dev/null b/tbs1/wood.bit differ
--- /dev/null
+++ b/tbs2.c
@@ -1,0 +1,6 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+char *progname = "tbs2";
binary files /dev/null b/tbs2/1.bit differ
binary files /dev/null b/tbs2/2.bit differ
binary files /dev/null b/tbs2/3.bit differ
binary files /dev/null b/tbs2/4.bit differ
binary files /dev/null b/tbs2/5.bit differ
binary files /dev/null b/tbs2/6.bit differ
binary files /dev/null b/tbs2/7.bit differ
binary files /dev/null b/tbs2/8.bit differ
binary files /dev/null b/tbs2/9.bit differ
binary files /dev/null b/tbs2/aab.bit differ
binary files /dev/null b/tbs2/aab1.bit differ
binary files /dev/null b/tbs2/aab2.bit differ
binary files /dev/null b/tbs2/aab3.bit differ
binary files /dev/null b/tbs2/aab4.bit differ
binary files /dev/null b/tbs2/aam.bit differ
binary files /dev/null b/tbs2/aam1.bit differ
binary files /dev/null b/tbs2/aam2.bit differ
binary files /dev/null b/tbs2/aam3.bit differ
binary files /dev/null b/tbs2/aam4.bit differ
binary files /dev/null b/tbs2/apu.bit differ
binary files /dev/null b/tbs2/apu1.bit differ
binary files /dev/null b/tbs2/apu2.bit differ
binary files /dev/null b/tbs2/apu3.bit differ
binary files /dev/null b/tbs2/apu4.bit differ
binary files /dev/null b/tbs2/arty.bit differ
binary files /dev/null b/tbs2/arty1.bit differ
binary files /dev/null b/tbs2/arty2.bit differ
binary files /dev/null b/tbs2/arty3.bit differ
binary files /dev/null b/tbs2/arty4.bit differ
binary files /dev/null b/tbs2/bomber.bit differ
binary files /dev/null b/tbs2/bomber1.bit differ
binary files /dev/null b/tbs2/bomber2.bit differ
binary files /dev/null b/tbs2/bomber3.bit differ
binary files /dev/null b/tbs2/bomber4.bit differ
binary files /dev/null b/tbs2/bridge.bit differ
binary files /dev/null b/tbs2/chopper.bit differ
binary files /dev/null b/tbs2/chopper1.bit differ
binary files /dev/null b/tbs2/chopper2.bit differ
binary files /dev/null b/tbs2/chopper3.bit differ
binary files /dev/null b/tbs2/chopper4.bit differ
binary files /dev/null b/tbs2/cursor.bit differ
binary files /dev/null b/tbs2/etank.bit differ
binary files /dev/null b/tbs2/etank1.bit differ
binary files /dev/null b/tbs2/etank2.bit differ
binary files /dev/null b/tbs2/etank3.bit differ
binary files /dev/null b/tbs2/etank4.bit differ
binary files /dev/null b/tbs2/factory.bit differ
binary files /dev/null b/tbs2/factory1.bit differ
binary files /dev/null b/tbs2/factory2.bit differ
binary files /dev/null b/tbs2/factory3.bit differ
binary files /dev/null b/tbs2/factory4.bit differ
binary files /dev/null b/tbs2/fighter.bit differ
binary files /dev/null b/tbs2/fighter1.bit differ
binary files /dev/null b/tbs2/fighter2.bit differ
binary files /dev/null b/tbs2/fighter3.bit differ
binary files /dev/null b/tbs2/fighter4.bit differ
binary files /dev/null b/tbs2/heavy.bit differ
binary files /dev/null b/tbs2/heavy1.bit differ
binary files /dev/null b/tbs2/heavy2.bit differ
binary files /dev/null b/tbs2/heavy3.bit differ
binary files /dev/null b/tbs2/heavy4.bit differ
binary files /dev/null b/tbs2/heli.bit differ
binary files /dev/null b/tbs2/heli1.bit differ
binary files /dev/null b/tbs2/heli2.bit differ
binary files /dev/null b/tbs2/heli3.bit differ
binary files /dev/null b/tbs2/heli4.bit differ
binary files /dev/null b/tbs2/htank.bit differ
binary files /dev/null b/tbs2/htank1.bit differ
binary files /dev/null b/tbs2/htank2.bit differ
binary files /dev/null b/tbs2/htank3.bit differ
binary files /dev/null b/tbs2/htank4.bit differ
binary files /dev/null b/tbs2/inf.bit differ
binary files /dev/null b/tbs2/inf1.bit differ
binary files /dev/null b/tbs2/inf2.bit differ
binary files /dev/null b/tbs2/inf3.bit differ
binary files /dev/null b/tbs2/inf4.bit differ
binary files /dev/null b/tbs2/lav.bit differ
binary files /dev/null b/tbs2/lav1.bit differ
binary files /dev/null b/tbs2/lav2.bit differ
binary files /dev/null b/tbs2/lav3.bit differ
binary files /dev/null b/tbs2/lav4.bit differ
binary files /dev/null b/tbs2/ltank.bit differ
binary files /dev/null b/tbs2/ltank1.bit differ
binary files /dev/null b/tbs2/ltank2.bit differ
binary files /dev/null b/tbs2/ltank3.bit differ
binary files /dev/null b/tbs2/ltank4.bit differ
--- /dev/null
+++ b/tbs2/map1.db
@@ -1,0 +1,16 @@
+map=factory1
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=bridge
+map=factory2
binary files /dev/null b/tbs2/missile.bit differ
binary files /dev/null b/tbs2/missile1.bit differ
binary files /dev/null b/tbs2/missile2.bit differ
binary files /dev/null b/tbs2/missile3.bit differ
binary files /dev/null b/tbs2/missile4.bit differ
binary files /dev/null b/tbs2/mtank.bit differ
binary files /dev/null b/tbs2/mtank1.bit differ
binary files /dev/null b/tbs2/mtank2.bit differ
binary files /dev/null b/tbs2/mtank3.bit differ
binary files /dev/null b/tbs2/mtank4.bit differ
binary files /dev/null b/tbs2/rkt.bit differ
binary files /dev/null b/tbs2/rkt1.bit differ
binary files /dev/null b/tbs2/rkt2.bit differ
binary files /dev/null b/tbs2/rkt3.bit differ
binary files /dev/null b/tbs2/rkt4.bit differ
binary files /dev/null b/tbs2/stealth.bit differ
binary files /dev/null b/tbs2/stealth1.bit differ
binary files /dev/null b/tbs2/stealth2.bit differ
binary files /dev/null b/tbs2/stealth3.bit differ
binary files /dev/null b/tbs2/stealth4.bit differ
--- /dev/null
+++ b/tbs2/tbs2.db
@@ -1,0 +1,137 @@
+nocorpse=
+
+unit=inf vis=2 mp=3 cost=1000
+ # ammo 0, food 99 (-1)
+unit=heavy vis=2 mp=2 cost=3000
+ # ammo 3,-1, food 70
+unit=lav vis=5 mp=8 cost=4000
+ # ammo -1, food 80
+unit=ltank vis=3 mp=6 cost=7000
+ # ammo 9,-1, food 70
+unit=mtank vis=1 mp=5 cost=16000
+ # ammo 8,-1, food 50
+unit=etank vis=1 mp=6 cost=22000
+ # ammo 9,-1, food 99
+unit=htank vis=1 mp=4 cost=28000
+ # ammo 3,-1, food 50
+unit=arty vis=1 mp=5 range=2,3 cost=6000
+ # ammo 9, food 50
+unit=rkt vis=1 mp=5 range=3,5 cost=15000
+ # ammo 6, food 50
+unit=aab vis=2 mp=6 cost=8000
+ # ammo 9, food 60
+unit=aam vis=4 mp=5 range=3,5 cost=12000
+ # ammo 6, food 50
+unit=apu vis=1 mp=6 cost=5000
+ # no weapon
+ # resupply fuel and ammo to adjacent units
+ # load up to 1 inf/heavy
+ # food 70
+unit=fighter vis=2 mp=9 cost=20000
+ # ammo 9, food 99
+unit=bomber vis=2 mp=7 cost=22000
+ # ammo 9, food 99
+unit=stealth vis=4 mp=6 cost=24000
+ # cloak/uncloak
+ # ammo 6, food 60
+unit=chopper vis=3 mp=6 cost=9000
+ # ammo 6,-1, food 99
+unit=heli vis=2 mp=6 cost=5000
+ # no weapon
+ # load up to 1 inf/heavy
+ # food 99
+unit=missile vis=1 mp=9 cost=24000
+ # no weapon
+ # trigger -> aoe 1,3 damage=50
+unit=lander vis=1 mp=6 cost=12000
+ # no weapon
+ # load up to 1 inf/heavy
+ # food 99
+unit=repboat vis=1 mp=7 cost=7500
+ # no weapon
+ # load 2 inf/heavy
+ # repair unit
+ # food 60
+unit=cruiser vis=3 mp=6 cost=18000
+ # ammo 9, food 99
+ # can carry 2 heli?
+unit=sub vis=5 mp=5 cost=20000
+ # ammo 6, food 60
+ # cloak/uncloak
+unit=btlship vis=2 mp=5 range=2,6 cost=28000
+ # ammo 9, food 99
+unit=carrier vis=4 mp=5 range=3,8 cost=30000
+ # ammo 9, food 99
+ # load/unload/resupply (but not repair?) 2 air units
+
+move=bridge # changed to allow any unit
+move=road
+ lander=9 repboat=9 cruiser=9 sub=9 btlship=9 carrier=9
+move=plain
+ lav=1 arty=1 rkt=1 aam=1
+ lander=9 repboat=9 cruiser=9 sub=9 btlship=9 carrier=9
+move=wood
+ lav=2 arty=2 rkt=2 aam=2
+ ltank=1 mtank=1 etank=1 htank=1 aab=1 apu=1
+ lander=9 repboat=9 cruiser=9 sub=9 btlship=9 carrier=9
+move=mountain
+ inf=1
+ lav=9 arty=9 rkt=9 aam=9
+ ltank=9 mtank=9 etank=9 htank=9 aab=9 apu=9
+ lander=9 repboat=9 cruiser=9 sub=9 btlship=9 carrier=9
+move=sea
+ inf=9 heavy=9
+ lav=9 arty=9 rkt=9 aam=9
+ ltank=9 mtank=9 etank=9 htank=9 aab=9 apu=9
+move=shoal
+ cruiser=9 sub=9 btlship=9 carrier=9
+move=reef
+ inf=9 heavy=9
+ lav=9 arty=9 rkt=9 aam=9
+ ltank=9 mtank=9 etank=9 htank=9 aab=9 apu=9
+ lander=1 repboat=1 cruiser=1 sub=1 btlship=1 carrier=1
+move=mordor
+ inf=9 heavy=9
+ lav=9 arty=9 rkt=9 aam=9
+ ltank=9 mtank=9 etank=9 htank=9 aab=9 apu=9
+ fighter=9 bomber=9 stealth=9 missile=9 chopper=9 heli=9
+ lander=9 repboat=9 cruiser=9 sub=9 btlship=9 carrier=9
+
+terrain=bridge move=bridge
+terrain=road move=road
+terrain=plain move=plain
+terrain=wood move=wood def=1
+terrain=mountain move=mountain def=3
+terrain=river move=mountain
+terrain=sea move=sea
+terrain=shoal move=shoal
+terrain=reef move=reef
+terrain=mordor move=mordor
+terrain=hq move=road def=3 income=1000
+terrain=city move=road def=2 income=1000
+terrain=factory move=road def=2 income=1000
+terrain=airport move=road def=2 income=1000
+terrain=shipyard move=bridge def=2 income=1000
+terrain=silo move=road def=2
+ # launch: aoe 1,3 damage=30, only once
+terrain=tower move=road def=2
+ # global atk bonus
+
+spawn=factory unit=inf unit=heavy unit=lav unit=arty unit=rkt unit=aab unit=aam unit=apu unit=ltank unit=mtank unit=etank unit=htank
+spawn=airport unit=fighter unit=bomber unit=stealth unit=missile unit=chopper unit=heli
+spawn=shipyard unit=lander unit=repboat unit=cruiser unit=sub unit=btlship unit=carrier
+
+resupply=hq spawn=factory
+resupply=city spawn=factory
+resupply=factory spawn=factory
+resupply=airport spawn=airport
+resupply=shipyard spawn=shipyard
+
+occupy=hq unit=inf unit=heavy
+occupy=city unit=inf unit=heavy
+occupy=factory unit=inf unit=heavy
+occupy=airport unit=inf unit=heavy
+occupy=shipyard unit=inf unit=heavy
+occupy=tower unit=inf unit=heavy
+
+# FIXME: http://warswiki.org/wiki/Damage/Advance_Wars:_Dual_Strike_chart
--- /dev/null
+++ b/tchs.c
@@ -1,0 +1,6 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+char *progname = "tchs";
binary files /dev/null b/tchs/bishop1.bit differ
binary files /dev/null b/tchs/bishop2.bit differ
--- /dev/null
+++ b/tchs/chess.db
@@ -1,0 +1,6 @@
+unit=pawn
+unit=bishop
+unit=knight
+unit=rook
+unit=queen
+unit=king
binary files /dev/null b/tchs/cursor.bit differ
binary files /dev/null b/tchs/king1.bit differ
binary files /dev/null b/tchs/king2.bit differ
binary files /dev/null b/tchs/knight1.bit differ
binary files /dev/null b/tchs/knight2.bit differ
--- /dev/null
+++ b/tchs/map1.db
@@ -1,0 +1,64 @@
+map=tile0 unit=rook2
+map=tile1 unit=knight2
+map=tile0 unit=bishop2
+map=tile1 unit=king2
+map=tile0 unit=queen2
+map=tile1 unit=bishop2
+map=tile0 unit=knight2
+map=tile1 unit=rook2
+map=tile1 unit=pawn2
+map=tile0 unit=pawn2
+map=tile1 unit=pawn2
+map=tile0 unit=pawn2
+map=tile1 unit=pawn2
+map=tile0 unit=pawn2
+map=tile1 unit=pawn2
+map=tile0 unit=pawn2
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile1
+map=tile0
+map=tile0 unit=pawn1
+map=tile1 unit=pawn1
+map=tile0 unit=pawn1
+map=tile1 unit=pawn1
+map=tile0 unit=pawn1
+map=tile1 unit=pawn1
+map=tile0 unit=pawn1
+map=tile1 unit=pawn1
+map=tile1 unit=rook1
+map=tile0 unit=knight1
+map=tile1 unit=bishop1
+map=tile0 unit=king1
+map=tile1 unit=queen1
+map=tile0 unit=bishop1
+map=tile1 unit=knight1
+map=tile0 unit=rook1
binary files /dev/null b/tchs/pawn1.bit differ
binary files /dev/null b/tchs/pawn2.bit differ
binary files /dev/null b/tchs/queen1.bit differ
binary files /dev/null b/tchs/queen2.bit differ
binary files /dev/null b/tchs/rook1.bit differ
binary files /dev/null b/tchs/rook2.bit differ
--- /dev/null
+++ b/tchs/tchs.db
@@ -1,0 +1,9 @@
+unit=pawn
+unit=bishop
+unit=knight
+unit=rook
+unit=queen
+unit=king
+
+terrain=tile0
+terrain=tile1
binary files /dev/null b/tchs/tile0.bit differ
binary files /dev/null b/tchs/tile1.bit differ