History

convert ai from old state machine to new behavior trees

mischief committed 9 years ago

Commit ef328b71 parent ad3734d11 files · +421 −356Browse files

Unified · Split

11 files
  1. ai.c +358 −274
  2. dat.h +10 −23
  3. hack9.c +5 −4
  4. item.c +3 −0
  5. level.c +5 −2
  6. levelgen.c +10 −5
  7. monst.c +7 −45
  8. path.c +3 −0
  9. tile.c +3 −0
  10. ui.c +14 −3
  11. util.c +3 −0
ai.c+358−274
@@ -3,360 +3,444 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "bt.h"
7+#include "map.h"
8+
69 #include "dat.h"
710 #include "alg.h"
811
9−AIState*
10−mkstate(char *name, Monster *m, void *aux, AIFun enter, AIFun exec, AIFun exit)
11−{
12− AIState *i;
13− i = mallocz(sizeof(AIState), 1);
14− assert(i != nil);
15− snprint(i->name, sizeof(i->name), "%s", name);
16− i->m = m;
17− i->aux = aux;
18− i->enter = enter;
19− i->exec = exec;
20− i->exit = exit;
21− return i;
22−}
23−
24−void
25−freestate(AIState *a)
26−{
27− free(a);
28−}
29−
3012 enum
3113 {
32− IDLERADIUS = 4,
14+ IDLERADIUS = 6,
3315 IDLEIDLE = 4,
3416 };
3517
36−typedef struct idledata idledata;
37−struct idledata
18+enum
3819 {
39− int idle;
40− int attacking;
20+ AIPOINT = 0,
21+ AIINT,
22+ AIMONST,
23+ AIPATH,
24+ AIWALKTO,
4125 };
4226
43−static void
44−idleenter(AIState *a)
27+typedef struct AiData AiData;
28+struct AiData
4529 {
46− idledata *d;
47− d = mallocz(sizeof(idledata), 1);
48− assert(d != nil);
49− a->aux = d;
50− d->idle = nrand(IDLEIDLE);
51−}
30+ u32int type;
31+ union {
32+ int i;
33+ Point pt;
34+ Monster *m;
35+ Point *path;
36+ WalkToData *wd;
37+ };
38+};
5239
5340 static void
54−idleexec(AIState *a)
41+aivfree(void *v)
5542 {
56− idledata *d;
57− Monster *m, *mt;
58− int i, r, xd, yd, xs, ys, npath;
59− Point c, p, s, *path;
60− d = a->aux;
61− m = a->m;
62−
63− if(m->ai == nil)
64− d->idle++;
65−
66− if(d->idle > IDLEIDLE){
67− mpushstate(m, wander(m));
68− d->idle = 0;
69− return;
70− }
43+ AiData *d;
7144
72− if(m->ai == nil)
73− d->attacking = 0;
74− if(d->attacking != 0)
45+ d = v;
46+
47+ if(d == nil)
7548 return;
7649
77− /* find something to attack */
78− r = 8;
79− c = m->pt;
80− for(p.x = c.x; p.x >= c.x - r; p.x--){
81− for(p.y = c.y; p.y >= c.y - r; p.y--){
82− if(eqpt(p, c))
83− continue;
84− xd = p.x - c.x;
85− yd = p.y - c.y;
86− if(xd*xd+yd*yd <= r*r){
87− xs = c.x - xd;
88− ys = c.y - yd;
89−
90− Point pts[] = { p, Pt(p.x, ys), Pt(xs, p.y), Pt(xs, ys) };
91− shuffle(pts, 4, sizeof(Point));
92− for(i = 0; i < 4; i++){
93− s = pts[i];
94− if(!ptinrect(s, m->l->r))
95− continue;
96− if(hasflagat(m->l, s, Fhasmonster)){
97− mt = tileat(m->l, s)->monst;
98− /* BUG: should not be nil. */
99− if(mt == nil)
100− continue;
101− if(abs(m->align - mt->align) > 15){
102− if(manhattan(m->pt, s) != ORTHOCOST){
103− npath = pathfind(m->l, m->pt, s, &path, Fblocked);
104− if(npath < 0)
105− continue;
106− if(npath > r*2){
107− free(path);
108− continue;
109− }
110− free(path);
111− }
112− d->attacking = 1;
113− mpushstate(m, attack(m, mt));
114− return;
115− }
116− }
117− }
118− }
119− }
50+ switch(d->type){
51+ case AIMONST:
52+ mfree(d->m);
53+ break;
54+ case AIPATH:
55+ free(d->path);
56+ break;
57+ case AIWALKTO:
58+ free(d->wd->path);
59+ free(d->wd);
60+ break;
12061 }
62+
63+ free(d);
12164 }
12265
123−static void
124−idleexit(AIState *a)
66+static AiData*
67+aidata(u32int typ)
12568 {
126− free(a->aux);
69+ AiData *d = emalloc(sizeof(AiData));
70+ d->type = typ;
71+ return d;
12772 }
12873
129−void
130−idle(Monster *m)
74+AiData*
75+aipt(Point pt)
76+{
77+ AiData *d = aidata(AIPOINT);
78+ d->pt = pt;
79+ return d;
80+}
81+
82+AiData*
83+aiint(int i)
84+{
85+ AiData *d = aidata(AIINT);
86+ d->i = i;
87+ return d;
88+}
89+
90+AiData*
91+aimonst(Monster *m)
92+{
93+ AiData *d = aidata(AIMONST);
94+ d->m = m;
95+ return d;
96+}
97+
98+AiData*
99+aipath(Point *path)
100+{
101+ AiData *d = aidata(AIPATH);
102+ d->path = path;
103+ return d;
104+}
105+
106+AiData*
107+aiwd(WalkToData *wd)
131108 {
132− AIState *i;
133− i = mkstate("idle", m, nil, idleenter, idleexec, idleexit);
134− i->enter(i);
135− m->aglobal = i;
109+ AiData *d = aidata(AIWALKTO);
110+ d->wd = wd;
111+ return d;
136112 }
137113
138114 static void
139−wanderexec(AIState *a)
115+OOM(void)
116+{
117+ sysfatal("ai: %r");
118+}
119+
120+static int
121+wander(void *v)
140122 {
141123 int try;
142124 Point p;
143125 Monster *m;
144− AIState *ai;
145− m = a->m;
126+ AiData *pt, *in;
146127
147− if(a->aux != nil){
148−leave:
149− ai = mpopstate(m);
150− freestate(ai);
151− return;
152− }
128+ m = v;
153129
154− try = 0;
155− do {
130+ for(try = 0; try < 5; try++){
156131 p = addpt(m->pt, Pt(nrand(IDLERADIUS*2+1)-IDLERADIUS, nrand(IDLERADIUS*2+1)-IDLERADIUS));
157− if(try++ > 10)
158− goto leave;
159− } while(!ptinrect(p, m->l->r) || hasflagat(m->l, p, Fblocked) || manhattan(m->pt, p) < ORTHOCOST*2);
160132
161− mpushstate(m, walkto(m, p, 1));
162− a->aux=a;
163−}
133+ if(ptinrect(p, m->l->r) &&
134+ !hasflagat(m->l, p, Fblocked|Fhasmonster) &&
135+ manhattan(m->pt, p) >= ORTHOCOST*2)
136+ break;
137+ }
164138
165−AIState*
166−wander(Monster *m)
167−{
168− AIState *i;
169− i = mkstate("wander", m, nil, nil, wanderexec, nil);
170− return i;
139+ if(try == 5)
140+ return TASKFAIL;
141+
142+ pt = aipt(p);
143+ in = aiint(0);
144+
145+ if(mapset(m->bb, "wander_dst", pt) < 0)
146+ OOM();
147+
148+ if(mapset(m->bb, "wander_offset", in) < 0)
149+ OOM();
150+
151+ return TASKSUCCESS;
171152 }
172153
173−typedef struct walktodata walktodata;
174−struct walktodata
154+static WalkToData*
155+planpathcalc(Monster *m, Point dst, int offset)
175156 {
176− Point dst;
177− Point last;
178157 int npath;
179− int next;
180158 Point *path;
181− int wait;
182− int blocked;
183−};
159+ WalkToData *wd;
160+
161+ npath = pathfind(m->l, m->pt, dst, &path, Fhasmonster|Fblocked);
162+ if(npath < 0)
163+ return nil;
164+
165+ wd = emalloc(sizeof(WalkToData));
166+ wd->next = 1;
167+ wd->npath = npath - offset;
168+ wd->path = path;
169+
170+ return wd;
171+}
184172
185173 static int
186−walktoinner(AIState *a)
174+planpathkey(Monster *m, char *dst, char *offset, char *data)
187175 {
188− Point p;
189− walktodata *d;
190− Monster *m;
191− d = a->aux;
192− m = a->m;
176+ WalkToData *wd;
177+ AiData *dd, *doff;
193178
194− /* d->last might not equal m->pt if some other
195− * state pre-empted us, like attack */
196− if(!eqpt(d->last, m->pt) || d->blocked > d->wait){
197− goto cleanup;
198− }
179+ dd = mapget(m->bb, dst);
180+ doff = mapget(m->bb, offset);
199181
200− //dbg("%s path -> %P %d/%d", m->md->name, d->dst, d->next, d->npath);
201− if(d->path == nil){
202− /* plan route */
203− d->npath = pathfind(m->l, m->pt, d->dst, &d->path, Fhasmonster|Fblocked);
204− d->next = 1;
205− if(d->npath < 0){
206− if(d->wait > 0){
207− //dbg("%s route to %P blocked; routing through monsters", m->md->name, d->dst);
208− /* path not through monsters available? */
209− d->npath = pathfind(m->l, m->pt, d->dst, &d->path, Fblocked);
210− d->next = 1;
211− if(d->npath < 0){
212− d->blocked++;
213− return 0;
214− }
215− }
216− }
217− }
182+ wd = planpathcalc(m, dd->pt, doff->i);
183+ if(wd == nil)
184+ return TASKFAIL;
218185
219− if(d->next == d->npath){
220− goto cleanup;
221− }
186+ if(mapset(m->bb, data, aiwd(wd)) < 0)
187+ OOM();
222188
223− p = d->path[d->next];
224− //dbg("next %P %06b", p, flagat(m->l, p));
189+ return TASKSUCCESS;
190+}
225191
226− if(hasflagat(m->l, p, Fhasmonster|Fblocked)){
227− d->blocked++;
228− dbg("%s blocked %d/%d", m->md->name, d->blocked, d->wait);
229− return 0;
230− }
192+static int
193+planpathwander(void *v)
194+{
195+ Monster *m;
231196
232− d->blocked = 0;
233− d->next++;
197+ m = v;
234198
235− maction(m, MMOVE, p);
236− d->last = p;
237− return 0;
238−cleanup:
239− free(d->path);
240− d->path = nil;
241− d->npath = 0;
242− d->blocked = 0;
243− return -1;
199+ return planpathkey(m, "wander_dst", "wander_offset", "wander_data");
244200 }
245201
246−static void
247−walktoexec(AIState *a)
202+static int
203+planpathattack(void *v)
248204 {
249205 Monster *m;
250− m = a->m;
251206
252− if(walktoinner(a) < 0){
253− a = mpopstate(m);
254− freestate(a);
255− return;
256− }
207+ m = v;
208+
209+ return planpathkey(m, "attack_dst", "attack_offset", "attack_data");
257210 }
258211
259−static void
260−walktoexit(AIState *a)
212+static int
213+walktoinner(Monster *m, char *data)
261214 {
262− walktodata *d;
263− d = a->aux;
264− if(d->path != nil)
265− free(d->path);
266− free(d);
215+ Point np;
216+ WalkToData *wd;
217+ AiData *dwd;
218+
219+ dwd = mapget(m->bb, data);
220+ assert(dwd != nil);
221+
222+ wd = dwd->wd;
223+
224+ if(wd->next == wd->npath)
225+ return TASKSUCCESS;
226+
227+ np = wd->path[wd->next++];
228+
229+ /* could just call maction(m,MMOVE,np) but
230+ * this prevents friendly fire
231+ */
232+ if(hasflagat(m->l, np, Fhasmonster|Fblocked))
233+ return TASKFAIL;
234+
235+ int rv = maction(m, MMOVE, np);
236+ if(rv != 1)
237+ return TASKFAIL;
238+
239+ return TASKRUNNING;
267240 }
268241
269−AIState*
270−walkto(Monster *m, Point p, int wait)
242+static int
243+walktowander(void *v)
271244 {
272− walktodata *d;
273− AIState *i;
274− d = mallocz(sizeof(*d), 1);
275− d->dst = p;
276− d->last = m->pt;
277− d->wait = wait;
278− assert(d != nil);
279− i = mkstate("walk", m, d, nil, walktoexec, walktoexit);
280− return i;
245+ Monster *m;
246+
247+ m = v;
248+
249+ return walktoinner(m, "wander_data");
281250 }
282251
283−typedef struct attackdata attackdata;
284−struct attackdata
252+static int
253+walktoattack(void *v)
285254 {
286− Monster *mt;
287− Point last;
288− AIState *w;
289− walktodata *wd;
255+ Monster *m;
256+ m = v;
257+ return walktoinner(m, "attack_data");
258+}
259+
260+enum {
261+ ATTACKRADIUS = 6,
290262 };
291263
292−static void
293−attackenter(AIState *a)
264+static int
265+checkenemy(Monster *m, Monster *target)
294266 {
295− attackdata *d;
296− d = a->aux;
297− if(d->w->enter != nil)
298− d->w->enter(d->w);
267+ if(m == target)
268+ return -1;
269+ if(target->flags & Mdead)
270+ return -1;
271+ if(abs(m->align - target->align) <= 15)
272+ return -1;
273+ return 0;
299274 }
300275
301−static void
302−attackexec(AIState *a)
276+static int
277+findenemy(void *v)
303278 {
304− int n;
305− attackdata *d;
306− Monster *m, *mt;
307− m = a->m;
308− d = a->aux;
309− mt = d->mt;
310−
311− n = manhattan(m->pt, mt->pt);
312− if(mt->flags & Mdead || n > ORTHOCOST*8 ){
313− /* stop the chase */
314− a = mpopstate(m);
315− freestate(a);
316− return;
317− } else if(n > ORTHOCOST){
318− /* walk to target */
319− if(!eqpt(mt->pt, d->last)){
320− d->wd->dst = mt->pt;
321− }
322− d->wd->wait = 2;
323− d->wd->last = m->pt;
324− if(walktoinner(d->w) < 0){
325− a = mpopstate(m);
326− freestate(a);
327− return;
279+ int i, r, xd, yd, xs, ys;
280+ Point c, p, s;
281+ Tile *t;
282+ Monster *m, *target;
283+ AiData *dtgt;
284+
285+ m = v;
286+ c = m->pt;
287+
288+ r = ATTACKRADIUS;
289+
290+ target = nil;
291+
292+ /* check if we already have a target */
293+ dtgt = mapget(m->bb, "attack_target");
294+ if(dtgt != nil)
295+ target = dtgt->m;
296+
297+ if(target != nil && checkenemy(m, target) == 0)
298+ return TASKSUCCESS;
299+
300+ for(p.x = c.x; p.x >= c.x - r; p.x--){
301+ for(p.y = c.y; p.y >= c.y - r; p.y--){
302+ xd = p.x - c.x;
303+ yd = p.y - c.y;
304+
305+ /* skip points outside circle */
306+ if(xd*xd+yd*yd > r*r)
307+ continue;
308+
309+ xs = c.x - xd;
310+ ys = c.y - yd;
311+
312+ Point pts[] = { p, Pt(p.x, ys), Pt(xs, p.y), Pt(xs, ys) };
313+ shuffle(pts, 4, sizeof(Point));
314+ for(i = 0; i < 4; i++){
315+ s = pts[i];
316+
317+ if(!ptinrect(s, m->l->r))
318+ continue;
319+ if(!hasflagat(m->l, s, Fhasmonster))
320+ continue;
321+
322+ t = tileat(m->l, s);
323+ target = t->monst;
324+
325+ if(checkenemy(m, target) != 0)
326+ continue;
327+
328+ //fprint(2, "FINDENEMY: %s %P\n", target->md->name, target->pt);
329+
330+ /* found a suitable target */
331+ incref(&target->ref);
332+ if(mapset(m->bb, "attack_target", aimonst(target)) < 0)
333+ OOM();
334+
335+ return TASKSUCCESS;
336+ }
328337 }
329− d->last = mt->pt;
330− } else {
331− /* attack! */
332− maction(m, MMOVE, mt->pt);
333338 }
339+
340+ return TASKFAIL;
334341 }
335342
336−static void
337−attackexit(AIState *a)
343+static int
344+targetenemy(void *v)
338345 {
339− attackdata *d;
340− d = a->aux;
346+ Monster *m;
347+ AiData *dtgt, *dst, *off;
341348
342− if(d->w->exit != nil)
343− d->w->exit(d->w);
344− freestate(d->w);
345− mfree(d->mt);
346− free(d);
349+ m = v;
350+
351+ dtgt = mapget(m->bb, "attack_target");
352+ if(dtgt == nil)
353+ return TASKFAIL;
354+
355+ dst = aipt(dtgt->m->pt);
356+ off = aiint(1);
357+
358+ if(mapset(m->bb, "attack_dst", dst) < 0)
359+ OOM();
360+
361+ if(mapset(m->bb, "attack_offset", off) < 0)
362+ OOM();
363+
364+ return TASKSUCCESS;
365+}
366+
367+static int
368+attackenemy(void *v)
369+{
370+ Monster *m, *target;
371+ AiData *dtgt;
372+ m = v;
373+
374+ dtgt = mapget(m->bb, "attack_target");
375+ assert(dtgt != nil);
376+
377+ target = dtgt->m;
378+
379+ /* monster has moved */
380+ if(manhattan(m->pt, target->pt) > ORTHOCOST)
381+ return TASKFAIL;
382+
383+ /* hit the enemy */
384+ if(maction(m, MMOVE, target->pt) != 0)
385+ return TASKFAIL;
386+
387+ if(target->flags & Mdead)
388+ return TASKSUCCESS;
389+
390+ return TASKRUNNING;
391+}
392+
393+static BehaviorNode*
394+bnidle(void)
395+{
396+ BehaviorNode *bnwfind, *bnplanpath, *bnwalkto;
397+
398+ bnwfind = btleaf("wander find", wander);
399+ bnplanpath = btleaf("wander plan path", planpathwander);
400+ bnwalkto = btleaf("wander move", walktowander);
401+
402+ btsetguard(bnwalkto, bnplanpath);
403+ btsetguard(bnplanpath, bnwfind);
404+
405+ return bnwalkto;
347406 }
348407
349−AIState*
350−attack(Monster *m, Monster *targ)
408+static BehaviorNode*
409+bnattack(void)
351410 {
352− attackdata *d;
353− AIState *i;
354− d = mallocz(sizeof(*d), 1);
355− incref(&targ->ref);
356− d->mt = targ;
357− d->wd = mallocz(sizeof(walktodata), 1);
358− d->w = mkstate("chase", m, d->wd, nil, nil, walktoexit);
359− i = mkstate("attack", m, d, attackenter, attackexec, attackexit);
360− return i;
411+ BehaviorNode *bnfind, *bntarget;
412+ BehaviorNode *bnplanpath, *bnmove, *bnhit;
413+ BehaviorNode *bnatk;
414+
415+ bnfind = btleaf("find enemy", findenemy);
416+
417+ bntarget = btleaf("target enemy", targetenemy);
418+ bnplanpath = btleaf("attack plan path", planpathattack);
419+ bnmove = btleaf("move to enemy", walktoattack);
420+ bnhit = btleaf("hit enemy", attackenemy);
421+
422+ bnatk = btsequence("attack", bnplanpath, bnmove, bnhit, nil);
423+ btsetguard(bnatk, bntarget);
424+ btsetguard(bntarget, bnfind);
425+
426+ return bnatk;
361427 }
362428
429+void
430+idle(Monster *m)
431+{
432+ Map *map;
433+ BehaviorNode *root;
434+
435+ map = mapnew(aivfree);
436+ if(map == nil)
437+ OOM();
438+
439+ root = btdynguard("root", bnattack(), bnidle(), nil);
440+
441+ if(root == nil)
442+ OOM();
443+
444+ m->bt = root;
445+ m->bb = map;
446+}
dat.h+10−23
@@ -1,4 +1,3 @@
1−typedef struct AIState AIState;
21 typedef struct EquipData EquipData;
32 typedef struct ItemData ItemData;
43 typedef struct Item Item;
@@ -39,26 +38,15 @@ extern long turn; · 34 unmodified lines
3938 int isyou(Monster *m);
4039 int nearyou(Point p);
4140
42−typedef void (*AIFun)(AIState *);
43−
44−struct AIState
45−{
46− AIState *prev;
47− char name[16];
48− Monster *m;
49− void *aux;
50− AIFun enter;
51− AIFun exec;
52− AIFun exit;
41+typedef struct WalkToData WalkToData;
42+struct WalkToData {
43+ int next;
44+ int npath;
45+ Point *path;
5346 };
5447
5548 /* ai.c */
56−AIState *mkstate(char *name, Monster *m, void *aux, AIFun enter, AIFun exec, AIFun exit);
57−void freestate(AIState *a);
5849 void idle(Monster *m);
59−AIState* wander(Monster *m);
60−AIState* walkto(Monster *m, Point p, int wait);
61−AIState* attack(Monster *m, Monster *targ);
6250
6351 enum
6452 {
@@ -258,8 +246,8 @@ struct Monster · 193 unmodified lines
258246 u32int flags;
259247
260248 /* ai state */
261− AIState *ai;
262− AIState *aglobal;
249+ BehaviorNode *bt;
250+ Map *bb;
263251
264252 /* inventory */
265253 ItemList inv;
@@ -277,8 +265,6 @@ MonsterData *mdbyname(char *monster); · 11 unmodified lines
277265 Monster *mbyname(char *monster);
278266 void mfree(Monster *m);
279267 int mupdate(Monster *m);
280−void mpushstate(Monster *m, AIState *a);
281−AIState *mpopstate(Monster *m);
282268 int maction(Monster *m, int what, Point where);
283269 int mwield(Monster *m, int n);
284270 int munwield(Monster *m, int type);
@@ -385,7 +371,7 @@ struct Level · 100 unmodified lines
385371 Point up;
386372 Point down;
387373
388− AIState *ai;
374+ BehaviorNode *bt;
389375
390376 int nspawns;
391377 Spawn spawns[10];
@@ -429,7 +415,8 @@ void warn(char *fmt, ...); · 37 unmodified lines
429415 void bad(char *fmt, ...);
430416 void dbg(char *fmt, ...);
431417 void uiinit(char *name);
432−void uiexec(AIState *ai);
418+void uirun(void);
419+int uibt(void *a);
433420 void uiredraw(int justui);
434421
435422 /* util.c */
hack9.c+5−4
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <thread.h>
44 #include <draw.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710 #include "alg.h"
811
@@ -67,8 +70,7 @@ movemons(void) · 58 unmodified lines
6770 l = player->l;
6871
6972 /* run ai on level, for spawns etc */
70− if(l->ai != nil && l->ai->exec != nil)
71− l->ai->exec(l->ai);
73+ bttick(l->bt, l);
7274
7375 for(p.x = 0; p.x < l->width; p.x++){
7476 for(p.y = 0; p.y < l->height; p.y++){
@@ -166,7 +168,7 @@ threadmain(int argc, char *argv[]) · 91 unmodified lines
166168 t->monst = player;
167169 setflagat(level, player->pt, Fhasmonster);
168170
169− player->ai = mkstate("input", player, nil, nil, uiexec, nil);
171+ player->bt = btleaf("input", uibt);
170172
171173 if(debug > 0){
172174 player->hp = 500;
@@ -180,4 +182,3 @@ threadmain(int argc, char *argv[]) · 7 unmodified lines
180182 movemons();
181183 }
182184 }
183−
item.c+3−0
@@ -5,6 +5,9 @@ · 4 unmodified lines
55 #include <bio.h>
66 #include <ndb.h>
77
8+#include "map.h"
9+#include "bt.h"
10+
811 #include "dat.h"
912
1013 enum
level.c+5−2
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710 #include "alg.h"
811
@@ -103,6 +106,8 @@ freelevel(Level *l) · 94 unmodified lines
103106 Point p;
104107 Tile *t;
105108
109+ btfree(l->bt, l);
110+
106111 for(x = 0; x < l->width; x++){
107112 for(y = 0; y < l->height; y++){
108113 p = (Point){x, y};
@@ -119,8 +124,6 @@ freelevel(Level *l) · 10 unmodified lines
119124
120125 free(l->tiles);
121126 free(l->flags);
122− if(l->ai != nil)
123− freestate(l->ai);
124127 free(l);
125128 }
126129
levelgen.c+10−5
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710 #include "alg.h"
811
@@ -246,8 +249,8 @@ genmonsters(Level *l, char *type, int count) · 237 unmodified lines
246249 }
247250 }
248251
249−static void
250−levelexec(AIState *a)
252+static int
253+levelexec(void *v)
251254 {
252255 int i, j, n;
253256 Point p, np, *neigh;
@@ -258,7 +261,7 @@ levelexec(AIState *a) · 4 unmodified lines
258261 Monster *m;
259262 Item *it;
260263
261− l = a->aux;
264+ l = v;
262265
263266 for(i = 0; i < l->nspawns; i++){
264267 sp = &l->spawns[i];
@@ -305,6 +308,8 @@ levelexec(AIState *a) · 40 unmodified lines
305308 }
306309 }
307310 }
311+
312+ return TASKSUCCESS;
308313 }
309314
310315 Level*
@@ -331,8 +336,8 @@ mklevel(int width, int height, int floor) · 20 unmodified lines
331336 if(l->flags == nil)
332337 goto err1;
333338
334− l->ai = mkstate("level", nil, l, nil, levelexec, nil);
335− if(l->ai == nil)
339+ l->bt = btleaf("level", levelexec);
340+ if(l->bt == nil)
336341 goto err2;
337342
338343 for(p.x = 0; p.x < width; p.x++){
monst.c+7−45
@@ -5,6 +5,9 @@ · 4 unmodified lines
55 #include <bio.h>
66 #include <ndb.h>
77
8+#include "map.h"
9+#include "bt.h"
10+
811 #include "dat.h"
912
1013 enum
@@ -207,20 +210,12 @@ void · 196 unmodified lines
207210 mfree(Monster *m)
208211 {
209212 int n;
210− AIState *a;
211213
212214 if(decref(&m->ref) != 0)
213215 return;
214216
215− if(m->aglobal != nil){
216− if(m->aglobal->exit != nil)
217− m->aglobal->exit(m->aglobal);
218− freestate(m->aglobal);
219− }
220− if(m->ai != nil){
221− while((a = mpopstate(m)) != nil)
222− freestate(a);
223− }
217+ btfree(m->bt, m);
218+ mapfree(m->bb);
224219
225220 for(n = 0; n < NEQUIP; n++){
226221 if(m->armor[n] != nil){
@@ -230,6 +225,7 @@ mfree(Monster *m) · 3 unmodified lines
230225
231226 ilfree(&m->inv);
232227
228+ memset(m, 0, sizeof(Monster));
233229 free(m);
234230 }
235231
@@ -239,15 +235,7 @@ mupdate(Monster *m) · 3 unmodified lines
239235 m->mvp += m->mvr;
240236
241237 while(m->mvp > 12){
242− if(m->aglobal != nil)
243− m->aglobal->exec(m->aglobal);
244− if(m->ai != nil){
245− //dbg("the %s is %sing...", m->md->name, m->ai->name);
246− if(m->ai->exec != nil)
247− m->ai->exec(m->ai);
248− } else {
249− //dbg("the %s is %sing...", m->md->name, m->aglobal->name);
250− }
238+ bttick(m->bt, m);
251239
252240 m->mvp -= 12;
253241 m->turns++;
@@ -263,32 +251,6 @@ mupdate(Monster *m) · 9 unmodified lines
263251 return 0;
264252 }
265253
266−void
267−mpushstate(Monster *m, AIState *a)
268−{
269− assert(a != nil);
270− a->prev = m->ai;
271− m->ai = a;
272− if(a->enter != nil)
273− a->enter(a);
274−}
275−
276−AIState*
277−mpopstate(Monster *m)
278−{
279− AIState *a;
280−
281− if(m->ai == nil)
282− return nil;
283−
284− a = m->ai;
285− if(a->exit != nil)
286− a->exit(a);
287− m->ai = a->prev;
288−
289− return a;
290−}
291−
292254 /* determines what happens when a monster gets some xp */
293255 static void
294256 maddxp(Monster *m, int xp)
path.c+3−0
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710 #include "alg.h"
811
tile.c+3−0
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710
811 Tileset*
ui.c+14−3
@@ -5,6 +5,9 @@ · 4 unmodified lines
55 #include <keyboard.h>
66 #include <mouse.h>
77
8+#include "map.h"
9+#include "bt.h"
10+
811 #include "dat.h"
912 #include "alg.h"
1013
@@ -311,11 +314,13 @@ drawlevel(Level *l, Tileset *ts, Rectangle r) · 300 unmodified lines
311314 stringbg(screen, p2, display->white, ZP, smallfont, buf, display->black, ZP);
312315 /* debugging the ai state */
313316 if(debug){
317+/*
314318 if(m->ai != nil){
315319 stringbg(screen, sp, display->white, ZP, smallfont, m->ai->name, display->black, ZP);
316320 } else {
317321 stringbg(screen, sp, display->white, ZP, smallfont, m->aglobal->name, display->black, ZP);
318322 }
323+*/
319324 }
320325 } else if(hasflagat(l, p, Fhasitem)){
321326 drawtile(ts, screen, sp, ilnth(&t->items, 0)->id->tile);
@@ -854,14 +859,12 @@ chelp(Rune c) · 532 unmodified lines
854859 }
855860
856861 void
857−uiexec(AIState *ai)
862+uirun(void)
858863 {
859864 int al;
860865 Rune c;
861866 Msg *l, *lp;
862867
863− USED(ai);
864−
865868 enum { ALOG, ATICK, AMOUSE, ARESIZE, AKEYBOARD, AEND };
866869 Alt a[AEND+1] = {
867870 { ui.msgc, &l, CHANRCV },
@@ -952,6 +955,14 @@ uiexec(AIState *ai) · 84 unmodified lines
952955 }
953956 }
954957
958+int
959+uibt(void *a)
960+{
961+ USED(a);
962+ uirun();
963+ return TASKSUCCESS;
964+}
965+
955966 static int
956967 ch2ind(KeyMenu *km, Rune want)
957968 {
util.c+3−0
@@ -3,6 +3,9 @@ · 2 unmodified lines
33 #include <draw.h>
44 #include <thread.h>
55
6+#include "map.h"
7+#include "bt.h"
8+
69 #include "dat.h"
710
811 Point cardinals[] = {