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.c565
1 files changed, 431 insertions, 134 deletions
diff --git a/src/map/unit.c b/src/map/unit.c
index af0c0a948..a5bd282a9 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -95,9 +95,18 @@ 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;
+#ifdef OFFICIAL_WALKPATH
+ if( !path->search_long(NULL, bl->m, bl->x, bl->y, ud->to_x, ud->to_y, CELL_CHKNOPASS) // Check if there is an obstacle between
+ && wpd.path_len > 14 // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett]
+ && (bl->type != BL_NPC) ) // If type is a NPC, please disregard.
+ return 0;
+#endif
+
memcpy(&ud->walkpath,&wpd,sizeof(wpd));
if (ud->target_to && ud->chaserange>1) {
@@ -106,11 +115,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 +146,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 +257,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);
@@ -205,7 +309,7 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
return 0;
} else
- sd->areanpc_id=0;
+ npc->untouch_areanpc(sd, bl->m, x, y);
if( sd->md ) { // mercenary should be warped after being 3 seconds too far from the master [greenbox]
if( !check_distance_bl(&sd->bl, &sd->md->bl, MAX_MER_DISTANCE) ) {
@@ -217,7 +321,6 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
}
} else // reset the tick, he is not far anymore
sd->md->masterteleport_timer = 0;
-
}
if( sd->hd ) {
if( homun_alive(sd->hd) && !check_distance_bl(&sd->bl, &sd->hd->bl, MAX_MER_DISTANCE) ) {
@@ -231,6 +334,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
@@ -238,12 +343,15 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
if (md->min_chase > md->db->range3) md->min_chase--;
//Walk skills are triggered regardless of target due to the idle-walk mob state.
//But avoid triggering on stop-walk calls.
- if(tid != INVALID_TIMER &&
- !(ud->walk_count%WALK_SKILL_INTERVAL) &&
- mob->skill_use(md, tick, -1))
- {
- if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER))
- { //Skill used, abort walking
+ 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)
+ && 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,8 +383,29 @@ 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(ud->state.change_walk_target)
- return unit->walktoxy_sub(bl);
+ //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) {
+ if(unit->walktoxy_sub(bl)) {
+ return 1;
+ } else {
+ clif->fixpos(bl);
+ return 0;
+ }
+ }
ud->walkpath.path_pos++;
if(ud->walkpath.path_pos>=ud->walkpath.path_len)
@@ -294,7 +423,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)) {
@@ -308,21 +437,31 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
}
if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange)) {
//Reached destination.
- if (ud->state.attack_continue)
- { //Aegis uses one before every attack, we should
+ if (ud->state.attack_continue) {
+ //Aegis uses one before every attack, we should
//only need this one for syncing purposes. [Skotlex]
ud->target_to = 0;
clif->fixpos(bl);
unit->attack(bl, tbl->id, ud->state.attack_continue);
}
} else { //Update chase-path
- unit->walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue?2:0));
+ unit->walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue? 1 : 0));
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(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 +479,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 +492,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 +520,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 +537,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);
}
@@ -430,8 +568,8 @@ int unit_walktobl_sub(int tid, int64 tick, int id, intptr_t data) {
// if flag&2, start attacking upon arrival within range, otherwise just walk to that character.
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;
+ struct unit_data *ud = NULL;
+ struct status_change *sc = NULL;
nullpo_ret(bl);
nullpo_ret(tbl);
@@ -447,13 +585,17 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
ud->to_y = bl->y;
ud->target_to = 0;
return 0;
+ } else if (range == 0) {
+ //Should walk on the same cell as target (for looters)
+ ud->to_x = tbl->x;
+ ud->to_y = tbl->y;
}
ud->state.walk_easy = flag&1;
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
@@ -465,8 +607,8 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
return 1;
}
- if(DIFF_TICK(ud->canmove_tick, timer->gettick()) > 0)
- { //Can't move, wait a bit before invoking the movement.
+ if (DIFF_TICK(ud->canmove_tick, timer->gettick()) > 0) {
+ //Can't move, wait a bit before invoking the movement.
timer->add(ud->canmove_tick+1, unit->walktobl_sub, bl->id, ud->target);
return 1;
}
@@ -474,11 +616,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 +685,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, 0x2) )
break;
to_x += dir_x;
@@ -630,7 +767,7 @@ int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool
if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
return 0;
} else
- sd->areanpc_id=0;
+ npc->untouch_areanpc(sd, bl->m, bl->x, bl->y);
if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 )
{ // Check if pet needs to be teleported. [Skotlex]
@@ -666,7 +803,7 @@ int unit_setdir(struct block_list *bl,unsigned char dir)
uint8 unit_getdir(struct block_list *bl) {
struct unit_data *ud;
nullpo_ret(bl);
-
+
if( bl->type == BL_NPC )
return ((TBL_NPC*)bl)->dir;
ud = unit->bl2ud(bl);
@@ -700,6 +837,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;
}
@@ -729,7 +867,7 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag)
if(map->getcell(bl->m, bl->x, bl->y, CELL_CHKNPC)) {
npc->touch_areanpc(sd, bl->m, bl->x, bl->y);
} else {
- sd->areanpc_id = 0;
+ npc->untouch_areanpc(sd, bl->m, bl->x, bl->y);;
}
}
}
@@ -930,7 +1068,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 +1077,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)
@@ -956,7 +1093,6 @@ int unit_can_move(struct block_list *bl) {
)
)
return 0;
-
if (sc->opt1 > 0 && sc->opt1 != OPT1_STONEWAIT && sc->opt1 != OPT1_BURNING && !(sc->opt1 == OPT1_CRYSTALIZE && bl->type == BL_MOB))
return 0;
@@ -965,6 +1101,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,25 +1147,26 @@ 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) {
+ //Bosses can ignore skill induced walkdelay (but not damage induced)
+ if(bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS))
+ return 0;
+ //Make sure walk delay is not decreased
if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
return 0;
} else {
//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)
- { //Stop walking, if chasing, readjust timers.
- if (delay == 1)
- { //Minimal delay (walk-delay) disabled. Just stop walking.
+ if (ud->walktimer != INVALID_TIMER) {
+ //Stop walking, if chasing, readjust timers.
+ if (delay == 1) {
+ //Minimal delay (walk-delay) disabled. Just stop walking.
unit->stop_walking(bl,4);
} else {
//Resume running after can move again [Kevin]
@@ -1028,7 +1176,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);
}
@@ -1086,7 +1234,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
if(skill->not_ok(skill_id, sd)) // [MouseJstr]
return 0;
- switch(skill_id) { //Check for skills that auto-select target
+ switch (skill_id) {
+ //Check for skills that auto-select target
case MO_CHAINCOMBO:
if (sc && sc->data[SC_BLADESTOP]) {
if ((target=map->id2bl(sc->data[SC_BLADESTOP]->val4)) == NULL)
@@ -1103,6 +1252,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;
@@ -1138,33 +1298,48 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
if(!status->check_skilluse(src, target, skill_id, 0))
return 0;
+ if( src != target && status->isdead(target) ) {
+ /**
+ * Skills that may be cast on dead targets
+ **/
+ switch( skill_id ) {
+ case NPC_WIDESOULDRAIN:
+ case PR_REDEMPTIO:
+ case ALL_RESURRECTION:
+ case WM_DEADHILLHERE:
+ break;
+ default:
+ return 1;
+ }
+ }
+
tstatus = status->get_status_data(target);
// Record the status of the previous skill)
- if(sd) {
+ if (sd) {
- if( (skill->get_inf2(skill_id)&INF2_ENSEMBLE_SKILL) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 1 ) {
- clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ if ((skill->get_inf2(skill_id)&INF2_ENSEMBLE_SKILL) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 1) {
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
return 0;
}
-
- switch(skill_id){
+
+ switch (skill_id){
case SA_CASTCANCEL:
- if(ud->skill_id != skill_id){
+ if (ud->skill_id != skill_id){
sd->skill_id_old = ud->skill_id;
sd->skill_lv_old = ud->skill_lv;
}
break;
case BD_ENCORE:
//Prevent using the dance skill if you no longer have the skill in your tree.
- if(!sd->skill_id_dance || pc->checkskill(sd,sd->skill_id_dance)<=0){
- clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ if (!sd->skill_id_dance || pc->checkskill(sd, sd->skill_id_dance) <= 0){
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
return 0;
}
sd->skill_id_old = skill_id;
break;
case WL_WHITEIMPRISON:
- if( battle->check_target(src,target,BCT_SELF|BCT_ENEMY) < 0 ) {
- clif->skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
+ if (battle->check_target(src, target, BCT_SELF | BCT_ENEMY) < 0) {
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0);
return 0;
}
break;
@@ -1175,13 +1350,20 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
sd->skill_lv_old = skill_lv;
break;
}
- /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */
+ }
+
+ if (sd || src->type == BL_HOM){
+ if (!sd && (target = battle->get_master(src)))
+ sd = map->id2sd(target->id);
+ if (sd){
+ /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */
#if 0
- if ( sd->skillitem != skill_id && !skill->check_condition_castbegin(sd, skill_id, skill_lv) )
+ if (sd->skillitem != skill_id && !skill->check_condition_castbegin(sd, skill_id, skill_lv))
#else
- if ( !skill->check_condition_castbegin(sd, skill_id, skill_lv) )
+ if (!skill->check_condition_castbegin(sd, skill_id, skill_lv))
#endif
- return 0;
+ return 0;
+ }
}
if( src->type == BL_MOB )
@@ -1198,6 +1380,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) ) {
@@ -1241,6 +1435,25 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
casttime = -1;
temp = 1;
break;
+ case CR_DEVOTION:
+ if (sd) {
+ int i = 0, count = min(skill_lv, 5);
+ ARR_FIND(0, count, i, sd->devotion[i] == target_id);
+ if (i == count) {
+ ARR_FIND(0, count, i, sd->devotion[i] == 0);
+ if(i == count) {
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0; // Can't cast on other characters when limit is reached
+ }
+ }
+ }
+ 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] &&
@@ -1277,7 +1490,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
case RA_WUGDASH:
if (sc && sc->data[SC_WUGDASH])
casttime = -1;
- break;
+ break;
case EL_WIND_SLASH:
case EL_HURRICANE:
case EL_TYPOON_MIS:
@@ -1320,7 +1533,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
/**
* why the if else chain: these 3 status do not stack, so its efficient that way.
**/
- if( sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_id != AS_CLOAKING ) {
+ if( sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_id != AS_CLOAKING ) {
status_change_end(src, SC_CLOAKING, INVALID_TIMER);
if (!src->prev) return 0; //Warped away!
} else if( sc->data[SC_CLOAKINGEXCEED] && !(sc->data[SC_CLOAKINGEXCEED]->val4&4) && skill_id != GC_CLOAKINGEXCEED ) {
@@ -1328,10 +1541,10 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
if (!src->prev) return 0;
}
}
-
+
if(!ud->state.running) //need TK_RUN or WUGDASH handler to be done before that, see bugreport:6026
unit->stop_walking(src,1);// even though this is not how official works but this will do the trick. bugreport:6829
-
+
// in official this is triggered even if no cast time.
clif->skillcasting(src, src->id, target_id, 0,0, skill_id, skill->get_ele(skill_id, skill_lv), casttime);
if( casttime > 0 || temp )
@@ -1434,13 +1647,11 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
if( skill->not_ok(skill_id, sd) || !skill->check_condition_castbegin(sd, skill_id, skill_lv) )
return 0;
/**
- * "WHY IS IT HEREE": pneuma cannot be canceled past this point, the client displays the animation even,
- * if we cancel it from nodamage_id, so it has to be here for it to not display the animation.
+ * "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 == AL_PNEUMA && map->getcell(src->m, skill_x, skill_y, CELL_CHKLANDPROTECTOR) ) {
- clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ 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))
@@ -1452,7 +1663,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
return 0;
}
- /* Check range and obstacle */
+ /* Check range and obstacle */
bl.type = BL_NUL;
bl.m = src->m;
bl.x = skill_x;
@@ -1463,10 +1674,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);
@@ -1486,14 +1711,14 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
ud->state.skillcastcancel = castcancel&&casttime>0?1:0;
if( !sd || sd->skillitem != skill_id || skill->get_cast(skill_id,skill_lv) )
ud->canact_tick = tick + casttime + 100;
-// if( sd )
-// {
-// switch( skill_id )
-// {
-// case ????:
-// sd->canequip_tick = tick + casttime;
-// }
-// }
+#if 0
+ if (sd) {
+ switch (skill_id) {
+ case ????:
+ sd->canequip_tick = tick + casttime;
+ }
+ }
+#endif // 0
ud->skill_id = skill_id;
ud->skill_lv = skill_lv;
ud->skillx = skill_x;
@@ -1548,18 +1773,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);
+
+ if(ud->attacktimer == INVALID_TIMER)
+ return;
- timer->delete( ud->attacktimer, unit->attack_timer );
+ //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.
@@ -1568,6 +1826,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);
}
@@ -1585,6 +1844,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));
@@ -1613,19 +1873,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]
@@ -1710,7 +1981,7 @@ bool unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range,
/*==========================================
* Calculates position of Pet/Mercenary/Homunculus/Elemental
*------------------------------------------*/
-int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir)
+int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir)
{
int dx, dy, x, y, i, k;
struct unit_data *ud = unit->bl2ud(bl);
@@ -1827,15 +2098,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) ) {
@@ -1857,10 +2132,17 @@ 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;
- if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
- { // Link monsters nearby [Skotlex]
+ //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;
map->foreachinrange(mob->linksearch, src, md->db->range2, BL_MOB, md->class_, target, tick);
}
@@ -1882,7 +2164,7 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
return 1;
ud->attackabletime = tick + sstatus->adelay;
-// You can't move if you can't attack neither.
+ // You can't move if you can't attack neither.
if (src->type&battle_config.attack_walk_delay)
unit->set_walkdelay(src, tick, sstatus->amotion, 1);
}
@@ -1927,8 +2209,8 @@ int unit_skillcastcancel(struct block_list *bl,int type)
if (!ud->state.skillcastcancel)
return 0;
- if (sd && (sd->special_state.no_castcancel2 ||
- ( sd->special_state.no_castcancel && !map_flag_gvg(bl->m) && !map->list[bl->m].flag.battleground))) //fixed flags being read the wrong way around [blackhole89]
+ if (sd && (sd->special_state.no_castcancel2
+ || (sd->special_state.no_castcancel && !map_flag_gvg(bl->m) && !map->list[bl->m].flag.battleground))) //fixed flags being read the wrong way around [blackhole89]
return 0;
}
@@ -1975,6 +2257,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();
@@ -2040,15 +2323,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.
@@ -2172,7 +2459,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;
}
@@ -2267,10 +2554,10 @@ int unit_remove_map(struct block_list *bl, clr_type clrtype, const char* file, i
void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype)
{
unit->remove_map(&sd->bl,clrtype,ALC_MARK);
-
+
//CLR_RESPAWN is the warp from logging out, CLR_TELEPORT is the warp from teleporting, but pets/homunc need to just 'vanish' instead of showing the warping animation.
if (clrtype == CLR_RESPAWN || clrtype == CLR_TELEPORT) clrtype = CLR_OUTSIGHT;
-
+
if(sd->pd)
unit->remove_map(&sd->pd->bl, clrtype, ALC_MARK);
if(homun_alive(sd->hd))
@@ -2299,7 +2586,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
nullpo_ret(ud);
map->freeblock_lock();
- if( bl->prev ) //Players are supposed to logout with a "warp" effect.
+ if( bl->prev ) //Players are supposed to logout with a "warp" effect.
unit->remove_map(bl, clrtype, ALC_MARK);
switch( bl->type ) {
@@ -2310,7 +2597,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
unsigned int k;
sd->state.loggingout = 1;
-
+
if( status->isdead(bl) )
pc->setrestartvalue(sd,2);
@@ -2332,8 +2619,8 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
pc->cleareventtimer(sd);
pc->inventory_rental_clear(sd);
pc->delspiritball(sd,sd->spiritball,1);
- for(i = 1; i < 5; i++)
- pc->del_charm(sd, sd->charm[i], i);
+ for(i = SPIRITS_TYPE_CHARM_WATER; i < SPIRITS_TYPE_SPHERE; i++)
+ pc->del_charm(sd, sd->spiritcharm[i], i);
if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting
script->free_state(sd->st);
@@ -2369,7 +2656,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
sd->quest_log = NULL;
sd->num_quests = sd->avail_quests = 0;
}
-
+
for( k = 0; k < sd->hdatac; k++ ) {
if( sd->hdata[k]->flag.free ) {
aFree(sd->hdata[k]->data);
@@ -2393,10 +2680,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
if( pd->s_skill )
{
if (pd->s_skill->timer != INVALID_TIMER) {
- if (pd->s_skill->id)
- timer->delete(pd->s_skill->timer, pet->skill_support_timer);
- else
- timer->delete(pd->s_skill->timer, pet->heal_timer);
+ timer->delete(pd->s_skill->timer, pet->skill_support_timer);
}
aFree(pd->s_skill);
pd->s_skill = NULL;
@@ -2423,10 +2707,10 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
aFree (pd->loot);
pd->loot = NULL;
}
- if( pd->pet.intimate > 0 )
+ if (pd->pet.intimate > 0) {
intif->save_petdata(pd->pet.account_id,&pd->pet);
- else
- { //Remove pet.
+ } else {
+ //Remove pet.
intif->delete_petdata(pd->pet.pet_id);
if (sd) sd->status.pet_id = 0;
}
@@ -2436,6 +2720,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
}
case BL_MOB:
{
+ unsigned int k;
struct mob_data *md = (struct mob_data*)bl;
if( md->spawn_timer != INVALID_TIMER )
{
@@ -2490,6 +2775,15 @@ int unit_free(struct block_list *bl, clr_type clrtype) {
mob->clone_delete(md);
if( md->tomb_nid )
mob->mvptomb_destroy(md);
+
+ for (k = 0; k < md->hdatac; k++) {
+ if( md->hdata[k]->flag.free ) {
+ aFree(md->hdata[k]->data);
+ }
+ aFree(md->hdata[k]);
+ }
+ if (md->hdata)
+ aFree(md->hdata);
break;
}
case BL_HOM:
@@ -2561,6 +2855,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;
}
@@ -2571,7 +2866,7 @@ int do_final_unit(void) {
void unit_defaults(void) {
unit = &unit_s;
-
+
unit->init = do_init_unit;
unit->final = do_final_unit;
/* */
@@ -2594,6 +2889,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;