From 7961554a72e475cd71bd34f65bb548c1b13dc685 Mon Sep 17 00:00:00 2001 From: skotlex Date: Mon, 10 Apr 2006 21:38:05 +0000 Subject: - Modified the unit_data structure to handle automatically switching between chasing and attacking a character. Note that it's a work in progress and not yet properly tested/finished... git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@5979 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/map.c | 2 +- src/map/map.h | 2 + src/map/mob.c | 169 ++++++----------------------------------- src/map/pc.c | 33 ++++---- src/map/pet.c | 68 ++++++----------- src/map/skill.h | 4 + src/map/unit.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++------ src/map/unit.h | 4 +- 8 files changed, 272 insertions(+), 237 deletions(-) (limited to 'src/map') diff --git a/src/map/map.c b/src/map/map.c index 156ae7b1c..55b7996a4 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -1412,7 +1412,7 @@ int map_search_freecell(struct block_list *src, int m, int *x,int *y, int rx, in if (map_getcell(m,*x,*y,CELL_CHKREACH)) { - if(flag&2 && !unit_can_reach(src, *x, *y)) + if(flag&2 && !unit_can_reach_pos(src, *x, *y, 1)) continue; if(flag&4 && spawn++ < battle_config.no_spawn_on_player && map_foreachinarea(map_count_sub, m, diff --git a/src/map/map.h b/src/map/map.h index a94da16df..4380cd9f5 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -359,7 +359,9 @@ struct unit_data { int skilltimer; int attacktarget; int attacktimer; + int walktarget; int walktimer; + int chaserange; unsigned int attackabletime; unsigned int canact_tick; unsigned int canmove_tick; diff --git a/src/map/mob.c b/src/map/mob.c index 509f1c359..bdf2360f2 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -515,9 +515,7 @@ int mob_spawn_guardian(struct map_session_data *sd,char *mapname, */ int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state) { - int dx,dy; - struct walkpath_data wpd; - int i, easy = 0; + int easy = 0; nullpo_retr(0, md); nullpo_retr(0, bl); @@ -531,41 +529,7 @@ int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state easy = 1; break; } - - if( md->bl.m != bl->m) // 違うャbプ - return 0; - - if( md->bl.x==bl->x && md->bl.y==bl->y ) // 同じマス - return 1; - - if( range>0 && !check_distance_bl(&md->bl, bl, range)) - return 0; - - // Obstacle judging - wpd.path_len=0; - wpd.path_pos=0; - wpd.path_half=0; - if(path_search_real(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x,bl->y,easy,CELL_CHKNOREACH)!=-1) - return 1; - - // It judges whether it can adjoin or not. - dx=bl->x - md->bl.x; - dy=bl->y - md->bl.y; - dx=(dx>0)?1:((dx<0)?-1:0); - dy=(dy>0)?1:((dy<0)?-1:0); - if (map_getcell(md->bl.m,bl->x+dx,bl->y+dy,CELL_CHKNOREACH)) - { //Look for a suitable cell to place in. - for(i=0;i<9 && map_getcell(md->bl.m,bl->x-1+i/3,bl->y-1+i%3,CELL_CHKNOREACH);i++); - if (i<9) { - dx = 1-i/3; - dy = 1-i%3; - } - } - - if(path_search_real(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-dx,bl->y-dy,easy,CELL_CHKNOREACH)!=-1) - return 1; - - return 0; + return unit_can_reach_bl(&md->bl, bl, range, easy, NULL, NULL); } /*========================================== @@ -952,24 +916,8 @@ static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick) (md->master_dist>MOB_SLAVEDISTANCE || md->master_dist == 0) && unit_can_move(&md->bl)) { - int i=0,dx,dy; mob_stop_attack(md); - do { - if(i<=5){ - dx=bl->x - md->bl.x; - dy=bl->y - md->bl.y; - - if(dx<0) dx+=rand()%MOB_SLAVEDISTANCE +1; - else if(dx>0) dx-=rand()%MOB_SLAVEDISTANCE +1; - if(dy<0) dy+=rand()%MOB_SLAVEDISTANCE +1; - else if(dy>0) dy-=rand()%MOB_SLAVEDISTANCE +1; - - }else{ - old_dist = MOB_SLAVEDISTANCE*2+1; - dx=bl->x - md->bl.x + rand()%old_dist - MOB_SLAVEDISTANCE; - dy=bl->y - md->bl.y + rand()%old_dist - MOB_SLAVEDISTANCE; - } - } while(!unit_walktoxy(&md->bl,md->bl.x+dx,md->bl.y+dy,0)&& ++i<10); + unit_walktobl(&md->bl, bl, MOB_SLAVEDISTANCE, 0); } } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) { //Delete the summoned mob if it's in a gvg ground and the master is elsewhere. [Skotlex] @@ -1033,7 +981,7 @@ int mob_randomwalk(struct mob_data *md,int tick) nullpo_retr(0, md); speed=status_get_speed(&md->bl); - if(DIFF_TICK(md->next_walktime,tick)<0){ + if(DIFF_TICK(md->next_walktime,tick)<0 && unit_can_move(&md->bl)){ int i,x,y,c,d=12-md->move_fail_count; if(d<5) d=5; for(i=0;itype == BL_PC && !(mode&MD_BOSS) && ((struct map_session_data*)tbl)->state.gangsterparadise )) { //Unlock current target. - if (md->ud.walktimer != -1 && (battle_config.mob_ai&8 || !tbl)) //Inmediately stop chasing. + if (battle_config.mob_ai&8) //Inmediately stop chasing. mob_stop_walking(md,2); mob_unlocktarget(md, tick-(battle_config.mob_ai&8?3000:0)); //Imediately do random walk. tbl = NULL; @@ -1234,91 +1182,27 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap) mob_unlocktarget(md,tick); return 0; } - if (!check_distance_bl(&md->bl, tbl, view_range)) - { //Run towards the enemy when out of range? - if (!can_move) - { //Give it up. - if (can_walk) - mob_unlocktarget(md,tick); - return 0; - } - dx = tbl->x+(tbl->x > md->bl.x?-1:+1); - dy = tbl->y+(tbl->y > md->bl.y?-1:+1); - unit_walktoxy(&md->bl, dx, dy, 0); + if (!can_move) //Wait until you can move? return 0; - } md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH; - mobskill_use (md, tick, -1); - if (!can_move) //Wait until you can move? + if (md->ud.walktimer != -1 && md->ud.walktarget == tbl->id && + ( + !battle_config.mob_ai&1 || + check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->db->range) + )) //Current target tile is still within attack range. return 0; - if (md->ud.walktimer != -1 && - (!battle_config.mob_ai&1 || - check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->db->range)) //Current target tile is still within attack range. - ) { - return 0; //No need to follow, already doing it? - } //Target reachable. Locate suitable spot to move to. - i = j = 0; - dx = tbl->x - md->bl.x; - dy = tbl->y - md->bl.y; - if (dx < 0) dx=-1; - else if (dx > 0) dx=1; - if (dy < 0) dy=-1; - else if (dy > 0) dy=1; - if(!unit_walktoxy(&md->bl, tbl->x -dx, tbl->y -dy, 0)) { - j = (dy+1) + 3*(dx+1); - for (i = j+1; i%9 != j; i++) { - dx = -1+(i%9)/3; - dy = -1+(i%3); -#ifdef CELL_NOSTACK - if (map_getcell(md->bl.m, tbl->x-dx, tbl->y-dy, CELL_CHKSTACK)) - continue; -#endif - if (unit_walktoxy(&md->bl, tbl->x-dx, tbl->y-dy, 0)) - break; - } -#ifdef CELL_NOSTACK - if (i%9 == j) - { //All adjacent cells are taken. Try roaming around on 5x5 - for (i = j+1; i%9 != j; i++) { - dx = 2*(-1+(i%9)/3); - dy = 2*(-1+(i%3)); - if (map_getcell(md->bl.m, tbl->x-dx, tbl->y-dy, CELL_CHKSTACK)) - continue; - if (unit_walktoxy(&md->bl, tbl->x-dx, tbl->y-dy, 0)) { - unit_set_walkdelay(&md->bl, tick, 1000, 1); - break; - } - } - } - if (i%9 == j) - { - //On stacked mode, it is much more likely that you just can't reach the target. So unlock it - mob_unlocktarget(md, tick); - unit_set_walkdelay(&md->bl, tick, 1000, 1); - return 0; - } -#else - if (i%9 == j) - { //Failed? Try going to the other side of the target before retrying. - if (dx < 0) dx = 2; - else if (dx > 0) dx = -2; - if (dy < 0) dy = 2; - else if (dy > 0) dy = -2; - unit_walktoxy (&md->bl, tbl->x+dx, tbl->y+dy, 0); - } -#endif - } + unit_walktobl(&md->bl, tbl, md->db->range, !battle_config.mob_ai&1); return 0; } //Target within range, engage - md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; mob_stop_walking(md,1); + md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; unit_attack(&md->bl,tbl->id,1); return 0; } else { //Target is BL_ITEM, attempt loot. struct flooritem_data *fitem; - + int i; if (md->lootitem == NULL) { //Can't loot... mob_unlocktarget (md, tick); @@ -1335,15 +1219,10 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap) if (!can_move) // 動けない状態にある return 0; md->state.skillstate = MSS_LOOT; // ルート時スキル使用 - mobskill_use(md, tick, -1); - if (md->ud.walktimer != -1 && - check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, 0)) - { //Already on the way to looting. + if (md->ud.walktimer != -1 && md->ud.walktarget == tbl->id) + //Already on the way to looting. return 0; - } - dx = tbl->x - md->bl.x; - dy = tbl->y - md->bl.y; - if (!unit_walktoxy(&md->bl, md->bl.x+dx, md->bl.y+dy, 0)) + if (!unit_walktobl(&md->bl, tbl, 0, 1)) mob_unlocktarget(md, tick); //Can't loot... return 0; } @@ -1357,9 +1236,6 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap) memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0])); if(log_config.pick > 0) //Logs items, taken by (L)ooter Mobs [Lupus] log_pick((struct map_session_data*)md, "L", md->class_, md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]); - } else if (battle_config.monster_loot_type == 1) { //Can't loot, stuffed! - mob_unlocktarget(md,tick); - return 0; } else { //Destroy first looted item... if (md->lootitem[0].card[0] == (short)0xff00) intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) ); @@ -1458,10 +1334,11 @@ static int mob_ai_sub_lazy(DBKey key,void * data,va_list app) else if(rand()%1000spawn && !md->spawn->x && !md->spawn->y) - && !md->target_id && !(mode&MD_BOSS)) - unit_warp(&md->bl,-1,-1,-1,0); + // People don't want this, it seems custom, noone can prove it.... +// else if( rand()%1000spawn && !md->spawn->x && !md->spawn->y) +// && !md->target_id && !(mode&MD_BOSS)) +// unit_warp(&md->bl,-1,-1,-1,0); }else{ // Since PC is not even in the same map, suitable processing is carried out even if it takes. diff --git a/src/map/pc.c b/src/map/pc.c index 5852edc08..8502b5ce8 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -3821,26 +3821,23 @@ int pc_follow_timer(int tid,unsigned int tick,int id,int data) if (pc_isdead(sd)) return 0; - if ((tsd = map_id2sd(sd->followtarget)) != NULL) - { - if (pc_isdead(tsd)) - return 0; + if ((tsd = map_id2sd(sd->followtarget)) == NULL || pc_isdead(tsd)) + return 0; - // either player or target is currently detached from map blocks (could be teleporting), - // but still connected to this map, so we'll just increment the timer and check back later - if (sd->bl.prev != NULL && tsd->bl.prev != NULL && - sd->ud.skilltimer == -1 && sd->ud.attacktimer == -1 && sd->ud.walktimer == -1) - { - if((sd->bl.m == tsd->bl.m) && unit_can_reach(&sd->bl,tsd->bl.x,tsd->bl.y)) { - if (!check_distance_bl(&sd->bl, &tsd->bl, 5) && unit_can_move(&sd->bl)) - unit_walktoxy(&sd->bl,tsd->bl.x,tsd->bl.y, 0); - } else - pc_setpos(sd, tsd->mapindex, tsd->bl.x, tsd->bl.y, 3); - } - sd->followtimer = add_timer( - tick + sd->aspd + rand() % 1000, // increase time a bit to loosen up map's load - pc_follow_timer, sd->bl.id, 0); + // either player or target is currently detached from map blocks (could be teleporting), + // but still connected to this map, so we'll just increment the timer and check back later + if (sd->bl.prev != NULL && tsd->bl.prev != NULL && + sd->ud.skilltimer == -1 && sd->ud.attacktimer == -1 && sd->ud.walktimer == -1) + { + if((sd->bl.m == tsd->bl.m) && unit_can_reach_bl(&sd->bl,&tsd->bl, AREA_SIZE, 0, NULL, NULL)) { + if (!check_distance_bl(&sd->bl, &tsd->bl, 5)) + unit_walktobl(&sd->bl, &tsd->bl, 5, 0); + } else + pc_setpos(sd, tsd->mapindex, tsd->bl.x, tsd->bl.y, 3); } + sd->followtimer = add_timer( + tick + sd->aspd + rand() % 1000, // increase time a bit to loosen up map's load + pc_follow_timer, sd->bl.id, 0); return 0; } diff --git a/src/map/pet.c b/src/map/pet.c index 88eb26cd4..4904d1c45 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -81,33 +81,33 @@ static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir) dy = -diry[dir]*2; x = tx + dx; y = ty + dy; - if(!unit_can_reach(&pd->bl,x,y)) { + if(!unit_can_reach_pos(&pd->bl,x,y,0)) { if(dx > 0) x--; else if(dx < 0) x++; if(dy > 0) y--; else if(dy < 0) y++; - if(!unit_can_reach(&pd->bl,x,y)) { + if(!unit_can_reach_pos(&pd->bl,x,y,0)) { for(i=0;i<12;i++) { k = rand()%8; dx = -dirx[k]*2; dy = -diry[k]*2; x = tx + dx; y = ty + dy; - if(unit_can_reach(&pd->bl,x,y)) + if(unit_can_reach_pos(&pd->bl,x,y,0)) break; else { if(dx > 0) x--; else if(dx < 0) x++; if(dy > 0) y--; else if(dy < 0) y++; - if(unit_can_reach(&pd->bl,x,y)) + if(unit_can_reach_pos(&pd->bl,x,y,0)) break; } } if(i>=12) { x = tx; y = ty; - if(!unit_can_reach(&pd->bl,x,y)) + if(!unit_can_reach_pos(&pd->bl,x,y,0)) return 1; } } @@ -892,7 +892,7 @@ static int pet_randomwalk(struct pet_data *pd,unsigned int tick) speed = status_get_speed(&pd->bl); - if(DIFF_TICK(pd->next_walktime,tick) < 0){ + if(DIFF_TICK(pd->next_walktime,tick) < 0 && unit_can_move(&pd->bl)) { int i,x,y,c,d=12-pd->move_fail_count; if(d<5) d=5; for(i=0;imsd; @@ -960,14 +959,13 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) //Master too far, chase. if(pd->target_id) pet_unlocktarget(pd); - if(pd->ud.walktimer != -1 && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3)) + if(pd->ud.walktimer != -1 && pd->ud.walktarget == sd->bl.id) return 0; //Already walking to him pd->speed = (sd->speed>>1); if(pd->speed <= 0) pd->speed = 1; - pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir); - if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0)) + if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0)); pet_randomwalk(pd,tick); return 0; } @@ -987,11 +985,12 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) } // ペットによるルート - if(!pd->target_id && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) + if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) { //Use half the pet's range of sight. + int itc=0; map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl, - pd->db->range2/2, BL_ITEM,pd,&i); - + pd->db->range2/2, BL_ITEM,pd,&itc); + } if (!target) { //Just walk around. if (check_distance_bl(&sd->bl, &pd->bl, 3)) @@ -1014,51 +1013,26 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) if(pd->ud.walktimer != -1 && check_distance_blxy(target, pd->ud.to_x,pd->ud.to_y, pd->db->range)) return 0; - if(!unit_can_reach(&pd->bl, target->x, target->y)) + if(!unit_walktobl(&pd->bl, target, pd->db->range, 2)) { //Unreachable target. pet_unlocktarget(pd); - return 0; - } - i=0; - do { - if(i==0) { // 最初はAEGISと同じ方法で検索 - dx=target->x - pd->bl.x; - dy=target->y - pd->bl.y; - if(dx<0) dx++; - else if(dx>0) dx--; - if(dy<0) dy++; - else if(dy>0) dy--; - } - else { // だめならAthena式(ランダム) - dx=target->x - pd->bl.x + rand()%3 - 1; - dy=target->y - pd->bl.y + rand()%3 - 1; - } - } while(!unit_walktoxy(&pd->bl,pd->bl.x+dx,pd->bl.y+dy,0) && ++i<5); - - if(i>=5) { - if(dx<0) dx=2; - else if(dx>0) dx=-2; - if(dy<0) dy=2; - else if(dy>0) dy=-2; - unit_walktoxy(&pd->bl,pd->bl.x+dx,pd->bl.y+dy,0); } return 0; } //End Chase pet_stop_walking(pd,1); + if (pd->ud.attacktimer != -1 && pd->ud.attacktarget == pd->target_id) + return 0; //Already attacking. //Continuous attack. unit_attack(&pd->bl, pd->target_id, 1); } else { //Item Targeted, attempt loot if (!check_distance_bl(&pd->bl, target, 1)) { //Out of range - if(pd->ud.walktimer != -1 && check_distance_blxy(target, pd->ud.to_x, pd->ud.to_y, 0)) - return 0; // 既に移動中 + if(pd->ud.walktimer != -1 && pd->ud.walktarget == pd->target_id) + return 0; - if(!unit_can_reach(&pd->bl, target->x, target->y)) - { //Unreachable target. + if(!unit_walktobl(&pd->bl, target, 0, 1)) //Unreachable target. pet_unlocktarget(pd); - return 0; - } - unit_walktoxy(&pd->bl, target->x, target->y, 1); + return 0; } else{ // アイテムまでたどり着いた struct flooritem_data *fitem = (struct flooritem_data *)target; pet_stop_walking(pd,1); @@ -1109,8 +1083,8 @@ int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) if(pd->loot == NULL || pd->loot->item == NULL || (pd->loot->count >= pd->loot->max) || (sd_id && pd->msd && pd->msd->bl.id != sd_id)) return 0; - if(bl->m == pd->bl.m && check_distance_bl(&pd->bl, bl, pd->db->range2) && - unit_can_reach(&pd->bl,bl->x,bl->y) && rand()%1000<1000/(++(*itc))) + if(bl->m == pd->bl.m && unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) + && rand()%1000<1000/(++(*itc))) pd->target_id=bl->id; return 0; } diff --git a/src/map/skill.h b/src/map/skill.h index f485e3394..f27a47f5a 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -41,6 +41,10 @@ #define INF2_PARTY_ONLY 1024 #define INF2_GUILD_ONLY 2048 +//Walk intervals at which chase-skills are attempted to be triggered. +//Note that every 2 is an actual cell walked. +#define WALK_SKILL_INTERVAL 6 + // スキルデ?タベ?ス struct skill_db { char *name; diff --git a/src/map/unit.c b/src/map/unit.c index e928178cd..6b5dd0433 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -57,12 +57,12 @@ int unit_walktoxy_sub(struct block_list *bl) memcpy(&ud->walkpath,&wpd,sizeof(wpd)); + ud->state.change_walk_target=0; + if (bl->type == BL_PC) clif_walkok((TBL_PC*)bl); clif_move(bl); - ud->state.change_walk_target=0; - if(ud->walkpath.path_pos>=ud->walkpath.path_len) i = -1; else if(ud->walkpath.path[ud->walkpath.path_pos]&1) @@ -143,9 +143,6 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data) x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE, dx,dy,sd?BL_ALL:BL_PC,bl); - if(md && md->min_chase > md->db->range2) - md->min_chase--; - x += dx; y += dy; map_moveblock(bl, x, y, tick); @@ -178,11 +175,19 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data) if ( (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && sd->sc.data[SC_MIRACLE].timer==-1 && - rand()%100000 < battle_config.sg_miracle_skill_ratio + !ud->walkpath.path_pos%WALK_SKILL_INTERVAL && + rand()%10000 < battle_config.sg_miracle_skill_ratio ) { //SG_MIRACLE [Komurka] clif_displaymessage(sd->fd,"[Miracle of the Sun, Moon and Stars]"); sc_start(&sd->bl,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration); } + } else if (md) { + if (ud->attacktarget) { + if(md->min_chase > md->db->range2) md->min_chase--; + if(!ud->walkpath.path_pos%WALK_SKILL_INTERVAL && + mobskill_use(md, tick, -1)) + return 0; + } } } @@ -200,8 +205,23 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data) ud->walktimer = add_timer(tick+i,unit_walktoxy_timer,id,ud->walkpath.path_pos); } else if(sd && sd->sc.count && sd->sc.data[SC_RUN].timer!=-1) //Keep trying to run. pc_run(sd, sd->sc.data[SC_RUN].val1, sd->sc.data[SC_RUN].val2); - else - { //Stopped walking. Update to_x and to_y to current location [Skotlex] + else if (ud->walktarget) { + //Update target trajectory. + struct block_list *tbl = map_id2bl(ud->walktarget); + if (!tbl) { //Cancel chase. + ud->to_x = bl->x; + ud->to_y = bl->y; + return 0; + } + if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange)) + { //Reached destination. + if (ud->attacktarget == tbl->id) + unit_attack(bl, tbl->id, ud->state.attack_continue); + } else { //Update chase-path + unit_walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy); + return 0; + } + } else { //Stopped walking. Update to_x and to_y to current location [Skotlex] ud->to_x = bl->x; ud->to_y = bl->y; // if (bl->type == BL_NPC) //Original eA code had this one only for BL_NPCs @@ -221,10 +241,11 @@ int unit_walktoxy( struct block_list *bl, int x, int y, int easy) { if( ud == NULL) return 0; // 移動出来ないユニットは弾く - if(!unit_can_move(bl) || !(status_get_mode(bl)&MD_CANMOVE) ) + if(!(status_get_mode(bl)&MD_CANMOVE) || !unit_can_move(bl)) return 0; ud->state.walk_easy = easy; + ud->walktarget = 0; ud->to_x = x; ud->to_y = y; @@ -242,6 +263,87 @@ int unit_walktoxy( struct block_list *bl, int x, int y, int easy) { } } +static int unit_walktobl_sub(int tid,unsigned int tick,int id,int data) +{ + struct block_list *bl = map_id2bl(id); + struct unit_data *ud = bl?unit_bl2ud(bl):NULL; + + if (ud && ud->walktimer == -1 && ud->walktarget == data) + { + if (DIFF_TICK(ud->canmove_tick, tick) > 0) //Keep waiting? + add_timer(ud->canmove_tick+1, unit_walktobl_sub, id, data); + else if (unit_can_move(bl)) + unit_walktoxy_sub(bl); + } + return 0; +} + +// Chases a tbl. If the flag&1, use hard-path seek, +// if flag&2, start attacking upon arrival within range. +int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int flag) { + struct unit_data *ud = NULL; + struct status_change *sc = NULL; + int i; + nullpo_retr(0, bl); + nullpo_retr(0, tbl); + + ud = unit_bl2ud(bl); + if( ud == NULL) return 0; + + if (!(status_get_mode(bl)&MD_CANMOVE)) + return 0; + + i = distance_bl(bl, tbl)+1; + if (!unit_can_reach_bl(bl, tbl, i, flag&1, &ud->to_x, &ud->to_y)) { + ud->to_x = bl->x; + ud->to_y = bl->y; + return 0; + } + + ud->state.walk_easy = flag&1; + ud->walktarget = tbl->id; + ud->chaserange = range; + + if (range) { + //Adjust target cell + if (i < range) { + //We are already within required distance! + if (flag&2) //Attack + unit_attack(bl, tbl->id, 1); + return 1; + } + //Trim the last part of the path to account for range. + for (i = 1; i <= range && ud->walkpath.path_len>0; i++) { + int dir; + ud->walkpath.path_len--; + dir = ud->walkpath.path[ud->walkpath.path_len]; + ud->to_x -= dirx[dir]; + ud->to_y -= diry[dir]; + } + } + sc = status_get_sc(bl); + if (sc && sc->count && sc->data[SC_CONFUSION].timer != -1) //Randomize the target position + map_random_dir(bl, &ud->to_x, &ud->to_y); + + if (flag&2) { //Chase to attack. + ud->attacktarget = tbl->id; + ud->state.attack_continue = 1; + } + + if(ud->walktimer != -1) { + ud->state.change_walk_target = 1; + return 1; + } + if (DIFF_TICK(ud->canmove_tick, gettick()) > 0) + { //Can't move, wait a bit before invoking the movement. + add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->walktarget); + return 1; + } else if (!unit_can_move(bl)) + return 0; + + return unit_walktoxy_sub(bl); +} + //Instant warp function. int unit_movepos(struct block_list *bl,int dst_x,int dst_y, int easy, int checkpath) { @@ -543,6 +645,13 @@ int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int return 0; } ud->canmove_tick = tick + delay; + if (ud->walktimer != -1) + { //Stop walking, if chasing, readjust timers. + delete_timer(ud->walktimer, unit_walktoxy_timer); + clif_fixpos(bl); + if(ud->walktarget) + add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->walktarget); + } return 1; } @@ -552,8 +661,7 @@ static int unit_walkdelay_sub(int tid, unsigned int tick, int id, int data) if (!bl || status_isdead(bl)) return 0; - if (unit_set_walkdelay(bl, tick, data, 0)) - unit_stop_walking(bl,3); + unit_set_walkdelay(bl, tick, data, 0); return 0; } @@ -954,7 +1062,11 @@ int unit_stop_attack(struct block_list *bl) //Means current target is unattackable. For now only unlocks mobs. int unit_unattackable(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); - if (ud) ud->attacktarget = 0; + if (ud) { + ud->attacktarget = 0; + ud->walktarget = 0; + } + if(bl->type == BL_MOB) mob_unlocktarget((struct mob_data*)bl, gettick()) ; else if(bl->type == BL_PET) @@ -995,6 +1107,11 @@ int unit_attack(struct block_list *src,int target_id,int type) ud->attacktarget = target_id; ud->state.attack_continue = type; + if (type) { //If you re to attack continously, set to auto-case character + ud->walktarget = target_id; + ud->chaserange = status_get_range(src); + } + //Just change target/type. [Skotlex] if(ud->attacktimer != -1) return 0; @@ -1012,7 +1129,7 @@ int unit_attack(struct block_list *src,int target_id,int type) * *------------------------------------------ */ -int unit_can_reach(struct block_list *bl,int x,int y) +int unit_can_reach_pos(struct block_list *bl,int x,int y, int easy) { struct walkpath_data wpd; @@ -1025,9 +1142,63 @@ int unit_can_reach(struct block_list *bl,int x,int y) wpd.path_len=0; wpd.path_pos=0; wpd.path_half=0; - return (path_search_real(&wpd,bl->m,bl->x,bl->y,x,y,0,CELL_CHKNOREACH)!=-1)?1:0; + return (path_search_real(&wpd,bl->m,bl->x,bl->y,x,y,easy,CELL_CHKNOREACH)!=-1); +} + +/*========================================== + * + *------------------------------------------ + */ +int unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, int easy, short *x, short *y) +{ + struct walkpath_data wpd; + int i; + short dx,dy; + nullpo_retr(0, bl); + nullpo_retr(0, tbl); + + if( bl->m != tbl->m) + return 0; + + if( bl->x==tbl->x && bl->y==tbl->y ) + return 1; + + if(range>0 && !check_distance_bl(bl, tbl, range)) + return 0; + + wpd.path_len=0; + wpd.path_pos=0; + wpd.path_half=0; + +#ifndef CELL_NOSTACK + //Skip direct path seeking when in nostacking mode. + if(path_search_real(&wpd,bl->m,bl->x,bl->y,tbl->x,tbl->y,easy,CELL_CHKNOREACH)!=-1) { + if (x) *x = tbl->x; + if (y) *y = tbl->y; + return 1; + } +#endif + + // It judges whether it can adjoin or not. + dx=tbl->x - bl->x; + dy=tbl->y - bl->y; + dx=(dx>0)?1:((dx<0)?-1:0); + dy=(dy>0)?1:((dy<0)?-1:0); + + if (map_getcell(tbl->m,tbl->x+dx,tbl->y+dy,CELL_CHKNOREACH)) + { //Look for a suitable cell to place in. + for(i=0;i<9 && map_getcell(tbl->m,tbl->x-dirx[i],tbl->y-diry[i],CELL_CHKNOREACH);i++); + if (i==9) return 0; //No valid cells. + dx = dirx[i]; + dy = diry[i]; + } + + if (x) *x = tbl->x-dx; + if (y) *y = tbl->y-dy; + return (path_search_real(&wpd,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH)!=-1); } + /*========================================== * PCの攻撃 (timer関数) *------------------------------------------ @@ -1078,23 +1249,29 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t return 1; } - range = status_get_range( src ); + range = status_get_range(src); if(ud->walktimer != -1) range++; //Extra range when walking. if(!sd || sd->status.weapon != 11) range++; //Dunno why everyone but bows gets this extra range... if(unit_is_walking(target)) range++; //Extra range when chasing if(!check_distance_bl(src,target,range) ) { - if(!unit_can_reach(src,target->x,target->y)) - return 0; - if(sd) clif_movetoattack(sd,target); - return 1; + //Chase if required. + if(ud->state.attack_continue && ud->walktarget == target->id) { + if(sd) + clif_movetoattack(sd,target); + else + unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy); + } + return 1; } - if(!battle_check_range(src,target,range)) { //Within range, but no direct line of attack - if(unit_can_reach(src,target->x,target->y)) - unit_walktoxy(src,target->x,target->y, ud->state.walk_easy); - if(ud->state.attack_continue) + if(!battle_check_range(src,target,range)) { + //Within range, but no direct line of attack + if(ud->state.attack_continue && ud->walktarget == target->id) { + if(ud->chaserange > 2) ud->chaserange-=2; + unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy); ud->attacktimer = add_timer(tick + status_get_adelay(src),unit_attack_timer,src->id,0); + } return 1; } @@ -1141,7 +1318,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t // You can't move if you can't attack neither. // Only for non-players, since it makes it near impossible to run away when you are on auto-attack. - if (src->type != BL_PC) + if (src->type != BL_PC) unit_set_walkdelay(src, tick, status_get_amotion(src), 1); } @@ -1604,6 +1781,8 @@ int do_init_unit(void) { add_timer_func_list(unit_attack_timer, "unit_attack_timer"); add_timer_func_list(unit_walktoxy_timer,"unit_walktoxy_timer"); add_timer_func_list(unit_walkdelay_sub, "unit_walkdelay_sub"); + add_timer_func_list(unit_walktobl_sub, "unit_walktobl_sub"); + return 0; } diff --git a/src/map/unit.h b/src/map/unit.h index 9724a8523..f3c0c87c9 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -11,6 +11,7 @@ // 歩行開始 // 戻り値は、0 ( 成功 ), 1 ( 失敗 ) int unit_walktoxy( struct block_list *bl, int x, int y, int easy); +int unit_walktobl( struct block_list *bl, struct block_list *target, int range, int easy); // 歩行停止 // typeは以下の組み合わせ : @@ -31,7 +32,8 @@ int unit_setdir(struct block_list *bl,unsigned char dir); int unit_getdir(struct block_list *bl); // そこまで歩行でたどり着けるかの判定 -int unit_can_reach(struct block_list *bl,int x,int y); +int unit_can_reach_pos(struct block_list *bl,int x,int y,int easy); +int unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, int easy, short *x, short *y); // 攻撃関連 int unit_stop_attack(struct block_list *bl); -- cgit v1.2.3-60-g2f50