ref: 45e74ee185103abd827298d0e9b264811d60f0e7
parent: 0622cb8e9d9874ee52e9881415554bc38570551d
author: qwx <[email protected]>
date: Thu Aug 5 14:08:17 EDT 2021
sim: generalize moving into a queuable action player issues commands, each command is essentially a script with a hardcoded sequence of actions, and is pushed to a mobj's command queue. practically all commands begin by moving the unit somewhere. the command stores goal map coordinates and unit id's to avoid races; actions then decide how to use these. once an action sequence completes, the command is popped from the queue and the next one is executed (untested until move waypoints are added). a new transient mobj state is set between transitions. also, look ma: oop! this essentially implements a shitty way to script behavior... pending better solution. sim.c is split by action/state so as to be more manageable. some refactoring to make function names more expressive.
--- a/com.c
+++ b/com.c
@@ -59,30 +59,17 @@
}
static Mobj *
-getmobj(Mobj *r)
+mobjfromreq(Mobj *r)
{
- int n;
Mobj *mo;
- Team *t;
- n = r->idx >> Teamshift & Nteam - 1;
- if(n < 0 || n > nteam){
- werrstr("invalid team number %d", n);
+ if((mo = derefmobj(r->idx, r->uuid)) == nil)
return nil;
- }
- t = teams + n;
- n = r->idx & Teamidxmask;
- if(n > t->sz || (mo = t->mo[n]) == nil){
- werrstr("obj index %d out of bounds", n);
+ if(mo->x != r->x || mo->y != r->y){
+ werrstr("phase error: req mobj at %d,%d, found %s at %d,%d",
+ r->x, r->y, mo->o->name, mo->x, mo->y);
return nil;
}
- if(mo->idx != r->idx || mo->uuid != r->uuid
- || mo->x != r->x || mo->y != r->y){
- werrstr("phase error: %s at %d,%d has %#ux,%ld, req has %d,%d %#ux,%ld",
- mo->o->name, mo->x, mo->y, mo->idx, mo->uuid,
- r->x, r->y, r->idx, r->uuid);
- return nil;
- }
return mo;
}
@@ -98,19 +85,21 @@
&click.x, &click.y,
&reqt.idx, &reqt.uuid, &reqt.x, &reqt.y)) < 0)
return -1;
- if((mo = getmobj(&reqm)) == nil)
+ if((mo = mobjfromreq(&reqm)) == nil)
return -1;
if((mo->o->f & Fimmutable) || mo->o->speed == 0.0){
werrstr("reqmovenear: object %s can't move", mo->o->name);
return -1;
}
- if((tgt = getmobj(&reqt)) == nil)
+ if((tgt = mobjfromreq(&reqt)) == nil)
return -1;
if(click.x >= nodemapwidth || click.y >= nodemapheight){
werrstr("reqmovenear: invalid location %d,%d", click.x, click.y);
return -1;
}
- moveone(click, mo, tgt);
+ clearcommands(mo);
+ if(pushmovecommand(click, mo, tgt) < 0)
+ return -1;
return n;
}
@@ -125,7 +114,7 @@
&reqm.idx, &reqm.uuid, &reqm.x, &reqm.y,
&tgt.x, &tgt.y)) < 0)
return -1;
- if((mo = getmobj(&reqm)) == nil)
+ if((mo = mobjfromreq(&reqm)) == nil)
return -1;
if((mo->o->f & Fimmutable) || mo->o->speed == 0.0){
werrstr("reqmove: object %s can't move", mo->o->name);
@@ -135,7 +124,9 @@
werrstr("reqmove: invalid target %d,%d", tgt.x, tgt.y);
return -1;
}
- moveone(tgt, mo, nil);
+ clearcommands(mo);
+ if(pushmovecommand(tgt, mo, nil) < 0)
+ return -1;
return n;
}
--- a/dat.h
+++ b/dat.h
@@ -5,6 +5,8 @@
typedef struct Pics Pics;
typedef struct Obj Obj;
typedef struct Path Path;
+typedef struct Action Action;
+typedef struct Command Command;
typedef struct Munit Munit;
typedef struct Mresource Mresource;
typedef struct Mobj Mobj;
@@ -108,6 +110,7 @@
OState2,
OState3,
OSend,
+ OSskymaybe = 666,
/* unit */
OSidle = OState0,
@@ -134,6 +137,8 @@
PFalpha = 1<<14,
PFshadow = 1<<15,
PFtile = 1<<16,
+
+ Ncmd = 32,
};
struct Obj{
char *name;
@@ -169,7 +174,6 @@
int amount;
};
struct Munit{
- int state;
int team;
int hp;
int xp;
@@ -181,13 +185,30 @@
double u;
double v;
double speed;
- Mobjl *movingp;
- Mobjl *mapp;
+ Mobjl *mobjl;
+ Mobjl *mapl;
};
+struct Command{
+ int os;
+ char *name;
+ int (*initfn)(Mobj*);
+ Point goal;
+ vlong arg[4];
+};
+struct Action{
+ int os;
+ char *name;
+ void (*stepfn)(Mobj*);
+ void (*cleanupfn)(Mobj*);
+};
struct Mobj{
Obj *o;
int idx;
long uuid;
+ int state;
+ Action *actp;
+ Command cmds[Ncmd];
+ int ctail;
Point;
int px;
int py;
--- a/fns.h
+++ b/fns.h
@@ -8,11 +8,20 @@
int sendpause(void);
void stepsnd(void);
void initsnd(void);
-void updatemoves(void);
void linktomap(Mobj*);
-int moveone(Point, Mobj*, Mobj*);
+int pushmovecommand(Point, Mobj*, Mobj*);
+void resourcestate(Mobj*);
+void idlestate(Mobj*);
+Mobj* derefmobj(int, long);
+void nextaction(Mobj*);
+int pushactions(Mobj*, Action*);
+void clearcommands(Mobj*);
+void abortcommands(Mobj*);
+void popcommand(Mobj*);
+Command* pushcommand(Mobj*);
Mobjl* linkmobj(Mobjl*, Mobj*, Mobjl*);
void unlinkmobj(Mobjl*);
+void refmobj(Mobj*);
void stepsim(void);
void initsim(void);
void initsv(int, char*);
@@ -36,6 +45,7 @@
int findpath(Point, Mobj*);
Mobj* mapspawn(int, int, Obj*);
void initmap(void);
+Mobj* derefmobj(int, long);
int spawnunit(int, int, Obj*, int);
int spawnresource(int, int, Obj*, int);
void nukequeue(Pairheap**);
--- a/mkfile
+++ b/mkfile
@@ -12,7 +12,10 @@
pheap.$O\
sce.$O\
sim.$O\
+ sim.idle.$O\
sim.move.$O\
+ sim.resource.$O\
+ sim.spawn.$O\
snd.$O\
sv.$O\
util.$O\
--- a/sim.c
+++ b/sim.c
@@ -8,6 +8,8 @@
int nteam;
int initres[Nresource], foodcap;
+static Mobjl mobjl0 = {.l = &mobjl0, .lp = &mobjl0}, *mobjl = &mobjl0;
+
Mobjl *
linkmobj(Mobjl *l, Mobj *mo, Mobjl *p)
{
@@ -31,12 +33,13 @@
ml->lp = ml->l = nil;
}
-static void
+void
refmobj(Mobj *mo)
{
int n, i;
Team *t;
+ mo->mobjl = linkmobj(mobjl, mo, nil);
t = teams + mo->team;
if(mo->o->f & (Fbuild|Fimmutable))
t->nbuild++;
@@ -55,55 +58,104 @@
t->firstempty = i;
}
-int
-spawnunit(int x, int y, Obj *o, int team)
+void
+nextaction(Mobj *mo)
{
- Mobj *mo;
+ assert(mo->actp != nil);
+ if(mo->actp->cleanupfn != nil)
+ mo->actp->cleanupfn(mo);
+ mo->actp++;
+ if((mo->state = mo->actp->os) == OSskymaybe){
+ dprint("A nextaction %s %#p: done\n", mo->o->name, mo);
+ mo->actp = nil;
+ popcommand(mo);
+ return;
+ }
+ dprint("A nextaction %s %#p: %s\n", mo->o->name, mo, mo->actp->name);
+}
- if((mo = mapspawn(x, y, o)) == nil)
- return -1;
- mo->team = team;
- mo->θ = frand() * 256;
- mo->hp = o->hp;
- mo->state = OSidle;
- refmobj(mo);
+int
+pushactions(Mobj *mo, Action *a)
+{
+ mo->actp = a;
+ mo->state = a->os;
+ dprint("A pushaction %s %#p: %s\n", mo->o->name, mo, a->name);
return 0;
}
-int
-spawnresource(int x, int y, Obj *o, int amount)
+void
+clearcommands(Mobj *mo)
{
- int *t, *te;
+ dprint("C clearcommand %s %#p: %s\n", mo->o->name, mo, mo->cmds[0].name);
+ if(mo->actp != nil && mo->actp->cleanupfn != nil)
+ mo->actp->cleanupfn(mo);
+ mo->actp = nil;
+ memset(mo->cmds, 0, sizeof mo->cmds);
+ mo->ctail = 0;
+ idlestate(mo);
+}
+
+void
+abortcommands(Mobj *mo)
+{
+ dprint("C abortcommand %s %#p: %s\n", mo->o->name, mo, mo->cmds[0].name);
+ clearcommands(mo);
+}
+
+void
+popcommand(Mobj *mo)
+{
+ dprint("C popcommand %s %#p: %s\n", mo->o->name, mo, mo->cmds[0].name);
+ if(--mo->ctail > 0){
+ memmove(mo->cmds, mo->cmds+1, mo->ctail * sizeof *mo->cmds);
+ mo->state = OSskymaybe;
+ }else
+ clearcommands(mo);
+}
+
+Command *
+pushcommand(Mobj *mo)
+{
+ Command *c;
+
+ fprint(2, "pushcommand %s %#p\n", mo->o->name, mo);
+ if(mo->ctail >= nelem(mo->cmds)){
+ werrstr("command buffer overflow");
+ return nil;
+ }
+ c = mo->cmds + mo->ctail++;
+ if(mo->state == OSidle)
+ mo->state = OSskymaybe;
+ return c;
+}
+
+static void
+updatemobj(void)
+{
+ Mobjl *ml, *next;
Mobj *mo;
- Resource *r;
- if(amount <= 0){
- werrstr("spawnresource: invalid amount");
- return -1;
+ for(ml=mobjl->l, next=ml->l; ml!=mobjl; ml=next, next=next->l){
+ mo = ml->mo;
+ if(mo->state == OSidle)
+ continue;
+ if(mo->actp == nil
+ && (mo->cmds[0].initfn(mo) < 0 || mo->actp == nil || mo->state == OSskymaybe)){
+ /* FIXME: always skymaybe */
+ abortcommands(mo);
+ continue;
+ }
+ if(mo->state == OSskymaybe)
+ sysfatal("updatemobj: %s cmd %s impossible/stale state %d",
+ mo->o->name, mo->cmds[0].name, mo->state);
+ mo->actp->stepfn(mo);
}
- if((mo = mapspawn(x, y, o)) == nil)
- return -1;
- mo->team = 0;
- mo->amount = amount;
- mo->state = OSrich;
- r = o->res;
- for(t=r->thresh, te=t+r->nthresh; t<te; t++){
- if(amount >= *t)
- break;
- mo->state++;
- }
- if(mo->state >= OSend){
- dprint("spawnresource %s %d,%d: invalid state %d\n", o->name, x, y, mo->state);
- mo->state = OSpoor;
- }
- refmobj(mo);
- return 0;
}
void
stepsim(void)
{
- updatemoves();
+ updatemobj();
}
void
--- /dev/null
+++ b/sim.idle.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static void
+freezefrm(Mobj *mo, int oldstate)
+{
+ Pics *old, *new;
+
+ old = mo->o->pics[oldstate];
+ new = mo->o->pics[OSidle];
+ if(!new->freeze || !old->shared){
+ mo->freezefrm = 0;
+ return;
+ }
+ mo->freezefrm = tc % old[PTbase].nf;
+ if(mo->freezefrm > new[PTbase].nf)
+ sysfatal("idle:freezefrm obj %s: invalid frame number %d > %d",
+ mo->o->name, mo->freezefrm, new[PTbase].nf);
+}
+
+void
+idlestate(Mobj *mo)
+{
+ freezefrm(mo, mo->state);
+ mo->state = OSidle;
+}
--- a/sim.move.c
+++ b/sim.move.c
@@ -4,8 +4,6 @@
#include "dat.h"
#include "fns.h"
-static Mobjl moving0 = {.l = &moving0, .lp = &moving0}, *moving = &moving0;
-
void
linktomap(Mobj *mo)
{
@@ -12,7 +10,7 @@
Map *m;
m = map + mo->y / Node2Tile * mapwidth + mo->x / Node2Tile;
- mo->mapp = linkmobj(mo->o->f & Fair ? m->ml.lp : &m->ml, mo, mo->mapp);
+ mo->mapl = linkmobj(mo->o->f & Fair ? m->ml.lp : &m->ml, mo, mo->mapl);
}
static void
@@ -25,7 +23,7 @@
}
static double
-facemobj(Point p, Mobj *mo)
+facegoal(Point p, Mobj *mo)
{
int dx, dy;
double vx, vy, d, θ, θ256, Δθ;
@@ -56,83 +54,60 @@
}
static void
-freemove(Mobj *mo)
+nextpathnode(Mobj *mo)
{
- Pics *old, *new;
+ resetcoords(mo);
+ facegoal(*mo->pathp, mo);
+}
- unlinkmobj(mo->movingp);
+static void
+clearpath(Mobj *mo)
+{
+ mo->speed = 0.0;
mo->pathp = nil;
- old = mo->o->pics[mo->state];
- new = mo->o->pics[OSidle];
- if(new->freeze && old->shared){
- mo->freezefrm = tc % old[PTbase].nf;
- if(mo->freezefrm > new[PTbase].nf)
- sysfatal("freemove obj %s: invalid frame number %d > %d",
- mo->o->name, mo->freezefrm, new[PTbase].nf);
- }else
- mo->freezefrm = 0;
- mo->state = OSidle;
resetcoords(mo);
}
static void
-nextmove(Mobj *mo)
+cleanup(Mobj *mo)
{
- resetcoords(mo);
- facemobj(*mo->pathp, mo);
+ clearpath(mo);
+ mo->target = (Point){0,0};
+ mo->goalblocked = 0;
+ mo->pathlen = 0.0;
+ mo->npatherr = 0;
}
+static void
+movedone(Mobj *mo)
+{
+ dprint("mobj %s %#p successfully reached goal\n", mo->o->name, mo);
+ nextaction(mo);
+}
+
+static void
+abortmove(Mobj *mo)
+{
+ werrstr("move aborted");
+ abortcommands(mo);
+}
+
static int
repath(Point p, Mobj *mo)
{
- freemove(mo);
+ clearpath(mo);
mo->target = p;
if(findpath(p, mo) < 0){
- mo->θ = facemobj(p, mo);
+ mo->θ = facegoal(p, mo);
return -1;
}
- mo->movingp = linkmobj(moving, mo, mo->movingp);
mo->pathp = mo->paths;
- mo->state = OSmove;
- nextmove(mo);
+ nextpathnode(mo);
return 0;
}
-int
-moveone(Point p, Mobj *mo, Mobj *block)
-{
- setgoal(&p, mo, block);
- if(repath(p, mo) < 0){
- mo->speed = 0.0;
- dprint("move to %d,%d: %r\n", p.x, p.y);
- return -1;
- }
- return 0;
-}
-
-static int
-tryturn(Mobj *mo)
-{
- int r;
- double Δθ;
-
- r = 1;
- if(mo->Δθ <= mo->o->turn){
- r = 0;
- Δθ = mo->Δθ;
- }else
- Δθ = mo->o->turn;
- mo->θ += mo->Δθs * Δθ;
- if(mo->θ < 0)
- mo->θ += 256;
- else if(mo->θ >= 256)
- mo->θ -= 256;
- mo->Δθ -= Δθ;
- return r;
-}
-
static void
-updatespeed(Mobj *mo)
+accelerate(Mobj *mo)
{
if(1 + mo->pathlen < (mo->speed / 8) * (mo->speed / 8) / 2 / (mo->o->accel / 8)){
mo->speed -= mo->o->accel;
@@ -224,78 +199,143 @@
}
static int
-domove(Mobj *mo)
+continuemove(Mobj *mo)
{
int r;
- updatespeed(mo);
- unlinkmobj(mo->mapp);
+ accelerate(mo);
+ unlinkmobj(mo->mapl);
r = trymove(mo);
linktomap(mo);
return r;
}
+static int
+tryturn(Mobj *mo)
+{
+ int r;
+ double Δθ;
+
+ r = 1;
+ if(mo->Δθ <= mo->o->turn){
+ r = 0;
+ Δθ = mo->Δθ;
+ }else
+ Δθ = mo->o->turn;
+ mo->θ += mo->Δθs * Δθ;
+ if(mo->θ < 0)
+ mo->θ += 256;
+ else if(mo->θ >= 256)
+ mo->θ -= 256;
+ mo->Δθ -= Δθ;
+ return r;
+}
+
+static int
+nodereached(Mobj *mo)
+{
+ return mo->px == mo->pathp->x && mo->py == mo->pathp->y;
+}
+
static void
-stepmove(Mobj *mo)
+step(Mobj *mo)
{
- int n;
+ int nerr;
- n = 0;
+ nerr = 0;
restart:
- n++;
if(tryturn(mo))
return;
- if(domove(mo) < 0){
- if(n > 1){
- fprint(2, "stepmove: %s %#p bug inducing infinite loop!\n",
- mo->o->name, mo);
+ if(continuemove(mo) < 0){
+ if(nerr > 1){
+ fprint(2, "stepmove: %s %#p bug: infinite loop!\n", mo->o->name, mo);
return;
}
- dprint("stepmove: failed to move: %r\n");
+ dprint("stepmove: %s %#p failed moving to %d,%d from %d,%d: %r\n",
+ mo->o->name, mo, mo->pathp->x, mo->pathp->y, mo->px, mo->py);
if(repath(mo->target, mo) < 0){
- dprint("stepmove: %s %#p moving towards target: %r\n",
- mo->o->name, mo);
- mo->speed = 0.0;
+ dprint("stepmove: %s %#p moving towards target: %r\n", mo->o->name, mo);
+ abortcommands(mo);
return;
}
+ nerr++;
goto restart;
}
- if(mo->px == mo->pathp->x && mo->py == mo->pathp->y){
- mo->pathp++;
- if(mo->pathp < mo->pathe){
- nextmove(mo);
- return;
- }else if(mo->x == mo->target.x && mo->y == mo->target.y){
- mo->npatherr = 0;
- mo->speed = 0.0;
- freemove(mo);
- return;
- }
- dprint("stepmove: %s %#p reached final node, but not target\n",
+ if(!nodereached(mo))
+ return;
+ mo->pathp++;
+ if(mo->pathp < mo->pathe){
+ nextpathnode(mo);
+ return;
+ }else if(mo->x == mo->target.x && mo->y == mo->target.y){
+ movedone(mo);
+ return;
+ }
+ dprint("stepmove: %s %#p reached final node, but not target\n",
+ mo->o->name, mo);
+ if(mo->goalblocked && isblocked(mo->target.x, mo->target.y, mo->o)){
+ dprint("stepmove: %s %#p goal still blocked, stopping\n", mo->o->name, mo);
+ abortmove(mo);
+ return;
+ }
+ dprint("stepmove: %s %#p trying again\n", mo->o->name, mo);
+ if(mo->npatherr++ > 1 || repath(mo->target, mo) < 0){
+ dprint("stepmove: %s %#p still can't reach target: %r\n",
mo->o->name, mo);
- if(mo->goalblocked && isblocked(mo->target.x, mo->target.y, mo->o)){
- dprint("stepmove: %s %#p goal still blocked, stopping\n",
- mo->o->name, mo);
- mo->speed = 0.0;
- freemove(mo);
- return;
- }
- if(mo->npatherr++ > 1
- || repath(mo->target, mo) < 0){
- dprint("stepmove: %s %#p trying to find target: %r\n",
- mo->o->name, mo);
- mo->npatherr = 0;
- mo->speed = 0.0;
- freemove(mo);
- }
+ abortmove(mo);
+ return;
}
}
-void
-updatemoves(void)
+static Action acts[] = {
+ {
+ .os = OSmove,
+ .name = "moving",
+ .stepfn = step,
+ .cleanupfn = cleanup,
+ },
+ {
+ .os = OSskymaybe,
+ }
+};
+
+int
+newmove(Mobj *mo)
{
- Mobjl *ml, *oml;
+ Point goal;
+ Mobj *block;
+ Command *c;
- for(oml=moving->l, ml=oml->l; oml!=moving; oml=ml, ml=ml->l)
- stepmove(oml->mo);
+ c = mo->cmds;
+ goal = c->goal;
+ block = nil;
+ if(c->arg[0] >= 0 && (block = derefmobj(c->arg[0], c->arg[1])) == nil)
+ return -1;
+ setgoal(&goal, mo, block);
+ if(repath(goal, mo) < 0)
+ return -1;
+ if(pushactions(mo, acts) < 0)
+ return -1;
+ return 0;
+}
+
+int
+pushmovecommand(Point goal, Mobj *mo, Mobj *block)
+{
+ Command *c;
+
+ if((c = pushcommand(mo)) == nil){
+ fprint(2, "pushmovecommand: %r\n");
+ return -1;
+ }
+ c->os = OSmove;
+ c->name = "move";
+ c->initfn = newmove;
+ c->goal = goal;
+ if(block != nil){
+ c->arg[0] = block->idx;
+ c->arg[1] = block->uuid;
+ }else
+ c->arg[0] = -1;
+ return 0;
}
--- /dev/null
+++ b/sim.resource.c
@@ -1,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+resourcestate(Mobj *mo)
+{
+ int os, *t, *te;
+ Resource *r;
+
+ r = mo->o->res;
+ for(os=OSrich, t=r->thresh, te=t+r->nthresh; t<te; t++, os++)
+ if(mo->amount >= *t)
+ break;
+ mo->state = os;
+}
--- /dev/null
+++ b/sim.spawn.c
@@ -1,0 +1,64 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+Mobj *
+derefmobj(int idx, long uuid)
+{
+ int n;
+ Mobj *mo;
+ Team *t;
+
+ n = idx >> Teamshift & Nteam - 1;
+ if(n < 0 || n > nteam){
+ werrstr("invalid team number %d", n);
+ return nil;
+ }
+ t = teams + n;
+ n = idx & Teamidxmask;
+ if(n > t->sz || (mo = t->mo[n]) == nil){
+ werrstr("mobj index %d out of bounds or missing", n);
+ return nil;
+ }
+ if(mo->idx != idx || mo->uuid != uuid){
+ werrstr("phase error: %#ux,%ld → %s %#ux,%ld",
+ idx, uuid, mo->o->name, mo->idx, mo->uuid);
+ return nil;
+ }
+ return mo;
+}
+
+int
+spawnunit(int x, int y, Obj *o, int team)
+{
+ Mobj *mo;
+
+ if((mo = mapspawn(x, y, o)) == nil)
+ return -1;
+ mo->team = team;
+ mo->θ = frand() * 256;
+ mo->hp = o->hp;
+ idlestate(mo);
+ refmobj(mo);
+ return 0;
+}
+
+int
+spawnresource(int x, int y, Obj *o, int amount)
+{
+ Mobj *mo;
+
+ if(amount <= 0){
+ werrstr("spawnresource: invalid amount");
+ return -1;
+ }
+ if((mo = mapspawn(x, y, o)) == nil)
+ return -1;
+ mo->team = 0;
+ mo->amount = amount;
+ resourcestate(mo);
+ refmobj(mo);
+ return 0;
+}