| @@ -3,360 +3,444 @@ · 2 unmodified lines |
| 3 | 3 | #include <draw.h> |
| 4 | 4 | #include <thread.h> |
| 5 | 5 | |
| 6 | +#include "bt.h" |
| 7 | +#include "map.h" |
| 8 | + |
| 6 | 9 | #include "dat.h" |
| 7 | 10 | #include "alg.h" |
| 8 | 11 | |
| 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 | | − |
| 30 | 12 | enum |
| 31 | 13 | { |
| 32 | | − IDLERADIUS = 4, |
| 14 | + IDLERADIUS = 6, |
| 33 | 15 | IDLEIDLE = 4, |
| 34 | 16 | }; |
| 35 | 17 | |
| 36 | | −typedef struct idledata idledata; |
| 37 | | −struct idledata |
| 18 | +enum |
| 38 | 19 | { |
| 39 | | − int idle; |
| 40 | | − int attacking; |
| 20 | + AIPOINT = 0, |
| 21 | + AIINT, |
| 22 | + AIMONST, |
| 23 | + AIPATH, |
| 24 | + AIWALKTO, |
| 41 | 25 | }; |
| 42 | 26 | |
| 43 | | −static void |
| 44 | | −idleenter(AIState *a) |
| 27 | +typedef struct AiData AiData; |
| 28 | +struct AiData |
| 45 | 29 | { |
| 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 | +}; |
| 52 | 39 | |
| 53 | 40 | static void |
| 54 | | −idleexec(AIState *a) |
| 41 | +aivfree(void *v) |
| 55 | 42 | { |
| 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; |
| 71 | 44 | |
| 72 | | − if(m->ai == nil) |
| 73 | | − d->attacking = 0; |
| 74 | | − if(d->attacking != 0) |
| 45 | + d = v; |
| 46 | + |
| 47 | + if(d == nil) |
| 75 | 48 | return; |
| 76 | 49 | |
| 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; |
| 120 | 61 | } |
| 62 | + |
| 63 | + free(d); |
| 121 | 64 | } |
| 122 | 65 | |
| 123 | | −static void |
| 124 | | −idleexit(AIState *a) |
| 66 | +static AiData* |
| 67 | +aidata(u32int typ) |
| 125 | 68 | { |
| 126 | | − free(a->aux); |
| 69 | + AiData *d = emalloc(sizeof(AiData)); |
| 70 | + d->type = typ; |
| 71 | + return d; |
| 127 | 72 | } |
| 128 | 73 | |
| 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) |
| 131 | 108 | { |
| 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; |
| 136 | 112 | } |
| 137 | 113 | |
| 138 | 114 | static void |
| 139 | | −wanderexec(AIState *a) |
| 115 | +OOM(void) |
| 116 | +{ |
| 117 | + sysfatal("ai: %r"); |
| 118 | +} |
| 119 | + |
| 120 | +static int |
| 121 | +wander(void *v) |
| 140 | 122 | { |
| 141 | 123 | int try; |
| 142 | 124 | Point p; |
| 143 | 125 | Monster *m; |
| 144 | | − AIState *ai; |
| 145 | | − m = a->m; |
| 126 | + AiData *pt, *in; |
| 146 | 127 | |
| 147 | | − if(a->aux != nil){ |
| 148 | | −leave: |
| 149 | | − ai = mpopstate(m); |
| 150 | | − freestate(ai); |
| 151 | | − return; |
| 152 | | − } |
| 128 | + m = v; |
| 153 | 129 | |
| 154 | | − try = 0; |
| 155 | | − do { |
| 130 | + for(try = 0; try < 5; try++){ |
| 156 | 131 | 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); |
| 160 | 132 | |
| 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 | + } |
| 164 | 138 | |
| 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; |
| 171 | 152 | } |
| 172 | 153 | |
| 173 | | −typedef struct walktodata walktodata; |
| 174 | | −struct walktodata |
| 154 | +static WalkToData* |
| 155 | +planpathcalc(Monster *m, Point dst, int offset) |
| 175 | 156 | { |
| 176 | | − Point dst; |
| 177 | | − Point last; |
| 178 | 157 | int npath; |
| 179 | | − int next; |
| 180 | 158 | 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 | +} |
| 184 | 172 | |
| 185 | 173 | static int |
| 186 | | −walktoinner(AIState *a) |
| 174 | +planpathkey(Monster *m, char *dst, char *offset, char *data) |
| 187 | 175 | { |
| 188 | | − Point p; |
| 189 | | − walktodata *d; |
| 190 | | − Monster *m; |
| 191 | | − d = a->aux; |
| 192 | | − m = a->m; |
| 176 | + WalkToData *wd; |
| 177 | + AiData *dd, *doff; |
| 193 | 178 | |
| 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); |
| 199 | 181 | |
| 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; |
| 218 | 185 | |
| 219 | | − if(d->next == d->npath){ |
| 220 | | − goto cleanup; |
| 221 | | − } |
| 186 | + if(mapset(m->bb, data, aiwd(wd)) < 0) |
| 187 | + OOM(); |
| 222 | 188 | |
| 223 | | − p = d->path[d->next]; |
| 224 | | − //dbg("next %P %06b", p, flagat(m->l, p)); |
| 189 | + return TASKSUCCESS; |
| 190 | +} |
| 225 | 191 | |
| 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; |
| 231 | 196 | |
| 232 | | − d->blocked = 0; |
| 233 | | − d->next++; |
| 197 | + m = v; |
| 234 | 198 | |
| 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"); |
| 244 | 200 | } |
| 245 | 201 | |
| 246 | | −static void |
| 247 | | −walktoexec(AIState *a) |
| 202 | +static int |
| 203 | +planpathattack(void *v) |
| 248 | 204 | { |
| 249 | 205 | Monster *m; |
| 250 | | − m = a->m; |
| 251 | 206 | |
| 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"); |
| 257 | 210 | } |
| 258 | 211 | |
| 259 | | −static void |
| 260 | | −walktoexit(AIState *a) |
| 212 | +static int |
| 213 | +walktoinner(Monster *m, char *data) |
| 261 | 214 | { |
| 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; |
| 267 | 240 | } |
| 268 | 241 | |
| 269 | | −AIState* |
| 270 | | −walkto(Monster *m, Point p, int wait) |
| 242 | +static int |
| 243 | +walktowander(void *v) |
| 271 | 244 | { |
| 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"); |
| 281 | 250 | } |
| 282 | 251 | |
| 283 | | −typedef struct attackdata attackdata; |
| 284 | | −struct attackdata |
| 252 | +static int |
| 253 | +walktoattack(void *v) |
| 285 | 254 | { |
| 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, |
| 290 | 262 | }; |
| 291 | 263 | |
| 292 | | −static void |
| 293 | | −attackenter(AIState *a) |
| 264 | +static int |
| 265 | +checkenemy(Monster *m, Monster *target) |
| 294 | 266 | { |
| 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; |
| 299 | 274 | } |
| 300 | 275 | |
| 301 | | −static void |
| 302 | | −attackexec(AIState *a) |
| 276 | +static int |
| 277 | +findenemy(void *v) |
| 303 | 278 | { |
| 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 | + } |
| 328 | 337 | } |
| 329 | | − d->last = mt->pt; |
| 330 | | − } else { |
| 331 | | − /* attack! */ |
| 332 | | − maction(m, MMOVE, mt->pt); |
| 333 | 338 | } |
| 339 | + |
| 340 | + return TASKFAIL; |
| 334 | 341 | } |
| 335 | 342 | |
| 336 | | −static void |
| 337 | | −attackexit(AIState *a) |
| 343 | +static int |
| 344 | +targetenemy(void *v) |
| 338 | 345 | { |
| 339 | | − attackdata *d; |
| 340 | | − d = a->aux; |
| 346 | + Monster *m; |
| 347 | + AiData *dtgt, *dst, *off; |
| 341 | 348 | |
| 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; |
| 347 | 406 | } |
| 348 | 407 | |
| 349 | | −AIState* |
| 350 | | −attack(Monster *m, Monster *targ) |
| 408 | +static BehaviorNode* |
| 409 | +bnattack(void) |
| 351 | 410 | { |
| 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; |
| 361 | 427 | } |
| 362 | 428 | |
| 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 | +} |