diff options
Diffstat (limited to 'src/map/mob.c')
-rw-r--r-- | src/map/mob.c | 169 |
1 files changed, 91 insertions, 78 deletions
diff --git a/src/map/mob.c b/src/map/mob.c index ffab804a6..2605b414f 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -52,7 +52,9 @@ struct mob_interface mob_s; #define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME) -#define MOB_LAZYSKILLPERC 0 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute) +// Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute) +// in Aegis, this is 100% for mobs that have been activated by players and none otherwise. +#define MOB_LAZYSKILLPERC(md) (md->state.spotted?1000:0) // Move probability for mobs away from players (rate of 1000 minute) // in Aegis, this is 100% for mobs that have been activated by players and none otherwise. #define MOB_LAZYMOVEPERC(md) ((md)->state.spotted?1000:0) @@ -946,7 +948,7 @@ int mob_spawn (struct mob_data *md) md->state.aggressive = md->status.mode&MD_ANGRY?1:0; md->state.skillstate = MSS_IDLE; - md->next_walktime = tick+rnd()%5000+1000; + md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME; md->last_linktime = tick; md->dmgtick = tick - 5000; md->last_pcneartime = 0; @@ -1074,15 +1076,6 @@ int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) && battle->check_range(&md->bl,bl,md->db->range2) ) { //Pick closest target? - - if( map->list[bl->m].icewall_num && - !path->search_long(NULL,bl->m,md->bl.x,md->bl.y,bl->x,bl->y,CELL_CHKICEWALL) ) { - - if( !check_distance_bl(&md->bl, bl, status_get_range(&md->bl) ) ) - return 0; - - } - (*target) = bl; md->target_id=bl->id; md->min_chase= dist + md->db->range3; @@ -1286,22 +1279,23 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) { md->state.skillstate = MSS_IDLE; case MSS_IDLE: // Idle skill. - if ((md->target_id || !(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) && - mob->skill_use(md, tick, -1)) + if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mob->skill_use(md, tick, -1)) break; //Random walk. if (!md->master_id && DIFF_TICK(md->next_walktime, tick) <= 0 && !mob->randomwalk(md,tick)) //Delay next random walk when this one failed. - md->next_walktime=tick+rnd()%3000; + md->next_walktime = tick+rnd()%1000; break; default: mob_stop_attack(md); - if (battle_config.mob_ai&0x8) - mob_stop_walking(md,1); //Immediately stop chasing. + mob_stop_walking(md,1); //Stop chasing. md->state.skillstate = MSS_IDLE; - md->next_walktime=tick+rnd()%3000+3000; + if(battle_config.mob_ai&0x8) //Walk instantly after dropping target + md->next_walktime = tick+rnd()%1000; + else + md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME; break; } if (md->target_id) { @@ -1309,6 +1303,10 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) { md->ud.target_to = 0; unit->set_target(&md->ud, 0); } + if(map->count_oncell(md->bl.m, md->bl.x, md->bl.y, BL_CHAR|BL_NPC, 1) > battle_config.official_cell_stack_limit) { + unit->walktoxy(&md->bl, md->bl.x, md->bl.y, 8); + } + return 0; } /*========================================== @@ -1328,6 +1326,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) { d =12-md->move_fail_count; if(d<5) d=5; + if(d>7) d=7; for(i=0;i<retrycount;i++){ // Search of a movable place int r=rnd(); x=r%(d*2+1)-d; @@ -1335,7 +1334,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) { x+=md->bl.x; y+=md->bl.y; - if((map->getcell(md->bl.m,x,y,CELL_CHKPASS)) && unit->walktoxy(&md->bl,x,y,1)){ + if(((x != md->bl.x) || (y != md->bl.y)) && map->getcell(md->bl.m,x,y,CELL_CHKPASS) && unit->walktoxy(&md->bl,x,y,8)){ break; } } @@ -1358,7 +1357,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) { } md->state.skillstate=MSS_WALK; md->move_fail_count=0; - md->next_walktime = tick+rnd()%3000+3000+c; + md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME+c; return 1; } @@ -1404,9 +1403,6 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { if (md->ud.skilltimer != INVALID_TIMER) return false; - if(md->ud.walktimer != INVALID_TIMER && md->ud.walkpath.path_pos <= 3) - return false; - // Abnormalities if(( md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT && md->sc.opt1 != OPT1_BURNING && md->sc.opt1 != OPT1_CRYSTALIZE ) || md->sc.data[SC_DEEP_SLEEP] || md->sc.data[SC_BLADESTOP] || md->sc.data[SC__MANHOLE] || md->sc.data[SC_CURSEDCIRCLE_TARGET]) { @@ -1434,10 +1430,13 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { || ((TBL_PC*)tbl)->invincible_timer != INVALID_TIMER) ) ) { - //Unlock current target. + //No valid target if (mob->warpchase(md, tbl)) return true; //Chasing this target. - mob->unlocktarget(md, tick-(battle_config.mob_ai&0x8?3000:0)); //Immediately do random walk. + if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh) + && (tbl || md->ud.walkpath.path_pos == 0)) + return true; //Walk at least "mob_chase_refresh" cells before dropping the target unless target is non-existent + mob_unlocktarget(md, tick); //Unlock target tbl = NULL; } } @@ -1448,12 +1447,14 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { if( md->attacked_id == md->target_id ) { //Rude attacked check. if( !battle->check_range(&md->bl, tbl, md->status.rhw.range) - && ( //Can't attack back and can't reach back. + && ( //Can't attack back and can't reach back. (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1) - || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP] - || md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target. - || !mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) + || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP] + || md->sc.data[SC__MANHOLE] // Not yet confirmed if boss will teleport once it can't reach target. + || md->walktoxy_fail_count > 0) ) + || !mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) + ) && md->state.attacked_count++ >= RUDE_ATTACKED_COUNT && !mob->skill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack && can_move && unit->escape(&md->bl, tbl, rnd()%10 +1)) // Attempt escape @@ -1471,10 +1472,12 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { || (battle_config.mob_ai&0x2 && !status->check_skilluse(&md->bl, abl, 0, 0)) // Cannot normal attack back to Attacker || (!battle->check_range(&md->bl, abl, md->status.rhw.range) // Not on Melee Range and ... && ( // Reach check - (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1) - || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP] - || md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target. - || !mob->can_reach(md, abl, dist+md->db->range3, MSS_RUSH) + (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1) + || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP] + || md->sc.data[SC__MANHOLE] // Not yet confirmed if boss will teleport once it can't reach target. + || md->walktoxy_fail_count > 0) + ) + || !mob->can_reach(md, abl, dist+md->db->range3, MSS_RUSH) ) ) ) { @@ -1547,7 +1550,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { } } - //This handles triggering idle walk/skill. + //This handles triggering idle/walk skill. mob->unlocktarget(md, tick); return true; } @@ -1604,48 +1607,56 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { mob->unlocktarget (md,tick); return true; } + //Attempt to attack. //At this point we know the target is attackable, we just gotta check if the range matches. - if (md->ud.target == tbl->id && md->ud.attacktimer != INVALID_TIMER) //Already locked. + if (battle->check_range(&md->bl, tbl, md->status.rhw.range) && !(md->sc.option&OPTION_HIDE)) + { //Target within range and able to use normal attack, engage + if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER) + { //Only attack if no more attack delay left + if(tbl->type == BL_PC) + mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus) + + if( !(mode&MD_RANDOMTARGET) ) + unit->attack(&md->bl,tbl->id,1); + else { // Attack once and find a new random target + int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range; + unit->attack(&md->bl,tbl->id, 0); + if ((tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size))) { + md->target_id = tbl->id; + md->min_chase = md->db->range3; + } + } + } return true; + } - if (battle->check_range (&md->bl, tbl, md->status.rhw.range)) - { //Target within range, engage + //Monsters in berserk state, unable to use normal attacks, will always attempt a skill + if(md->ud.walktimer == INVALID_TIMER && (md->state.skillstate == MSS_BERSERK || md->state.skillstate == MSS_ANGRY)) { + if (DIFF_TICK(md->ud.canmove_tick, tick) <= MIN_MOBTHINKTIME && DIFF_TICK(md->ud.canact_tick, tick) < -MIN_MOBTHINKTIME*IDLE_SKILL_INTERVAL) + { //Only use skill if able to walk on next tick and not used a skill the last second + mob->skill_use(md, tick, -1); + } + } - if(tbl->type == BL_PC) - mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus) + //Target still in attack range, no need to chase the target + if(battle->check_range(&md->bl, tbl, md->status.rhw.range)) + return true; - if(!(mode&MD_RANDOMTARGET)) - unit->attack(&md->bl,tbl->id,1); - else { // Attack once and find new random target - int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range; - unit->attack(&md->bl,tbl->id,0); - tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size); - // If no target was found, keep atacking the old one - if( tbl ) { - md->target_id = tbl->id; - md->min_chase = md->db->range3; - } - } + //Only update target cell / drop target after having moved at least "mob_chase_refresh" cells + if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh)) return true; - } //Out of range... - if (!(mode&MD_CANMOVE)) - { //Can't chase. Attempt an idle skill before unlocking. - md->state.skillstate = MSS_IDLE; - if (!mob->skill_use(md, tick, -1)) + if (!(mode&MD_CANMOVE) || (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0)) + { //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill. + if (md->ud.attacktimer == INVALID_TIMER) + { //Only unlock target if no more attack delay left + //This handles triggering idle/walk skill. mob->unlocktarget(md,tick); + } return true; - } - - if (!can_move) - { //Stuck. Attempt an idle skill - md->state.skillstate = MSS_IDLE; - if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) - mob->skill_use(md, tick, -1); - return true; - } + } if (md->ud.walktimer != INVALID_TIMER && md->ud.target == tbl->id && ( @@ -1655,6 +1666,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) { return true; //Follow up if possible. + //Hint: Chase skills are handled in the walktobl routine if(!mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) || !unit->walktobl(&md->bl, tbl, md->status.rhw.range, 2)) mob->unlocktarget(md,tick); @@ -1735,20 +1747,17 @@ int mob_ai_sub_lazy(struct mob_data *md, va_list args) { } if( DIFF_TICK(md->next_walktime,tick) < 0 && (status_get_mode(&md->bl)&MD_CANMOVE) && unit->can_move(&md->bl) ) { - if( map->list[md->bl.m].users > 0 ) - { - if( rnd()%1000 < MOB_LAZYMOVEPERC(md) ) - mob->randomwalk(md, tick); - else - if( rnd()%1000 < MOB_LAZYSKILLPERC ) //Chance to do a mob's idle skill. - mob->skill_use(md, tick, -1); - } - else - { - if( rnd()%1000 < MOB_LAZYMOVEPERC(md) ) - mob->randomwalk(md, tick); - } + if( rnd()%1000 < MOB_LAZYMOVEPERC(md) ) + mob_randomwalk(md, tick); + } + else if( md->ud.walktimer == INVALID_TIMER ) + { + //Because it is not unset when the mob finishes walking. + md->state.skillstate = MSS_IDLE; + if( rnd()%1000 < MOB_LAZYSKILLPERC(md) ) //Chance to do a mob's idle skill. + mob->skill_use(md, tick, -1); } + return 0; } @@ -2641,7 +2650,7 @@ void mob_revive(struct mob_data *md, unsigned int hp) int64 tick = timer->gettick(); md->state.skillstate = MSS_IDLE; md->last_thinktime = tick; - md->next_walktime = tick+rnd()%50+5000; + md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME; md->last_linktime = tick; md->last_pcneartime = 0; memset(md->dmglog, 0, sizeof(md->dmglog)); // Reset the damage done on the rebirthed monster, otherwise will grant full exp + damage done. [Valaris] @@ -3711,6 +3720,10 @@ bool mob_parse_dbrow(char** str) { if (mstatus->dex < 1) mstatus->dex = 1; if (mstatus->luk < 1) mstatus->luk = 1; + //Tests showed that chase range is effectively 2 cells larger than expected [Playtester] + if (db->range3 > 0) + db->range3 += 2; + db->range2 = atoi(str[20]); db->range3 = atoi(str[21]); if (battle_config.view_range_rate != 100) { |