| @@ -3,360 +3,444 @@ · 2 unmodified lines |
| 3 | #include <draw.h> | 3 | #include <draw.h> |
| 4 | #include <thread.h> | 4 | #include <thread.h> |
| 5 | | 5 | |
| | 6 | #include "bt.h" |
| | 7 | #include "map.h" |
| | 8 | |
| 6 | #include "dat.h" | 9 | #include "dat.h" |
| 7 | #include "alg.h" | 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 | enum | 12 | enum |
| 31 | { | 13 | { |
| 32 | IDLERADIUS = 4, | 14 | IDLERADIUS = 6, |
| 33 | IDLEIDLE = 4, | 15 | IDLEIDLE = 4, |
| 34 | }; | 16 | }; |
| 35 | | 17 | |
| 36 | typedef struct idledata idledata; | 18 | enum |
| 37 | struct idledata | | |
| 38 | { | 19 | { |
| 39 | int idle; | 20 | AIPOINT = 0, |
| 40 | int attacking; | 21 | AIINT, |
| | 22 | AIMONST, |
| | 23 | AIPATH, |
| | 24 | AIWALKTO, |
| 41 | }; | 25 | }; |
| 42 | | 26 | |
| 43 | static void | 27 | typedef struct AiData AiData; |
| 44 | idleenter(AIState *a) | 28 | struct AiData |
| 45 | { | 29 | { |
| 46 | idledata *d; | 30 | u32int type; |
| 47 | d = mallocz(sizeof(idledata), 1); | 31 | union { |
| 48 | assert(d != nil); | 32 | int i; |
| 49 | a->aux = d; | 33 | Point pt; |
| 50 | d->idle = nrand(IDLEIDLE); | 34 | Monster *m; |
| 51 | } | 35 | Point *path; |
| | 36 | WalkToData *wd; |
| | 37 | }; |
| | 38 | }; |
| 52 | | 39 | |
| 53 | static void | 40 | static void |
| 54 | idleexec(AIState *a) | 41 | aivfree(void *v) |
| 55 | { | 42 | { |
| 56 | idledata *d; | 43 | AiData *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 | } | | |
| 71 | | 44 | |
| 72 | if(m->ai == nil) | 45 | d = v; |
| 73 | d->attacking = 0; | 46 | |
| 74 | if(d->attacking != 0) | 47 | if(d == nil) |
| 75 | return; | 48 | return; |
| 76 | | 49 | |
| 77 | /* find something to attack */ | 50 | switch(d->type){ |
| 78 | r = 8; | 51 | case AIMONST: |
| 79 | c = m->pt; | 52 | mfree(d->m); |
| 80 | for(p.x = c.x; p.x >= c.x - r; p.x--){ | 53 | break; |
| 81 | for(p.y = c.y; p.y >= c.y - r; p.y--){ | 54 | case AIPATH: |
| 82 | if(eqpt(p, c)) | 55 | free(d->path); |
| 83 | continue; | 56 | break; |
| 84 | xd = p.x - c.x; | 57 | case AIWALKTO: |
| 85 | yd = p.y - c.y; | 58 | free(d->wd->path); |
| 86 | if(xd*xd+yd*yd <= r*r){ | 59 | free(d->wd); |
| 87 | xs = c.x - xd; | 60 | break; |
| 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 | } | | |
| 120 | } | 61 | } |
| | 62 | |
| | 63 | free(d); |
| 121 | } | 64 | } |
| 122 | | 65 | |
| 123 | static void | 66 | static AiData* |
| 124 | idleexit(AIState *a) | 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 | 74 | AiData* |
| 130 | idle(Monster *m) | 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; | 109 | AiData *d = aidata(AIWALKTO); |
| 133 | i = mkstate("idle", m, nil, idleenter, idleexec, idleexit); | 110 | d->wd = wd; |
| 134 | i->enter(i); | 111 | return d; |
| 135 | m->aglobal = i; | | |
| 136 | } | 112 | } |
| 137 | | 113 | |
| 138 | static void | 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 | int try; | 123 | int try; |
| 142 | Point p; | 124 | Point p; |
| 143 | Monster *m; | 125 | Monster *m; |
| 144 | AIState *ai; | 126 | AiData *pt, *in; |
| 145 | m = a->m; | | |
| 146 | | 127 | |
| 147 | if(a->aux != nil){ | 128 | m = v; |
| 148 | leave: | | |
| 149 | ai = mpopstate(m); | | |
| 150 | freestate(ai); | | |
| 151 | return; | | |
| 152 | } | | |
| 153 | | 129 | |
| 154 | try = 0; | 130 | for(try = 0; try < 5; try++){ |
| 155 | do { | | |
| 156 | p = addpt(m->pt, Pt(nrand(IDLERADIUS*2+1)-IDLERADIUS, nrand(IDLERADIUS*2+1)-IDLERADIUS)); | 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)); | 133 | if(ptinrect(p, m->l->r) && |
| 162 | a->aux=a; | 134 | !hasflagat(m->l, p, Fblocked|Fhasmonster) && |
| 163 | } | 135 | manhattan(m->pt, p) >= ORTHOCOST*2) |
| | 136 | break; |
| | 137 | } |
| 164 | | 138 | |
| 165 | AIState* | 139 | if(try == 5) |
| 166 | wander(Monster *m) | 140 | return TASKFAIL; |
| 167 | { | 141 | |
| 168 | AIState *i; | 142 | pt = aipt(p); |
| 169 | i = mkstate("wander", m, nil, nil, wanderexec, nil); | 143 | in = aiint(0); |
| 170 | return i; | 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; | 154 | static WalkToData* |
| 174 | struct walktodata | 155 | planpathcalc(Monster *m, Point dst, int offset) |
| 175 | { | 156 | { |
| 176 | Point dst; | | |
| 177 | Point last; | | |
| 178 | int npath; | 157 | int npath; |
| 179 | int next; | | |
| 180 | Point *path; | 158 | Point *path; |
| 181 | int wait; | 159 | WalkToData *wd; |
| 182 | int blocked; | 160 | |
| 183 | }; | 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 | static int | 173 | static int |
| 186 | walktoinner(AIState *a) | 174 | planpathkey(Monster *m, char *dst, char *offset, char *data) |
| 187 | { | 175 | { |
| 188 | Point p; | 176 | WalkToData *wd; |
| 189 | walktodata *d; | 177 | AiData *dd, *doff; |
| 190 | Monster *m; | | |
| 191 | d = a->aux; | | |
| 192 | m = a->m; | | |
| 193 | | 178 | |
| 194 | /* d->last might not equal m->pt if some other | 179 | dd = mapget(m->bb, dst); |
| 195 | * state pre-empted us, like attack */ | 180 | doff = mapget(m->bb, offset); |
| 196 | if(!eqpt(d->last, m->pt) || d->blocked > d->wait){ | | |
| 197 | goto cleanup; | | |
| 198 | } | | |
| 199 | | 181 | |
| 200 | //dbg("%s path -> %P %d/%d", m->md->name, d->dst, d->next, d->npath); | 182 | wd = planpathcalc(m, dd->pt, doff->i); |
| 201 | if(d->path == nil){ | 183 | if(wd == nil) |
| 202 | /* plan route */ | 184 | return TASKFAIL; |
| 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 | } | | |
| 218 | | 185 | |
| 219 | if(d->next == d->npath){ | 186 | if(mapset(m->bb, data, aiwd(wd)) < 0) |
| 220 | goto cleanup; | 187 | OOM(); |
| 221 | } | | |
| 222 | | 188 | |
| 223 | p = d->path[d->next]; | 189 | return TASKSUCCESS; |
| 224 | //dbg("next %P %06b", p, flagat(m->l, p)); | 190 | } |
| 225 | | 191 | |
| 226 | if(hasflagat(m->l, p, Fhasmonster|Fblocked)){ | 192 | static int |
| 227 | d->blocked++; | 193 | planpathwander(void *v) |
| 228 | dbg("%s blocked %d/%d", m->md->name, d->blocked, d->wait); | 194 | { |
| 229 | return 0; | 195 | Monster *m; |
| 230 | } | | |
| 231 | | 196 | |
| 232 | d->blocked = 0; | 197 | m = v; |
| 233 | d->next++; | | |
| 234 | | 198 | |
| 235 | maction(m, MMOVE, p); | 199 | return planpathkey(m, "wander_dst", "wander_offset", "wander_data"); |
| 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; | | |
| 244 | } | 200 | } |
| 245 | | 201 | |
| 246 | static void | 202 | static int |
| 247 | walktoexec(AIState *a) | 203 | planpathattack(void *v) |
| 248 | { | 204 | { |
| 249 | Monster *m; | 205 | Monster *m; |
| 250 | m = a->m; | | |
| 251 | | 206 | |
| 252 | if(walktoinner(a) < 0){ | 207 | m = v; |
| 253 | a = mpopstate(m); | 208 | |
| 254 | freestate(a); | 209 | return planpathkey(m, "attack_dst", "attack_offset", "attack_data"); |
| 255 | return; | | |
| 256 | } | | |
| 257 | } | 210 | } |
| 258 | | 211 | |
| 259 | static void | 212 | static int |
| 260 | walktoexit(AIState *a) | 213 | walktoinner(Monster *m, char *data) |
| 261 | { | 214 | { |
| 262 | walktodata *d; | 215 | Point np; |
| 263 | d = a->aux; | 216 | WalkToData *wd; |
| 264 | if(d->path != nil) | 217 | AiData *dwd; |
| 265 | free(d->path); | 218 | |
| 266 | free(d); | 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* | 242 | static int |
| 270 | walkto(Monster *m, Point p, int wait) | 243 | walktowander(void *v) |
| 271 | { | 244 | { |
| 272 | walktodata *d; | 245 | Monster *m; |
| 273 | AIState *i; | 246 | |
| 274 | d = mallocz(sizeof(*d), 1); | 247 | m = v; |
| 275 | d->dst = p; | 248 | |
| 276 | d->last = m->pt; | 249 | return walktoinner(m, "wander_data"); |
| 277 | d->wait = wait; | | |
| 278 | assert(d != nil); | | |
| 279 | i = mkstate("walk", m, d, nil, walktoexec, walktoexit); | | |
| 280 | return i; | | |
| 281 | } | 250 | } |
| 282 | | 251 | |
| 283 | typedef struct attackdata attackdata; | 252 | static int |
| 284 | struct attackdata | 253 | walktoattack(void *v) |
| 285 | { | 254 | { |
| 286 | Monster *mt; | 255 | Monster *m; |
| 287 | Point last; | 256 | m = v; |
| 288 | AIState *w; | 257 | return walktoinner(m, "attack_data"); |
| 289 | walktodata *wd; | 258 | } |
| | 259 | |
| | 260 | enum { |
| | 261 | ATTACKRADIUS = 6, |
| 290 | }; | 262 | }; |
| 291 | | 263 | |
| 292 | static void | 264 | static int |
| 293 | attackenter(AIState *a) | 265 | checkenemy(Monster *m, Monster *target) |
| 294 | { | 266 | { |
| 295 | attackdata *d; | 267 | if(m == target) |
| 296 | d = a->aux; | 268 | return -1; |
| 297 | if(d->w->enter != nil) | 269 | if(target->flags & Mdead) |
| 298 | d->w->enter(d->w); | 270 | return -1; |
| | 271 | if(abs(m->align - target->align) <= 15) |
| | 272 | return -1; |
| | 273 | return 0; |
| 299 | } | 274 | } |
| 300 | | 275 | |
| 301 | static void | 276 | static int |
| 302 | attackexec(AIState *a) | 277 | findenemy(void *v) |
| 303 | { | 278 | { |
| 304 | int n; | 279 | int i, r, xd, yd, xs, ys; |
| 305 | attackdata *d; | 280 | Point c, p, s; |
| 306 | Monster *m, *mt; | 281 | Tile *t; |
| 307 | m = a->m; | 282 | Monster *m, *target; |
| 308 | d = a->aux; | 283 | AiData *dtgt; |
| 309 | mt = d->mt; | 284 | |
| 310 | | 285 | m = v; |
| 311 | n = manhattan(m->pt, mt->pt); | 286 | c = m->pt; |
| 312 | if(mt->flags & Mdead || n > ORTHOCOST*8 ){ | 287 | |
| 313 | /* stop the chase */ | 288 | r = ATTACKRADIUS; |
| 314 | a = mpopstate(m); | 289 | |
| 315 | freestate(a); | 290 | target = nil; |
| 316 | return; | 291 | |
| 317 | } else if(n > ORTHOCOST){ | 292 | /* check if we already have a target */ |
| 318 | /* walk to target */ | 293 | dtgt = mapget(m->bb, "attack_target"); |
| 319 | if(!eqpt(mt->pt, d->last)){ | 294 | if(dtgt != nil) |
| 320 | d->wd->dst = mt->pt; | 295 | target = dtgt->m; |
| 321 | } | 296 | |
| 322 | d->wd->wait = 2; | 297 | if(target != nil && checkenemy(m, target) == 0) |
| 323 | d->wd->last = m->pt; | 298 | return TASKSUCCESS; |
| 324 | if(walktoinner(d->w) < 0){ | 299 | |
| 325 | a = mpopstate(m); | 300 | for(p.x = c.x; p.x >= c.x - r; p.x--){ |
| 326 | freestate(a); | 301 | for(p.y = c.y; p.y >= c.y - r; p.y--){ |
| 327 | return; | 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 | 343 | static int |
| 337 | attackexit(AIState *a) | 344 | targetenemy(void *v) |
| 338 | { | 345 | { |
| 339 | attackdata *d; | 346 | Monster *m; |
| 340 | d = a->aux; | 347 | AiData *dtgt, *dst, *off; |
| 341 | | 348 | |
| 342 | if(d->w->exit != nil) | 349 | m = v; |
| 343 | d->w->exit(d->w); | 350 | |
| 344 | freestate(d->w); | 351 | dtgt = mapget(m->bb, "attack_target"); |
| 345 | mfree(d->mt); | 352 | if(dtgt == nil) |
| 346 | free(d); | 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* | 408 | static BehaviorNode* |
| 350 | attack(Monster *m, Monster *targ) | 409 | bnattack(void) |
| 351 | { | 410 | { |
| 352 | attackdata *d; | 411 | BehaviorNode *bnfind, *bntarget; |
| 353 | AIState *i; | 412 | BehaviorNode *bnplanpath, *bnmove, *bnhit; |
| 354 | d = mallocz(sizeof(*d), 1); | 413 | BehaviorNode *bnatk; |
| 355 | incref(&targ->ref); | 414 | |
| 356 | d->mt = targ; | 415 | bnfind = btleaf("find enemy", findenemy); |
| 357 | d->wd = mallocz(sizeof(walktodata), 1); | 416 | |
| 358 | d->w = mkstate("chase", m, d->wd, nil, nil, walktoexit); | 417 | bntarget = btleaf("target enemy", targetenemy); |
| 359 | i = mkstate("attack", m, d, attackenter, attackexec, attackexit); | 418 | bnplanpath = btleaf("attack plan path", planpathattack); |
| 360 | return i; | 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 | } |