summaryrefslogtreecommitdiff
path: root/src/map/unit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/unit.c')
-rw-r--r--src/map/unit.c354
1 files changed, 298 insertions, 56 deletions
diff --git a/src/map/unit.c b/src/map/unit.c
index 1f81e0d2f..044d7a43c 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -95,6 +95,8 @@ int unit_walktoxy_sub(struct block_list *bl)
ud = unit->bl2ud(bl);
if(ud == NULL) return 0;
+ memset(&wpd, 0, sizeof(wpd));
+
if( !path->search(&wpd,bl->m,bl->x,bl->y,ud->to_x,ud->to_y,ud->state.walk_easy,CELL_CHKNOPASS) )
return 0;
@@ -106,11 +108,11 @@ int unit_walktoxy_sub(struct block_list *bl)
uint8 dir;
//Trim the last part of the path to account for range,
//but always move at least one cell when requested to move.
- for (i = ud->chaserange*10; i > 0 && ud->walkpath.path_len>1;) {
+ for (i = (ud->chaserange*10)-10; i > 0 && ud->walkpath.path_len>1;) {
ud->walkpath.path_len--;
dir = ud->walkpath.path[ud->walkpath.path_len];
if(dir&1)
- i -= MOVE_DIAGONAL_COST;
+ i -= MOVE_COST*20; //When chasing, units will target a diamond-shaped area in range [Playtester]
else
i -= MOVE_COST;
ud->to_x -= dirx[dir];
@@ -137,9 +139,79 @@ int unit_walktoxy_sub(struct block_list *bl)
return 1;
}
+/**
+ * Triggered on full step if stepaction is true and executes remembered action.
+ * @param tid: Timer ID
+ * @param tick: Unused
+ * @param id: ID of bl to do the action
+ * @param data: Not used
+ * @return 1: Success 0: Fail (No valid bl)
+ */
+int unit_step_timer(int tid, int64 tick, int id, intptr_t data)
+{
+ struct block_list *bl;
+ struct unit_data *ud;
+ int target_id;
+
+ bl = map->id2bl(id);
+
+ if (!bl || bl->prev == NULL)
+ return 0;
+
+ ud = unit_bl2ud(bl);
+
+ if(!ud)
+ return 0;
+
+ if(ud->steptimer != tid) {
+ ShowError("unit_step_timer mismatch %d != %d\n",ud->steptimer,tid);
+ return 0;
+ }
+
+ ud->steptimer = INVALID_TIMER;
+
+ if(!ud->stepaction)
+ return 0;
+
+ //Set to false here because if an error occurs, it should not be executed again
+ ud->stepaction = false;
+
+ if(!ud->target_to)
+ return 0;
+
+ //Flush target_to as it might contain map coordinates which should not be used by other functions
+ target_id = ud->target_to;
+ ud->target_to = 0;
+
+ //If stepaction is set then we remembered a client request that should be executed on the next step
+ //Execute request now if target is in attack range
+ if(ud->stepskill_id && skill->get_inf(ud->stepskill_id) & INF_GROUND_SKILL) {
+ //Execute ground skill
+ struct map_data *md = &map->list[bl->m];
+ unit->skilluse_pos(bl, target_id%md->xs, target_id/md->xs, ud->stepskill_id, ud->stepskill_lv);
+ } else {
+ //If a player has target_id set and target is in range, attempt attack
+ struct block_list *tbl = map->id2bl(target_id);
+ if (!tbl || !status->check_visibility(bl, tbl)) {
+ return 0;
+ }
+ if(ud->stepskill_id == 0) {
+ //Execute normal attack
+ unit->attack(bl, tbl->id, (ud->state.attack_continue) + 2);
+ } else {
+ //Execute non-ground skill
+ unit->skilluse_id(bl, tbl->id, ud->stepskill_id, ud->stepskill_lv);
+ }
+ }
+
+ return 1;
+}
+
+
int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
int i;
int x,y,dx,dy;
+ unsigned char icewall_walk_block;
uint8 dir;
struct block_list *bl;
struct map_session_data *sd;
@@ -178,9 +250,34 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
dx = dirx[(int)dir];
dy = diry[(int)dir];
- if(map->getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS))
+ //Get icewall walk block depending on boss mode (players can't be trapped)
+ if(md && md->status.mode&MD_BOSS)
+ icewall_walk_block = battle_config.boss_icewall_walk_block;
+ else if(md)
+ icewall_walk_block = battle_config.mob_icewall_walk_block;
+ else
+ icewall_walk_block = 0;
+
+ //Monsters will walk into an icewall from the west and south if they already started walking
+ if(map->getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS)
+ && (icewall_walk_block == 0 || !map->getcell(bl->m,x+dx,y+dy,CELL_CHKICEWALL) || dx < 0 || dy < 0))
return unit->walktoxy_sub(bl);
+ //Monsters can only leave icewalls to the west and south
+ //But if movement fails more than icewall_walk_block times, they can ignore this rule
+ if(md && md->walktoxy_fail_count < icewall_walk_block && map->getcell(bl->m,x,y,CELL_CHKICEWALL) && (dx > 0 || dy > 0)) {
+ //Needs to be done here so that rudeattack skills are invoked
+ md->walktoxy_fail_count++;
+ clif->fixpos(bl);
+ //Monsters in this situation first use a chase skill, then unlock target and then use an idle skill
+ if (!(++ud->walk_count%WALK_SKILL_INTERVAL))
+ mob->skill_use(md, tick, -1);
+ mob->unlocktarget(md, tick);
+ if (!(++ud->walk_count%WALK_SKILL_INTERVAL))
+ mob->skill_use(md, tick, -1);
+ return 0;
+ }
+
//Refresh view for all those we lose sight
map->foreachinmovearea(clif->outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
@@ -231,6 +328,8 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
sd->hd->masterteleport_timer = 0;
}
} else if (md) {
+ //Movement was successful, reset walktoxy_fail_count
+ md->walktoxy_fail_count = 0;
if( map->getcell(bl->m,x,y,CELL_CHKNPC) ) {
if( npc->touch_areanpc2(md) ) return 0; // Warped
} else
@@ -240,9 +339,11 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//But avoid triggering on stop-walk calls.
if(tid != INVALID_TIMER &&
!(ud->walk_count%WALK_SKILL_INTERVAL) &&
+ map->list[bl->m].users > 0 &&
mob->skill_use(md, tick, -1))
{
- if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER))
+ if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER)
+ && md->state.skillstate != MSS_WALK) //Walk skills are supposed to be used while walking
{ //Skill used, abort walking
clif->fixpos(bl); //Fix position as walk has been canceled.
return 0;
@@ -275,6 +376,21 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
if(tid == INVALID_TIMER) //A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant.
return 0;
+ //If stepaction is set then we remembered a client request that should be executed on the next step
+ if (ud->stepaction && ud->target_to) {
+ //Delete old stepaction even if not executed yet, the latest command is what counts
+ if(ud->steptimer != INVALID_TIMER) {
+ timer->delete(ud->steptimer, unit->step_timer);
+ ud->steptimer = INVALID_TIMER;
+ }
+ //Delay stepactions by half a step (so they are executed at full step)
+ if(ud->walkpath.path[ud->walkpath.path_pos]&1)
+ i = status->get_speed(bl)*14/20;
+ else
+ i = status->get_speed(bl)/2;
+ ud->steptimer = timer->add(tick+i, unit->step_timer, bl->id, 0);
+ }
+
if(ud->state.change_walk_target)
return unit->walktoxy_sub(bl);
@@ -294,7 +410,7 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//Keep trying to run.
if ( !(unit->run(bl, NULL, SC_RUN) || unit->run(bl, sd, SC_WUGDASH)) )
ud->state.running = 0;
- } else if (ud->target_to) {
+ } else if (!ud->stepaction && ud->target_to) {
//Update target trajectory.
struct block_list *tbl = map->id2bl(ud->target_to);
if (!tbl || !status->check_visibility(bl, tbl)) {
@@ -323,6 +439,16 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//Stopped walking. Update to_x and to_y to current location [Skotlex]
ud->to_x = bl->x;
ud->to_y = bl->y;
+
+ if(map->count_oncell(bl->m, x, y, BL_CHAR|BL_NPC, 1) > battle_config.official_cell_stack_limit) {
+ //Walked on occupied cell, call unit_walktoxy again
+ if(ud->steptimer != INVALID_TIMER) {
+ //Execute step timer on next step instead
+ timer->delete(ud->steptimer, unit_step_timer);
+ ud->steptimer = INVALID_TIMER;
+ }
+ return unit->walktoxy(bl, x, y, 8);
+ }
}
return 0;
}
@@ -340,6 +466,7 @@ int unit_delay_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//&1 -> 1/0 = easy/hard
//&2 -> force walking
//&4 -> Delay walking if the reason you can't walk is the canwalk delay
+//&8 -> Search for an unoccupied cell and cancel if none available
int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
{
struct unit_data* ud = NULL;
@@ -352,6 +479,9 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
if( ud == NULL) return 0;
+ if ((flag&8) && !map->closest_freecell(bl->m, &x, &y, BL_CHAR|BL_NPC, 1)) //This might change x and y
+ return 0;
+
if (!path->search(&wpd, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS)) // Count walk path cells
return 0;
@@ -377,7 +507,7 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
ud->state.walk_easy = flag&1;
ud->to_x = x;
ud->to_y = y;
- unit->set_target(ud, 0);
+ unit->stop_attack(bl); //Sets target to 0
sc = status->get_sc(bl);
if( sc ) {
@@ -394,11 +524,6 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
return 1;
}
- if(ud->attacktimer != INVALID_TIMER) {
- timer->delete( ud->attacktimer, unit->attack_timer );
- ud->attacktimer = INVALID_TIMER;
- }
-
return unit->walktoxy_sub(bl);
}
@@ -453,7 +578,7 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
ud->target_to = tbl->id;
ud->chaserange = range; //Note that if flag&2, this SHOULD be attack-range
ud->state.attack_continue = flag&2?1:0; //Chase to attack.
- unit->set_target(ud, 0);
+ unit->stop_attack(bl); //Sets target to 0
sc = status->get_sc(bl);
if (sc && (sc->data[SC_CONFUSION] || sc->data[SC__CHAOS])) //Randomize the target position
@@ -474,11 +599,6 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
if(!unit->can_move(bl))
return 0;
- if(ud->attacktimer != INVALID_TIMER) {
- timer->delete( ud->attacktimer, unit->attack_timer );
- ud->attacktimer = INVALID_TIMER;
- }
-
if (unit->walktoxy_sub(bl)) {
set_mobstate(bl, flag&2);
return 1;
@@ -548,7 +668,7 @@ bool unit_run( struct block_list *bl, struct map_session_data *sd, enum sc_type
break;
//if sprinting and there's a PC/Mob/NPC, block the path [Kevin]
- if( map->count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC) )
+ if( map->count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC, 0) )
break;
to_x += dir_x;
@@ -700,6 +820,7 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag)
}
if( sd ) {
+ unit->stop_stepaction(bl); //Stop stepaction when knocked back
sd->ud.to_x = nx;
sd->ud.to_y = ny;
}
@@ -930,7 +1051,7 @@ int unit_can_move(struct block_list *bl) {
|| sc->data[SC_ELECTRICSHOCKER]
|| sc->data[SC_WUGBITE]
|| sc->data[SC_THORNS_TRAP]
- || sc->data[SC_MAGNETICFIELD]
+ || ( sc->data[SC_MAGNETICFIELD] && !sc->data[SC_HOVERING] )
|| sc->data[SC__MANHOLE]
|| sc->data[SC_CURSEDCIRCLE_ATKER]
|| sc->data[SC_CURSEDCIRCLE_TARGET]
@@ -939,7 +1060,6 @@ int unit_can_move(struct block_list *bl) {
|| (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3 && !(sc->data[SC_CAMOUFLAGE]->val3&1))
|| sc->data[SC_MEIKYOUSISUI]
|| sc->data[SC_KG_KAGEHUMI]
- || sc->data[SC_KYOUGAKU]
|| sc->data[SC_NEEDLE_OF_PARALYZE]
|| sc->data[SC_VACUUM_EXTREME]
|| (sc->data[SC_FEAR] && sc->data[SC_FEAR]->val2 > 0)
@@ -965,6 +1085,17 @@ int unit_can_move(struct block_list *bl) {
return 0;
}
+
+ // Icewall walk block special trapped monster mode
+ if(bl->type == BL_MOB) {
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+ if(md && ((md->status.mode&MD_BOSS && battle_config.boss_icewall_walk_block == 1 && map->getcell(bl->m,bl->x,bl->y,CELL_CHKICEWALL))
+ || (!(md->status.mode&MD_BOSS) && battle_config.mob_icewall_walk_block == 1 && map->getcell(bl->m,bl->x,bl->y,CELL_CHKICEWALL)))) {
+ md->walktoxy_fail_count = 1; //Make sure rudeattacked skills are invoked
+ return 0;
+ }
+ }
+
return 1;
}
@@ -1000,12 +1131,6 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
struct unit_data *ud = unit->bl2ud(bl);
if (delay <= 0 || !ud) return 0;
- /**
- * MvP mobs have no walk delay
- **/
- if( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) )
- return 0;
-
if (type) {
if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
return 0;
@@ -1013,6 +1138,9 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
//Don't set walk delays when already trapped.
if (!unit->can_move(bl))
return 0;
+ //Immune to being stopped for double the flinch time
+ if (DIFF_TICK(ud->canmove_tick, tick-delay) > 0)
+ return 0;
}
ud->canmove_tick = tick + delay;
if (ud->walktimer != INVALID_TIMER)
@@ -1028,7 +1156,7 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
}
else
{
- unit->stop_walking(bl,2|4);
+ unit->stop_walking(bl,4);
if(ud->target)
timer->add(ud->canmove_tick+1, unit->walktobl_sub, bl->id, ud->target);
}
@@ -1103,6 +1231,17 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
return 0;
}
break;
+ case GC_WEAPONCRUSH:
+ if( sc && sc->data[SC_COMBOATTACK] && sc->data[SC_COMBOATTACK]->val1 == GC_WEAPONBLOCKING ) {
+ if( (target=map->id2bl(sc->data[SC_COMBOATTACK]->val2)) == NULL ) {
+ clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ return 0;
+ }
+ } else {
+ clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ return 0;
+ }
+ break;
}
if (target)
target_id = target->id;
@@ -1213,6 +1352,18 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
else
range = skill->get_range2(src, skill_id, skill_lv); // Skill cast distance from database
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the skill request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, target, range-1)) {
+ ud->stepaction = true;
+ ud->target_to = target_id;
+ ud->stepskill_id = skill_id;
+ ud->stepskill_lv = skill_lv;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
+
//Check range when not using skill on yourself or is a combo-skill during attack
//(these are supposed to always have the same range as your attack)
if( src->id != target_id && (!temp || ud->attacktimer == INVALID_TIMER) ) {
@@ -1269,6 +1420,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
}
}
break;
+ case AB_CLEARANCE:
+ if( target->type != BL_MOB && battle->check_target(src,target,BCT_PARTY) <= 0 && sd ) {
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0);
+ return 0;
+ }
+ break;
case SR_GATEOFHELL:
case SR_TIGERCANNON:
if (sc && sc->data[SC_COMBOATTACK] &&
@@ -1469,6 +1626,12 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 0;
}
+ /**
+ * "WHY IS IT HEREE": ice wall cannot be canceled past this point, the client displays the animation even,
+ * if we cancel it from castend_pos, so it has to be here for it to not display the animation.
+ **/
+ if ( skill_id == WZ_ICEWALL && map->getcell(src->m, skill_x, skill_y, CELL_CHKNOICEWALL) )
+ return 0;
}
if (!status->check_skilluse(src, NULL, skill_id, 0))
@@ -1491,10 +1654,24 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
else
range = skill->get_range2(src, skill_id, skill_lv); // Skill cast distance from database
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the skill request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, &bl, range-1)) {
+ struct map_data *md = &map->list[src->m];
+ // Convert coordinates to target_to so we can use it as target later
+ ud->stepaction = true;
+ ud->target_to = (skill_x + skill_y*md->xs);
+ ud->stepskill_id = skill_id;
+ ud->stepskill_lv = skill_lv;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
+
if( skill->get_state(ud->skill_id) == ST_MOVE_ENABLE ) {
if( !unit->can_reach_bl(src, &bl, range + 1, 1, NULL, NULL) )
return 0; //Walk-path check failed.
- } else if( !battle->check_range(src, &bl, range + 1) )
+ } else if( !battle->check_range(src, &bl, range) )
return 0; //Arrow-path check failed.
unit->stop_attack(src);
@@ -1576,18 +1753,51 @@ int unit_set_target(struct unit_data* ud, int target_id)
return 0;
}
-int unit_stop_attack(struct block_list *bl)
+/**
+ * Stop a unit's attacks
+ * @param bl: Object to stop
+ */
+void unit_stop_attack(struct block_list *bl)
{
- struct unit_data *ud = unit->bl2ud(bl);
- nullpo_ret(bl);
+ struct unit_data *ud;
+ nullpo_retv(bl);
+ ud = unit_bl2ud(bl);
+ nullpo_retv(ud);
- if(!ud || ud->attacktimer == INVALID_TIMER)
- return 0;
+ //Clear target
+ unit_set_target(ud, 0);
- timer->delete( ud->attacktimer, unit->attack_timer );
+ if(ud->attacktimer == INVALID_TIMER)
+ return;
+
+ //Clear timer
+ timer->delete(ud->attacktimer, unit->attack_timer);
ud->attacktimer = INVALID_TIMER;
- unit->set_target(ud, 0);
- return 0;
+}
+
+/**
+ * Stop a unit's step action
+ * @param bl: Object to stop
+ */
+void unit_stop_stepaction(struct block_list *bl)
+{
+ struct unit_data *ud;
+ nullpo_retv(bl);
+ ud = unit_bl2ud(bl);
+ nullpo_retv(ud);
+
+ //Clear remembered step action
+ ud->stepaction = false;
+ ud->target_to = 0;
+ ud->stepskill_id = 0;
+ ud->stepskill_lv = 0;
+
+ if(ud->steptimer == INVALID_TIMER)
+ return;
+
+ //Clear timer
+ timer->delete(ud->steptimer, unit->step_timer);
+ ud->steptimer = INVALID_TIMER;
}
//Means current target is unattackable. For now only unlocks mobs.
@@ -1596,6 +1806,7 @@ int unit_unattackable(struct block_list *bl)
struct unit_data *ud = unit->bl2ud(bl);
if (ud) {
ud->state.attack_continue = 0;
+ ud->state.step_attack = 0;
unit->set_target(ud, 0);
}
@@ -1613,6 +1824,7 @@ int unit_unattackable(struct block_list *bl)
int unit_attack(struct block_list *src,int target_id,int continuous) {
struct block_list *target;
struct unit_data *ud;
+ int range;
nullpo_ret(ud = unit->bl2ud(src));
@@ -1641,19 +1853,30 @@ int unit_attack(struct block_list *src,int target_id,int continuous) {
unit->unattackable(src);
return 1;
}
- ud->state.attack_continue = continuous;
+ ud->state.attack_continue = (continuous&1)?1:0;
+ ud->state.step_attack = (continuous&2)?1:0;
unit->set_target(ud, target_id);
+ range = status_get_range(src);
+
if (continuous) //If you're to attack continuously, set to auto-case character
- ud->chaserange = status_get_range(src);
+ ud->chaserange = range;
//Just change target/type. [Skotlex]
if(ud->attacktimer != INVALID_TIMER)
return 0;
- //Set Mob's ANGRY/BERSERK states.
- if(src->type == BL_MOB)
- ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the attack request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, target, range-1)) {
+ ud->stepaction = true;
+ ud->target_to = ud->target;
+ ud->stepskill_id = 0;
+ ud->stepskill_lv = 0;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
if(DIFF_TICK(ud->attackabletime, timer->gettick()) > 0)
//Do attack next time it is possible. [Skotlex]
@@ -1855,15 +2078,19 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
}
sstatus = status->get_status_data(src);
- range = sstatus->rhw.range + 1;
+ range = sstatus->rhw.range;
- if( unit->is_walking(target) )
- range++; //Extra range when chasing
- if( !check_distance_bl(src,target,range) ) { //Chase if required.
- if(sd)
- clif->movetoattack(sd,target);
- else if(ud->state.attack_continue)
- unit->walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
+ if( (unit_is_walking(target) || ud->state.step_attack)
+ && (target->type == BL_PC || !map->getcell(target->m,target->x,target->y,CELL_CHKICEWALL)) )
+ range++; // Extra range when chasing (does not apply to mobs locked in an icewall)
+
+ if(sd && !check_distance_client_bl(src,target,range)) {
+ // Player tries to attack but target is too far, notify client
+ clif->movetoattack(sd,target);
+ return 1;
+ } else if(md && !check_distance_bl(src,target,range)) {
+ // Monster: Chase if required
+ unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
return 1;
}
if( !battle->check_range(src,target,range) ) {
@@ -1885,8 +2112,15 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
if(ud->walktimer != INVALID_TIMER)
unit->stop_walking(src,1);
if(md) {
- if (mob->skill_use(md,tick,-1))
- return 1;
+ //First attack is always a normal attack
+ if(md->state.skillstate == MSS_ANGRY || md->state.skillstate == MSS_BERSERK) {
+ if (mob->skill_use(md,tick,-1))
+ return 1;
+ } else {
+ // Set mob's ANGRY/BERSERK states.
+ md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ }
+
if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
{ // Link monsters nearby [Skotlex]
md->last_linktime = tick;
@@ -2003,6 +2237,7 @@ void unit_dataset(struct block_list *bl) {
ud->walktimer = INVALID_TIMER;
ud->skilltimer = INVALID_TIMER;
ud->attacktimer = INVALID_TIMER;
+ ud->steptimer = INVALID_TIMER;
ud->attackabletime =
ud->canact_tick =
ud->canmove_tick = timer->gettick();
@@ -2068,15 +2303,19 @@ int unit_remove_map(struct block_list *bl, clr_type clrtype, const char* file, i
map->freeblock_lock();
- unit->set_target(ud, 0);
-
if (ud->walktimer != INVALID_TIMER)
unit->stop_walking(bl,0);
- if (ud->attacktimer != INVALID_TIMER)
- unit->stop_attack(bl);
if (ud->skilltimer != INVALID_TIMER)
unit->skillcastcancel(bl,0);
+ //Clear target even if there is no timer
+ if (ud->target || ud->attacktimer != INVALID_TIMER)
+ unit_stop_attack(bl);
+
+ //Clear stepaction even if there is no timer
+ if (ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(bl);
+
// Do not reset can-act delay. [Skotlex]
ud->attackabletime = ud->canmove_tick /*= ud->canact_tick*/ = timer->gettick();
if(sc && sc->count ) { //map-change/warp dispells.
@@ -2200,7 +2439,7 @@ int unit_remove_map(struct block_list *bl, clr_type clrtype, const char* file, i
sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
} else if (--map->list[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
map->removemobs(bl->m);
- if( !(sd->sc.option&OPTION_INVISIBLE) ) {
+ if (!(pc_isinvisible(sd))) {
// decrement the number of active pvp players on the map
--map->list[bl->m].users_pvp;
}
@@ -2589,6 +2828,7 @@ int do_init_unit(bool minimal) {
timer->add_func_list(unit->walktoxy_timer,"unit_walktoxy_timer");
timer->add_func_list(unit->walktobl_sub, "unit_walktobl_sub");
timer->add_func_list(unit->delay_walktoxy_timer,"unit_delay_walktoxy_timer");
+ timer->add_func_list(unit->step_timer,"unit_step_timer");
return 0;
}
@@ -2622,6 +2862,8 @@ void unit_defaults(void) {
unit->warp = unit_warp;
unit->stop_walking = unit_stop_walking;
unit->skilluse_id = unit_skilluse_id;
+ unit->step_timer = unit_step_timer;
+ unit->stop_stepaction = unit_stop_stepaction;
unit->is_walking = unit_is_walking;
unit->can_move = unit_can_move;
unit->resume_running = unit_resume_running;