root/src/map/unit.c @ 19

Revision 19, 56.9 kB (checked in by jinshiro, 17 years ago)

Now Compiles with Cygwin GCC

RevLine 
[1]1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/showmsg.h"
5#include "../common/timer.h"
6#include "../common/nullpo.h"
7#include "../common/db.h"
8#include "../common/malloc.h"
9#include "unit.h"
10#include "map.h"
11#include "path.h"
12#include "pc.h"
13#include "mob.h"
14#include "pet.h"
15#include "mercenary.h"
16#include "skill.h"
17#include "clif.h"
18#include "npc.h"
19#include "guild.h"
20#include "status.h"
21#include "battle.h"
22#include "chat.h"
23#include "trade.h"
24#include "vending.h"
25#include "party.h"
26#include "intif.h"
27#include "chrif.h"
28#include "script.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34
35const short dirx[8]={0,-1,-1,-1,0,1,1,1};
36const short diry[8]={1,1,0,-1,-1,-1,0,1};
37
38struct unit_data* unit_bl2ud(struct block_list *bl)
39{
40        if( bl == NULL) return NULL;
41        if( bl->type == BL_PC)  return &((struct map_session_data*)bl)->ud;
42        if( bl->type == BL_MOB) return &((struct mob_data*)bl)->ud;
43        if( bl->type == BL_PET) return &((struct pet_data*)bl)->ud;
44        if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud;
45        if( bl->type == BL_HOM) return &((struct homun_data*)bl)->ud;   //[orn]
46        return NULL;
47}
48
49static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data);
50static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr data);
51
52int unit_walktoxy_sub(struct block_list *bl)
53{
54        int i;
55        struct walkpath_data wpd;
56        struct unit_data *ud = NULL;
57
58        nullpo_retr(1, bl);
59        ud = unit_bl2ud(bl);
60        if(ud == NULL) return 0;
61
62        if( !path_search(&wpd,bl->m,bl->x,bl->y,ud->to_x,ud->to_y,ud->state.walk_easy,CELL_CHKNOPASS) )
63                return 0;
64
65        memcpy(&ud->walkpath,&wpd,sizeof(wpd));
66       
67        if (ud->target && ud->chaserange>1) {
68                //Generally speaking, the walk path is already to an adjacent tile
69                //so we only need to shorten the path if the range is greater than 1.
70                int dir;
71                //Trim the last part of the path to account for range,
72                //but always move at least one cell when requested to move.
73                for (i = ud->chaserange*10; i > 0 && ud->walkpath.path_len>1;) {
74                   ud->walkpath.path_len--;
75                        dir = ud->walkpath.path[ud->walkpath.path_len];
76                        if(dir&1)
77                                i-=14;
78                        else
79                                i-=10;
80                        ud->to_x -= dirx[dir];
81                        ud->to_y -= diry[dir];
82                }
83        }
84
85        ud->state.change_walk_target=0;
86
87        if (bl->type == BL_PC) {
88                ((TBL_PC *)bl)->head_dir = 0;
89                clif_walkok((TBL_PC*)bl);
90        }
91        clif_move(ud);
92
93        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
94                i = -1;
95        else if(ud->walkpath.path[ud->walkpath.path_pos]&1)
96                i = status_get_speed(bl)*14/10;
97        else
98                i = status_get_speed(bl);
99        if( i > 0)
100                ud->walktimer = add_timer(gettick()+i,unit_walktoxy_timer,bl->id,i);
101        return 1;
102}
103
104static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr data)
105{
106        int i;
107        int x,y,dx,dy;
108        uint8 dir;
109        struct block_list       *bl;
110        struct map_session_data *sd;
111        struct mob_data         *md;
112        struct unit_data        *ud;
113
114        bl = map_id2bl(id);
115        if(bl == NULL)
116                return 0;
117        sd = BL_CAST(BL_PC, bl);
118        md = BL_CAST(BL_MOB, bl);
119        ud = unit_bl2ud(bl);
120       
121        if(ud == NULL) return 0;
122
123        if(ud->walktimer != tid){
124                ShowError("unit_walk_timer mismatch %d != %d\n",ud->walktimer,tid);
125                return 0;
126        }
127        ud->walktimer=-1;
128        if( bl->prev == NULL ) return 0; // block_list ‚©‚甲‚¯‚Ä‚¢‚é‚̂ňړ®’âŽ~‚·‚é
129
130        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
131                return 0;
132
133        if(ud->walkpath.path[ud->walkpath.path_pos]>=8)
134                return 1;
135        x = bl->x;
136        y = bl->y;
137
138        dir = ud->walkpath.path[ud->walkpath.path_pos];
139        ud->dir = dir;
140
141        dx = dirx[(int)dir];
142        dy = diry[(int)dir];
143
144        if(map_getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS))
145                return unit_walktoxy_sub(bl);
146       
147        // ƒoƒVƒŠƒJ”»’è
148
149        map_foreachinmovearea(clif_outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
150
151        x += dx;
152        y += dy;
153        map_moveblock(bl, x, y, tick);
154        ud->walk_count++; //walked cell counter, to be used for walk-triggered skills. [Skotlex]
155
156        if (bl->x != x || bl->y != y || ud->walktimer != -1)
157                return 0; //map_moveblock has altered the object beyond what we expected (moved/warped it)
158
159        ud->walktimer = 1;
160        map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl);
161        ud->walktimer = -1;
162       
163        if(sd) {
164                if(map_getcell(bl->m,x,y,CELL_CHKNPC)) {
165                        npc_touch_areanpc(sd,bl->m,x,y);
166                        if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
167                                return 0;
168                } else
169                        sd->areanpc_id=0;
170                if (sd->state.gmaster_flag &&
171                        (battle_config.guild_aura&(agit_flag?2:1)) &&
172                        (battle_config.guild_aura&(map_flag_gvg2(bl->m)?8:4))
173                )
174                { //Guild Aura: Likely needs to be recoded, this method seems inefficient.
175                        struct guild *g = sd->state.gmaster_flag;
176                        int skill, strvit= 0, agidex = 0;
177                        if ((skill = guild_checkskill(g, GD_LEADERSHIP)) > 0) strvit |= (skill&0xFFFF)<<16;
178                        if ((skill = guild_checkskill(g, GD_GLORYWOUNDS)) > 0) strvit |= (skill&0xFFFF);
179                        if ((skill = guild_checkskill(g, GD_SOULCOLD)) > 0) agidex |= (skill&0xFFFF)<<16;
180                        if ((skill = guild_checkskill(g, GD_HAWKEYES)) > 0) agidex |= skill&0xFFFF;
181                        if (strvit || agidex)
182                                map_foreachinrange(skill_guildaura_sub, bl,2, BL_PC,
183                                        bl->id, sd->status.guild_id, strvit, agidex);
184                }
185        } else if (md) {
186                if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) {
187                        if( npc_touch_areanpc2(md) ) return 0; // Warped
188                } else
189                        md->areanpc_id = 0;
190                if (md->min_chase > md->db->range3) md->min_chase--;
191                //Walk skills are triggered regardless of target due to the idle-walk mob state.
192                //But avoid triggering on stop-walk calls.
193                if(tid != -1 &&
194                        !(ud->walk_count%WALK_SKILL_INTERVAL) &&
195                        mobskill_use(md, tick, -1))
196                {
197                        if (!(ud->skillid == NPC_SELFDESTRUCTION && ud->skilltimer != -1))
198                        {       //Skill used, abort walking
199                                clif_fixpos(bl); //Fix position as walk has been cancelled.
200                                return 0;
201                        }
202                        //Resend walk packet for proper Self Destruction display.
203                        clif_move(ud);
204                }
205        }
206
207        if(tid == -1) //A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant.
208                return 0;
209               
210        if(ud->state.change_walk_target)
211                return unit_walktoxy_sub(bl);
212
213        ud->walkpath.path_pos++;
214        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
215                i = -1;
216        else if(ud->walkpath.path[ud->walkpath.path_pos]&1)
217                i = status_get_speed(bl)*14/10;
218        else
219                i = status_get_speed(bl);
220
221        if( md && map_getcell(bl->m,x,y,CELL_CHKBASILICA) ) {
222                skill_blown(bl,bl,2,unit_getdir(bl),0);
223                clif_fixpos(bl);
224        }
225       
226        if(i > 0)
227                ud->walktimer = add_timer(tick+i,unit_walktoxy_timer,id,i);
228        else if(ud->state.running) {
229                //Keep trying to run.
230                if (!unit_run(bl))
231                        ud->state.running = 0;
232        }
233        else if (ud->target) {
234                //Update target trajectory.
235                struct block_list *tbl = map_id2bl(ud->target);
236                if (!tbl || !status_check_visibility(bl, tbl)) {        //Cancel chase.
237                        ud->to_x = bl->x;
238                        ud->to_y = bl->y;
239                        if (tbl && bl->type == BL_MOB) //See if the mob can do a warp chase.
240                                mob_warpchase((TBL_MOB*)bl, tbl);
241                        return 0;
242                }
243                if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange))
244                {       //Reached destination.
245                        if (ud->state.attack_continue)
246                        {       //Aegis uses one before every attack, we should
247                                //only need this one for syncing purposes. [Skotlex]
248                                clif_fixpos(bl);
249                                unit_attack(bl, tbl->id, ud->state.attack_continue);
250                        }
251                } else { //Update chase-path
252                        unit_walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue?2:0));
253                        return 0;
254                }
255        }
256        else {  //Stopped walking. Update to_x and to_y to current location [Skotlex]
257                ud->to_x = bl->x;
258                ud->to_y = bl->y;
259                if(md && md->nd) // Tell the script engine we've finished walking (for AI pathfinding)
260                        mob_script_callback(md, NULL, CALLBACK_WALKACK);
261        }
262        return 0;
263}
264
265static int unit_delay_walktoxy_timer(int tid, unsigned int tick, int id, intptr data)
266{
267        struct block_list *bl = map_id2bl(id);
268
269        if (!bl || bl->prev == NULL)
270                return 0;
271        unit_walktoxy(bl, (short)((data>>16)&0xffff), (short)(data&0xffff), 0);
272        return 1;
273}
274
275//flag parameter:
276//&1 -> 1/0 = easy/hard
277//&2 -> force walking
278//&4 -> Delay walking if the reason you can't walk is the canwalk delay
279int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
280{
281        struct unit_data* ud = NULL;
282        struct status_change* sc = NULL;
283
284        nullpo_retr(0, bl);
285       
286        ud = unit_bl2ud(bl);
287       
288        if( ud == NULL) return 0;
289
290        if (flag&4 && DIFF_TICK(ud->canmove_tick, gettick()) > 0 &&
291                DIFF_TICK(ud->canmove_tick, gettick()) < 2000)
292        {       // Delay walking command. [Skotlex]
293                add_timer(ud->canmove_tick+1, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF));
294                return 1;
295        }
296
297        if(!(flag&2) && (!(status_get_mode(bl)&MD_CANMOVE) || !unit_can_move(bl)))
298                return 0;
299       
300        ud->state.walk_easy = flag&1;
301        ud->target = 0;
302        ud->to_x = x;
303        ud->to_y = y;
304       
305        sc = status_get_sc(bl);
306        if (sc && sc->data[SC_CONFUSION]) //Randomize the target position
307                map_random_dir(bl, &ud->to_x, &ud->to_y);
308
[13]309
[1]310        if(ud->walktimer != -1) {
311                // Œ»Ý•à‚¢‚Ä‚¢‚éÅ’†‚Ì–Ú“I’n•ύX‚Ȃ̂Ń}ƒX–Ú‚Ì’†S‚É—ˆ‚œŽž‚É
312                // timerŠÖ”‚©‚çunit_walktoxy_sub‚ðŒÄ‚Ô‚æ‚€‚É‚·‚é
313                ud->state.change_walk_target = 1;
314                return 1;
315        }
316
317        if(ud->attacktimer != -1) {
318                delete_timer( ud->attacktimer, unit_attack_timer );
319                ud->attacktimer = -1;
320        }
321
322        return unit_walktoxy_sub(bl);
323}
324
325//To set Mob's CHASE/FOLLOW states (shouldn't be done if there's no path to reach)
326#define set_mobstate(bl, flag) \
327        if((bl)->type == BL_MOB && (flag)) \
328                ((TBL_MOB*)(bl))->state.skillstate = ((TBL_MOB*)(bl))->state.aggressive?MSS_FOLLOW:MSS_RUSH;
329
330static int unit_walktobl_sub(int tid, unsigned int tick, int id, intptr data)
331{
332        struct block_list *bl = map_id2bl(id);
333        struct unit_data *ud = bl?unit_bl2ud(bl):NULL;
334
335        if (ud && ud->walktimer == -1 && ud->target == data)
336        {
337                if (DIFF_TICK(ud->canmove_tick, tick) > 0) //Keep waiting?
338                        add_timer(ud->canmove_tick+1, unit_walktobl_sub, id, data);
339                else if (unit_can_move(bl))
340                {
341                        if (unit_walktoxy_sub(bl))
342                                set_mobstate(bl, ud->state.attack_continue);
343                }
344        }
345        return 0;       
346}
347
348// Chases a tbl. If the flag&1, use hard-path seek,
349// if flag&2, start attacking upon arrival within range, otherwise just walk to that character.
350int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int flag)
351{
352        struct unit_data        *ud = NULL;
353        struct status_change            *sc = NULL;
354        nullpo_retr(0, bl);
355        nullpo_retr(0, tbl);
356       
357        ud = unit_bl2ud(bl);
358        if( ud == NULL) return 0;
359
360        if (!(status_get_mode(bl)&MD_CANMOVE))
361                return 0;
362       
363        if (!unit_can_reach_bl(bl, tbl, distance_bl(bl, tbl)+1, flag&1, &ud->to_x, &ud->to_y)) {
364                ud->to_x = bl->x;
365                ud->to_y = bl->y;
366                return 0;
367        }
368
369        ud->state.walk_easy = flag&1;
370        ud->target = tbl->id;
371        ud->chaserange = range; //Note that if flag&2, this SHOULD be attack-range
372        ud->state.attack_continue = flag&2?1:0; //Chase to attack.
373
374        sc = status_get_sc(bl);
375        if (sc && sc->data[SC_CONFUSION]) //Randomize the target position
376                map_random_dir(bl, &ud->to_x, &ud->to_y);
[13]377        //Custom Jobs (blackmagic)
378        if (sc && sc->data[SC_FEAR]) //Flee from my target [Brainstorm]
379                unit_escape(bl, tbl, 15);
380        //Custom Job End
[1]381
[19]382        if (sc && sc->data[SC_FEAR]) //Flee from my target [Brainstorm]
383        unit_escape(bl, tbl, 15);
384
385
[1]386        if(ud->walktimer != -1) {
387                ud->state.change_walk_target = 1;
388                set_mobstate(bl, flag&2);
389                return 1;
390        }
391
392        if(DIFF_TICK(ud->canmove_tick, gettick()) > 0)
393        {       //Can't move, wait a bit before invoking the movement.
394                add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->target);
395                return 1;
396        }
397
398        if(!unit_can_move(bl))
399                return 0;
400
401        if(ud->attacktimer != -1) {
402                delete_timer( ud->attacktimer, unit_attack_timer );
403                ud->attacktimer = -1;
404        }
405
406        if (unit_walktoxy_sub(bl)) {
407                set_mobstate(bl, flag&2);
408                return 1;
409        }
410        return 0;
411}
412#undef set_mobstate
413
414int unit_run(struct block_list *bl)
415{
416        struct status_change *sc = status_get_sc(bl);
417        short to_x,to_y,dir_x,dir_y;
418        int lv;
419        int i;
420
421        if (!(sc && sc->data[SC_RUN]))
422                return 0;
423       
424        if (!unit_can_move(bl)) {
425                status_change_end(bl,SC_RUN,-1);
426                return 0;
427        }
428       
429        lv = sc->data[SC_RUN]->val1;
430        dir_x = dirx[sc->data[SC_RUN]->val2];
431        dir_y = diry[sc->data[SC_RUN]->val2];
432
433        // determine destination cell
434        to_x = bl->x;
435        to_y = bl->y;
436        for(i=0;i<AREA_SIZE;i++)
437        {
438                if(!map_getcell(bl->m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
439                        break;
440
441                //if sprinting and there's a PC/Mob/NPC, block the path [Kevin]
442                if(sc->data[SC_RUN] && map_count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC))
443                        break;
444                       
445                to_x += dir_x;
446                to_y += dir_y;
447        }
448
449        if(to_x == bl->x && to_y == bl->y) {
450                //If you can't run forward, you must be next to a wall, so bounce back. [Skotlex]
451                clif_status_change(bl, SI_BUMP, 1);
452
453                //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin]
454                unit_bl2ud(bl)->state.running = 0;
455                status_change_end(bl,SC_RUN,-1);
456
457                skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0);
458                clif_fixpos(bl); //Why is a clif_slide (skill_blown) AND a fixpos needed? Ask Aegis.
459                clif_status_change(bl, SI_BUMP, 0);
460                return 0;
461        }
462        if (unit_walktoxy(bl, to_x, to_y, 1))
463                return 1;
464        //There must be an obstacle nearby. Attempt walking one cell at a time.
465        do {
466                to_x -= dir_x;
467                to_y -= dir_y;
468        } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1));
469        if (i==0) {
470                // copy-paste from above
471                clif_status_change(bl, SI_BUMP, 1);
472
473                //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin]
474                unit_bl2ud(bl)->state.running = 0;
475                status_change_end(bl,SC_RUN,-1);
476
477                skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0);
478                clif_fixpos(bl);
479                clif_status_change(bl, SI_BUMP, 0);
480                return 0;
481        }
482        return 1;
483}
484
485//Makes bl attempt to run dist cells away from target. Uses hard-paths.
486int unit_escape(struct block_list *bl, struct block_list *target, short dist)
487{
488        int dir = map_calc_dir(target, bl->x, bl->y);
489        while( dist > 0 && map_getcell(bl->m, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], CELL_CHKNOREACH) )
490                dist--;
491        return ( dist > 0 && unit_walktoxy(bl, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], 0) );
492}
493
494//Instant warp function.
495int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath)
496{
497        short dx,dy;
498        uint8 dir;
499        struct unit_data        *ud = NULL;
500        struct map_session_data *sd = NULL;
501
502        nullpo_retr(0, bl);
503        sd = BL_CAST(BL_PC, bl);
504        ud = unit_bl2ud(bl);
505
506        if( ud == NULL) return 0;
507
508        unit_stop_walking(bl,1);
509        unit_stop_attack(bl);
510
511        if( checkpath && (map_getcell(bl->m,dst_x,dst_y,CELL_CHKNOPASS) || !path_search(NULL,bl->m,bl->x,bl->y,dst_x,dst_y,easy,CELL_CHKNOREACH)) )
512                return 0; // unreachable
513
514        dir = map_calc_dir(bl, dst_x,dst_y);
515        ud->dir = dir;
516
517        dx = dst_x - bl->x;
518        dy = dst_y - bl->y;
519
520        map_foreachinmovearea(clif_outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
521
522        map_moveblock(bl, dst_x, dst_y, gettick());
523       
524        ud->walktimer = 1;
525        map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl);
526        ud->walktimer = -1;
527               
528        if(sd) {
529                if(map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC)) {
530                        npc_touch_areanpc(sd,bl->m,bl->x,bl->y);
531                        if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
532                                return 0;
533                } else
534                        sd->areanpc_id=0;
535                if(sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0)
536                {       //Check if pet needs to be teleported. [Skotlex]
537                        int flag = 0;
538                        struct block_list* bl = &sd->pd->bl;
539                        if( !checkpath && !path_search(NULL,bl->m,bl->x,bl->y,dst_x,dst_y,0,CELL_CHKNOPASS) )
540                                flag = 1;
541                        else if (!check_distance_bl(&sd->bl, bl, AREA_SIZE)) //Too far, teleport.
542                                flag = 2;
543                        if (flag) {
544                                unit_movepos(bl,sd->bl.x,sd->bl.y, 0, 0);
545                                clif_slide(bl,bl->x,bl->y);
546                        }
547                }
548        }
549        return 1;
550}
551
552int unit_setdir(struct block_list *bl,unsigned char dir)
553{
554        struct unit_data *ud;
555        nullpo_retr( 0, bl );
556        ud = unit_bl2ud(bl);
557        if (!ud) return 0;
558        ud->dir = dir;
559        if (bl->type == BL_PC) 
560                ((TBL_PC *)bl)->head_dir = 0;
561        clif_changed_dir(bl, AREA);
562        return 0;
563}
564
565uint8 unit_getdir(struct block_list *bl)
566{
567        struct unit_data *ud;
568        nullpo_retr( 0, bl );
569        ud = unit_bl2ud(bl);
570        if (!ud) return 0;
571        return ud->dir;
572}
573
574//Warps a unit/ud to a given map/position.
575//In the case of players, pc_setpos is used.
576//it respects the no warp flags, so it is safe to call this without doing nowarpto/nowarp checks.
577int unit_warp(struct block_list *bl,short m,short x,short y,int type)
578{
579        struct unit_data *ud;
580        nullpo_retr(0, bl);
581        ud = unit_bl2ud(bl);
582       
583        if(bl->prev==NULL || !ud)
584                return 1;
585
586        if (type < 0 || type == 1)
587                //Type 1 is invalid, since you shouldn't warp a bl with the "death"
588                //animation, it messes up with unit_remove_map! [Skotlex]
589                return 1;
590       
591        if( m<0 ) m=bl->m;
592       
593        switch (bl->type) {
594                case BL_MOB:
595                        if (map[bl->m].flag.monster_noteleport)
596                                return 1;
597                        if (m != bl->m && map[m].flag.nobranch && battle_config.mob_warp&4)
598                                return 1;
599                        break;
600                case BL_PC:
601                        if (map[bl->m].flag.noteleport)
602                                return 1;
603                        break;
604        }
605       
606        if (x<0 || y<0)
607        {       //Random map position.
608                if (!map_search_freecell(NULL, m, &x, &y, -1, -1, 1)) {
609                        ShowWarning("unit_warp failed. Unit Id:%d/Type:%d, target position map %d (%s) at [%d,%d]\n", bl->id, bl->type, m, map[m].name, x, y);
610                        return 2;
611                       
612                }
613        } else if (map_getcell(m,x,y,CELL_CHKNOREACH))
614        {       //Invalid target cell
615                ShowWarning("unit_warp: Specified non-walkable target cell: %d (%s) at [%d,%d]\n", m, map[m].name, x,y);
616               
617                if (!map_search_freecell(NULL, m, &x, &y, 4, 4, 1))
618                {       //Can't find a nearby cell
619                        ShowWarning("unit_warp failed. Unit Id:%d/Type:%d, target position map %d (%s) at [%d,%d]\n", bl->id, bl->type, m, map[m].name, x, y);
620                        return 2;
621                }
622        }
623
624        if (bl->type == BL_PC) //Use pc_setpos
625                return pc_setpos((TBL_PC*)bl, map_id2index(m), x, y, type);
626       
627        if (!unit_remove_map(bl, type))
628                return 3;
629       
630        if (bl->m != m && battle_config.clear_unit_onwarp &&
631                battle_config.clear_unit_onwarp&bl->type)
632                skill_clear_unitgroup(bl);
633
634        bl->x=ud->to_x=x;
635        bl->y=ud->to_y=y;
636        bl->m=m;
637
638        map_addblock(bl);
639        clif_spawn(bl);
640        skill_unit_move(bl,gettick(),1);
641
642        if(bl->type == BL_MOB){
643                TBL_MOB *md = (TBL_MOB *)bl;
644                if(md->nd) // Tell the script engine we've warped
645                        mob_script_callback(md, NULL, CALLBACK_WARPACK);
646        }
647        return 0;
648}
649
650/*==========================================
651 * •às’âŽ~
652 *------------------------------------------*/
653int unit_stop_walking(struct block_list *bl,int type)
654{
655        struct unit_data *ud;
656        struct TimerData *data;
657        unsigned int tick;
658        nullpo_retr(0, bl);
659
660        ud = unit_bl2ud(bl);
661        if(!ud || ud->walktimer == -1)
662                return 0;
663        //NOTE: We are using timer data after deleting it because we know the
664        //delete_timer function does not messes with it. If the function's
665        //behaviour changes in the future, this code could break!
666        data = get_timer(ud->walktimer);
667        delete_timer(ud->walktimer, unit_walktoxy_timer);
668        ud->walktimer = -1;
669        ud->state.change_walk_target = 0;
670        tick = gettick();
671        if ((type&0x02 && !ud->walkpath.path_pos) //Force moving at least one cell.
672                || (data && DIFF_TICK(data->tick, tick) <= data->data/2)) //Enough time has passed to cover half-cell
673        {       
674                ud->walkpath.path_len = ud->walkpath.path_pos+1;
675                unit_walktoxy_timer(-1, tick, bl->id, ud->walkpath.path_pos);
676        }
677
678        if(type&0x01)
679                clif_fixpos(bl);
680       
681        ud->walkpath.path_len = 0;
682        ud->walkpath.path_pos = 0;
683        ud->to_x = bl->x;
684        ud->to_y = bl->y;
685        if(bl->type == BL_PET && type&~0xff)
686                ud->canmove_tick = gettick() + (type>>8);
687
688        //Readded, the check in unit_set_walkdelay means dmg during running won't fall through to this place in code [Kevin]
689        if (ud->state.running)
690                status_change_end(bl, SC_RUN, -1);
691        return 1;
692}
693
694int unit_skilluse_id(struct block_list *src, int target_id, short skill_num, short skill_lv)
695{
696        if(skill_num < 0) return 0;
697
698        return unit_skilluse_id2(
699                src, target_id, skill_num, skill_lv,
700                skill_castfix(src, skill_num, skill_lv),
701                skill_get_castcancel(skill_num)
702        );
703}
704
705int unit_is_walking(struct block_list *bl)
706{
707        struct unit_data *ud = unit_bl2ud(bl);
708        nullpo_retr(0, bl);
709        if(!ud) return 0;
710        return (ud->walktimer != -1);
711}
712
713/*==========================================
714 * Determines if the bl can move based on status changes. [Skotlex]
715 *------------------------------------------*/
716int unit_can_move(struct block_list *bl)
717{
718        struct map_session_data *sd;
719        struct unit_data *ud;
720        struct status_change *sc;
721       
722        nullpo_retr(0, bl);
723        ud = unit_bl2ud(bl);
724        sc = status_get_sc(bl);
725        sd = BL_CAST(BL_PC, bl);
726       
727        if (!ud)
728                return 0;
729       
730        if (ud->skilltimer != -1 && (!sd || !pc_checkskill(sd, SA_FREECAST) || skill_get_inf2(ud->skillid)&INF2_GUILD_SKILL))
731                return 0; // prevent moving while casting
732       
733        if (DIFF_TICK(ud->canmove_tick, gettick()) > 0)
734                return 0;
735       
736        if (sd && (
737                pc_issit(sd) ||
738                sd->vender_id ||
739                sd->state.blockedmove
740        ))
741                return 0; //Can't move
742       
743        if (sc) {
744                if (sc->opt1 > 0 && sc->opt1 != OPT1_STONEWAIT)
745                        return 0;
746
747                if ((sc->option & OPTION_HIDE) && (!sd || pc_checkskill(sd, RG_TUNNELDRIVE) <= 0))
748                        return 0;
749
750                if (sc->count && (
751                        sc->data[SC_ANKLE]
752                        || sc->data[SC_AUTOCOUNTER]
753                        || sc->data[SC_TRICKDEAD]
754                        || sc->data[SC_BLADESTOP]
755                        || sc->data[SC_BLADESTOP_WAIT]
756                        || sc->data[SC_SPIDERWEB]
757                        || (sc->data[SC_DANCING] && sc->data[SC_DANCING]->val4 && (
758                                !sc->data[SC_LONGING] ||
759                                (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT ||
760                                (sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE
761                        ))
762                        || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)       // cannot move while gospel is in effect
763                        || sc->data[SC_STOP]
764                        || sc->data[SC_CLOSECONFINE]
765                        || sc->data[SC_CLOSECONFINE2]
766                        || (sc->data[SC_CLOAKING] && //Need wall at level 1-2
767                                sc->data[SC_CLOAKING]->val1 < 3 && !(sc->data[SC_CLOAKING]->val4&1))
768                        || sc->data[SC_MADNESSCANCEL]
769                        || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
770                ))
771                        return 0;
772        }
773        return 1;
774}
775
776
777/*==========================================
778 * Resume running after a walk delay
779 *------------------------------------------*/
780
781int unit_resume_running(int tid, unsigned int tick, int id, intptr data)
782{
783
784        struct unit_data *ud = (struct unit_data *)data;
785        TBL_PC * sd = map_id2sd(id);
786
787        clif_skill_nodamage(ud->bl,ud->bl,TK_RUN,ud->skilllv,
788                        sc_start4(ud->bl,status_skill2sc(TK_RUN),100,ud->skilllv,unit_getdir(ud->bl),0,0,0));
789
790        if (sd) clif_walkok(sd);
791
792        return 0;
793
794}
795
796
797/*==========================================
798 * Applies walk delay to character, considering that
799 * if type is 0, this is a damage induced delay: if previous delay is active, do not change it.
800 * if type is 1, this is a skill induced delay: walk-delay may only be increased, not decreased.
801 *------------------------------------------*/
802int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int type)
803{
804        struct unit_data *ud = unit_bl2ud(bl);
805        if (delay <= 0 || !ud) return 0;
806       
807        if (type) {
808                if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
809                        return 0;
810        } else {
811                if (DIFF_TICK(ud->canmove_tick, tick) > 0)
812                        return 0;
813        }
814        ud->canmove_tick = tick + delay;
815        if (ud->walktimer != -1)
816        {       //Stop walking, if chasing, readjust timers.
817                if (delay == 1)
818                {       //Minimal delay (walk-delay) disabled. Just stop walking.
819                        unit_stop_walking(bl,0);
820                } else {
821                        //Resume running after can move again [Kevin]
822                        if(ud->state.running)
823                        {
824                                add_timer(ud->canmove_tick, unit_resume_running, bl->id, (int)ud);
825                        }
826                        else
827                        {
828                                unit_stop_walking(bl,2);
829                                if(ud->target)
830                                        add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->target);
831                        }
832                }
833        }
834        return 1;
835}
836
837int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, short skill_lv, int casttime, int castcancel)
838{
839        struct unit_data *ud;
840        struct status_data *tstatus;
841        struct status_change *sc;
842        struct map_session_data *sd = NULL;
843        struct block_list * target = NULL;
844        unsigned int tick = gettick();
845        int temp;
846
847        nullpo_retr(0, src);
848        if(status_isdead(src))
849                return 0; // Ž€‚ñ‚Å‚¢‚È‚¢‚©
850
851        sd = BL_CAST(BL_PC, src);
852        ud = unit_bl2ud(src);
853
854        if(ud == NULL) return 0;
855        sc = status_get_sc(src);       
856        if (sc && !sc->count)
857                sc = NULL; //Unneeded
858        //temp: used to signal combo-skills right now.
859        temp = (target_id == src->id && !(sd && sd->state.skill_flag)
860                && skill_get_inf(skill_num)&INF_SELF_SKILL
861                && skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF);
862        if (temp)
863                target_id = ud->target; //Auto-select skills. [Skotlex]
864
865        if (sd) {
866                //Target_id checking.
867                if(skillnotok(skill_num, sd)) // [MouseJstr]
868                        return 0;
869
870                mob_ksprotected(src, map_id2bl(target_id));
871
872                switch(skill_num)
873                {       //Check for skills that auto-select target
874                case MO_CHAINCOMBO:
875                        if (sc && sc->data[SC_BLADESTOP]){
876                                if ((target=(struct block_list *)sc->data[SC_BLADESTOP]->val4) == NULL)
877                                        return 0;
878                        }
879                        break;
880                case TK_JUMPKICK:
881                case TK_COUNTER:
882                case HT_POWER:
883                        if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_num)
884                                target_id = sc->data[SC_COMBO]->val2;
885                        break;
886                case WE_MALE:
887                case WE_FEMALE:
888                        if (!sd->status.partner_id)
889                                return 0;
890                        target = (struct block_list*)map_charid2sd(sd->status.partner_id);
891                        if (!target) {
892                                clif_skill_fail(sd,skill_num,0,0);
893                                return 0;
894                        }
895                        break;
896                }
897                if (target)
898                        target_id = target->id;
899        }
900        if (src->type==BL_HOM)
901        switch(skill_num)
902        { //Homun-auto-target skills.
903                case HLIF_HEAL:
904                case HLIF_AVOID:
905                case HAMI_DEFENCE:
906                case HAMI_CASTLE:
907                        target = battle_get_master(src);
908                        if (!target) return 0;
909                        target_id = target->id;
910        }
911
912        if( !target ) // choose default target
913                target = map_id2bl(target_id);
914
915        if( !target || src->m != target->m || !src->prev || !target->prev )
916                return 0;
917
918        //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex]
919        if(ud->skilltimer != -1 && skill_num != SA_CASTCANCEL)
920                return 0;
921
922        if(skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF && src->id == target_id)
923                return 0;
924
925        if(!status_check_skilluse(src, target, skill_num, 0))
926                return 0;
927
928        tstatus = status_get_status_data(target);
929        //’Œ‘O‚̃XƒLƒ‹ó‹µ‚Ì‹L˜^
930        if(sd) {
931                switch(skill_num){
932                case SA_CASTCANCEL:
933                        if(ud->skillid != skill_num){
934                                sd->skillid_old = ud->skillid;
935                                sd->skilllv_old = ud->skilllv;
936                        }
937                        break;
938                case BD_ENCORE:
939                        //Prevent using the dance skill if you no longer have the skill in your tree.
940                        if(!sd->skillid_dance || pc_checkskill(sd,sd->skillid_dance)<=0){
941                                clif_skill_fail(sd,skill_num,0,0);
942                                return 0;
943                        }
944                        sd->skillid_old = skill_num;
945                        break;
946                case BD_LULLABY:
947                case BD_RICHMANKIM:
948                case BD_ETERNALCHAOS:
949                case BD_DRUMBATTLEFIELD:
950                case BD_RINGNIBELUNGEN:
951                case BD_ROKISWEIL:
952                case BD_INTOABYSS:
953                case BD_SIEGFRIED:
954                case CG_MOONLIT:
955                        if (skill_check_pc_partner(sd, skill_num, &skill_lv, 1, 0) < 1)
956                        {
957                                clif_skill_fail(sd,skill_num,0,0);
958                                return 0;
959                        }
960                        break;
961                }
962                if (!skill_check_condition(sd, skill_num, skill_lv, 0))
963                        return 0;
964        }
965        //TODO: Add type-independant skill_check_condition function.
966        if (src->type == BL_MOB) {
967                switch (skill_num) {
968                        case NPC_SUMMONSLAVE:
969                        case NPC_SUMMONMONSTER:
970                        case AL_TELEPORT:
971                                if (((TBL_MOB*)src)->master_id && ((TBL_MOB*)src)->special_state.ai)
972                                        return 0;
973                }
974        }
975
976        //Check range when not using skill on yourself or is a combo-skill during attack
977        //(these are supposed to always have the same range as your attack)
978        if(src->id != target_id && (!temp || ud->attacktimer == -1))
979        {
980                if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
981                {
982                        if (!unit_can_reach_bl(src, target, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
983                                return 0; //Walk-path check failed.
984                } else
985                if      (!battle_check_range(src, target, skill_get_range2(src, skill_num,skill_lv)
986                        +(skill_num==RG_CLOSECONFINE?0:1)))
987                        //Close confine is exploitable thanks to this extra range "feature" of the client. [Skotlex]
988                        return 0; //Arrow-path check failed.
989        }
990
991        if (!temp) //Stop attack on non-combo skills [Skotlex]
992                unit_stop_attack(src);
993        else if(ud->attacktimer != -1) //Elsewise, delay current attack sequence
994                ud->attackabletime = tick + status_get_adelay(src);
995       
996        ud->state.skillcastcancel = castcancel;
997
998        //temp: Used to signal force cast now.
999        temp = 0;
1000       
1001        switch(skill_num){
1002        case ALL_RESURRECTION:
1003                if(battle_check_undead(tstatus->race,tstatus->def_ele)) {       
1004                        temp = 1;
1005                        casttime = skill_castfix(src, PR_TURNUNDEAD, skill_lv);
1006                } else if (!status_isdead(target))
1007                        return 0; //Can't cast on non-dead characters.
1008        break;
1009        case MO_FINGEROFFENSIVE:
1010                if(sd)
1011                        casttime += casttime * min(skill_lv, sd->spiritball);
1012        break;
1013        case MO_EXTREMITYFIST:
1014                if (sc && sc->data[SC_COMBO] &&
1015                   (sc->data[SC_COMBO]->val1 == MO_COMBOFINISH ||
1016                        sc->data[SC_COMBO]->val1 == CH_TIGERFIST ||
1017                        sc->data[SC_COMBO]->val1 == CH_CHAINCRUSH))
1018                        casttime = 0;
1019                temp = 1;
1020        break;
1021        case SA_SPELLBREAKER:
1022                temp = 1;
1023        break;
1024        case ST_CHASEWALK:
1025                if (sc && sc->data[SC_CHASEWALK])
1026                        casttime = 0;
1027        break;
1028        case TK_RUN:
1029                if (sc && sc->data[SC_RUN])
1030                        casttime = 0;
1031        break;
1032        case KN_CHARGEATK:
1033                {
1034                unsigned int k = (distance_bl(src,target)-1)/3; //+100% every 3 cells of distance
1035                if( k > 2 ) k = 2; // ...but hard-limited to 300%.
1036                casttime += casttime * k; 
1037                }
1038        break;
1039        }
1040       
1041        // moved here to prevent Suffragium from ending if skill fails
1042        if (!(skill_get_castnodex(skill_num, skill_lv)&2))
1043                casttime = skill_castfix_sc(src, casttime);
1044
1045        if( casttime>0 || temp){ 
1046
1047                clif_skillcasting(src, src->id, target_id, 0,0, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
1048
1049                if (sd && target->type == BL_MOB)
1050                {
1051                        TBL_MOB *md = (TBL_MOB*)target;
1052                        mobskill_event(md, src, tick, -1); //Cast targetted skill event.
1053                        //temp: used to store mob's mode now.
1054                        if (tstatus->mode&(MD_CASTSENSOR_IDLE|MD_CASTSENSOR_CHASE) &&
1055                                battle_check_target(target, src, BCT_ENEMY) > 0)
1056                        {
1057                                switch (md->state.skillstate) {
1058                                case MSS_RUSH:
1059                                case MSS_FOLLOW:
1060                                        if (!(tstatus->mode&MD_CASTSENSOR_CHASE))
1061                                                break;
1062                                        md->target_id = src->id;
1063                                        md->state.aggressive = (temp&MD_ANGRY)?1:0;
1064                                        md->min_chase = md->db->range3;
1065                                        break;
1066                                case MSS_IDLE:
1067                                case MSS_WALK:
1068                                        if (!(tstatus->mode&MD_CASTSENSOR_IDLE))
1069                                                break;
1070                                        md->target_id = src->id;
1071                                        md->state.aggressive = (temp&MD_ANGRY)?1:0;
1072                                        md->min_chase = md->db->range3;
1073                                        break;
1074                                }
1075                        }
1076                }
1077        }
1078
1079        if( casttime<=0 )
1080                ud->state.skillcastcancel=0;
1081
1082        ud->canact_tick  = tick + casttime + 100;
1083        ud->skilltarget  = target_id;
1084        ud->skillx       = 0;
1085        ud->skilly       = 0;
1086        ud->skillid      = skill_num;
1087        ud->skilllv      = skill_lv;
1088
1089        if(sc && sc->data[SC_CLOAKING] &&
1090                !(sc->data[SC_CLOAKING]->val4&4) && skill_num != AS_CLOAKING)
1091        {
1092                status_change_end(src,SC_CLOAKING,-1);
1093                if (!src->prev) return 0; //Warped away!
1094        }
1095
1096        if(casttime > 0) {
1097                ud->skilltimer = add_timer( tick+casttime, skill_castend_id, src->id, 0 );
1098                if(sd && pc_checkskill(sd,SA_FREECAST))
1099                        status_freecast_switch(sd);
1100                else
1101                        unit_stop_walking(src,1);
1102        }
1103        else
1104                skill_castend_id(ud->skilltimer,tick,src->id,0);
1105        return 1;
1106}
1107
1108int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, short skill_num, short skill_lv)
1109{
1110        if(skill_num < 0)
1111                return 0;
1112        return unit_skilluse_pos2(
1113                src, skill_x, skill_y, skill_num, skill_lv,
1114                skill_castfix(src, skill_num, skill_lv),
1115                skill_get_castcancel(skill_num)
1116        );
1117}
1118
1119int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, short skill_num, short skill_lv, int casttime, int castcancel)
1120{
1121        struct map_session_data *sd = NULL;
1122        struct unit_data        *ud = NULL;
1123        struct status_change *sc;
1124        struct block_list    bl;
1125        unsigned int tick = gettick();
1126
1127        nullpo_retr(0, src);
1128
1129        if(!src->prev) return 0; // map ã‚É‘¶Ý‚·‚é‚©
1130        if(status_isdead(src)) return 0;
1131
1132        sd = BL_CAST(BL_PC, src);
1133        ud = unit_bl2ud(src);
1134        if(ud == NULL) return 0;
1135
1136        if(ud->skilltimer != -1) //Normally not needed since clif.c checks for it, but at/char/script commands don't! [Skotlex]
1137                return 0;
1138       
1139        sc = status_get_sc(src);
1140        if (sc && !sc->count)
1141                sc = NULL;
1142       
1143        if(sd) {
1144                if (skillnotok(skill_num, sd) || !skill_check_condition(sd, skill_num, skill_lv,0))
1145                return 0;
1146        } 
1147       
1148        if (!status_check_skilluse(src, NULL, skill_num, 0))
1149                return 0;
1150
1151        if( map_getcell(src->m, skill_x, skill_y, CELL_CHKWALL) )
1152        {// can't cast ground targeted spells on wall cells
1153                if (sd) clif_skill_fail(sd,skill_num,0,0);
1154                return 0;
1155        }
1156
1157        /* ŽË’ö‚ƏáŠQ•šƒ`ƒFƒbƒN */
1158        bl.type = BL_NUL;
1159        bl.m = src->m;
1160        bl.x = skill_x;
1161        bl.y = skill_y;
1162
1163        if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
1164        {
1165                if (!unit_can_reach_bl(src, &bl, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
1166                        return 0; //Walk-path check failed.
1167        } else
1168        if      (!battle_check_range(src,&bl,skill_get_range2(src, skill_num,skill_lv)+1))
1169                return 0; //Arrow-path check failed.
1170
1171        unit_stop_attack(src);
1172        ud->state.skillcastcancel = castcancel;
1173
1174        // moved here to prevent Suffragium from ending if skill fails
1175        if (!(skill_get_castnodex(skill_num, skill_lv)&2))
1176                casttime = skill_castfix_sc(src, casttime);
1177
1178        if( casttime>0 ) {
1179                unit_stop_walking( src, 1);
1180                clif_skillcasting(src, src->id, 0, skill_x, skill_y, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
1181        } else
1182                ud->state.skillcastcancel=0;
1183
1184        ud->canact_tick  = tick + casttime + 100;
1185        ud->skillid      = skill_num;
1186        ud->skilllv      = skill_lv;
1187        ud->skillx       = skill_x;
1188        ud->skilly       = skill_y;
1189        ud->skilltarget  = 0;
1190
1191        if (sc && sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4))
1192        {
1193                status_change_end(src,SC_CLOAKING,-1);
1194                if (!src->prev) return 0; //Warped away!
1195        }
1196
1197        if(casttime > 0) {
1198                ud->skilltimer = add_timer( tick+casttime, skill_castend_pos, src->id, 0 );
1199                if(sd && pc_checkskill(sd,SA_FREECAST))
1200                        status_freecast_switch(sd);
1201                else
1202                        unit_stop_walking(src,1);
1203        }
1204        else {
1205                ud->skilltimer = -1;
1206                skill_castend_pos(ud->skilltimer,tick,src->id,0);
1207        }
1208        return 1;
1209}
1210
1211static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data);
1212
1213int unit_stop_attack(struct block_list *bl)
1214{
1215        struct unit_data *ud = unit_bl2ud(bl);
1216        nullpo_retr(0, bl);
1217
1218        if(!ud || ud->attacktimer == -1)
1219                return 0;
1220
1221        delete_timer( ud->attacktimer, unit_attack_timer );
1222        ud->attacktimer = -1;
1223        ud->target = 0;
1224        return 0;
1225}
1226
1227//Means current target is unattackable. For now only unlocks mobs.
1228int unit_unattackable(struct block_list *bl)
1229{
1230        struct unit_data *ud = unit_bl2ud(bl);
1231        if (ud) {
1232                ud->target = 0;
1233                ud->state.attack_continue = 0;
1234        }
1235       
1236        if(bl->type == BL_MOB)
1237                mob_unlocktarget((struct mob_data*)bl, gettick()) ;
1238        else if(bl->type == BL_PET)
1239                pet_unlocktarget((struct pet_data*)bl);
1240        return 0;
1241}
1242
1243/*==========================================
1244 * UŒ‚—v‹
1245 * type‚ª1‚È‚çŒp‘±UŒ‚
1246 *------------------------------------------*/
1247int unit_attack(struct block_list *src,int target_id,int continuous)
1248{
1249        struct block_list *target;
1250        struct unit_data  *ud;
1251
1252        nullpo_retr(0, ud = unit_bl2ud(src));
1253
1254        target=map_id2bl(target_id);
1255        if(target==NULL || status_isdead(target)) {
1256                unit_unattackable(src);
1257                return 1;
1258        }
1259
1260        if( src->type == BL_PC ){
1261                TBL_PC* sd = (TBL_PC*)src;
1262                if( target->type == BL_NPC )
1263                {// monster npcs [Valaris]
1264                        npc_click(sd,(TBL_NPC*)target); // submitted by leinsirk10 [Celest]
1265                        return 0;
1266                } else if( pc_is90overweight(sd) )
1267                {// overwheight - stop attacking and walking
1268                        unit_stop_attack(src);
1269                        unit_stop_walking(src,1);
1270                        return 0;
1271                }
1272        }
1273
1274        if(battle_check_target(src,target,BCT_ENEMY)<=0 ||
1275                !status_check_skilluse(src, target, 0, 0)
1276        ) {
1277                unit_unattackable(src);
1278                return 1;
1279        }
1280
1281        ud->target = target_id;
1282        ud->state.attack_continue = continuous;
1283        if (continuous) //If you're to attack continously, set to auto-case character
1284                ud->chaserange = status_get_range(src);
1285
1286        //Just change target/type. [Skotlex]
1287        if(ud->attacktimer != -1)
1288                return 0;
1289
1290        //Set Mob's ANGRY/BERSERK states.
1291        if(src->type == BL_MOB)
1292                ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK;
1293
1294        if(DIFF_TICK(ud->attackabletime, gettick()) > 0)
1295                //Do attack next time it is possible. [Skotlex]
1296                ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1297        else //Attack NOW.
1298                unit_attack_timer(-1,gettick(),src->id,0);
1299
1300        return 0;
1301}
1302
1303//Cancels an ongoing combo, resets attackable time and restarts the
1304//attack timer to resume attacking after amotion time. [Skotlex]
1305int unit_cancel_combo(struct block_list *bl)
1306{
1307        struct unit_data  *ud;
1308
1309        if (!status_change_end(bl, SC_COMBO, -1))
1310                return 0; //Combo wasn't active.
1311
1312        ud = unit_bl2ud(bl);
1313        nullpo_retr(0, ud);
1314
1315        ud->attackabletime = gettick() + status_get_amotion(bl);
1316
1317        if (ud->attacktimer == -1)
1318                return 1; //Nothing more to do.
1319       
1320        delete_timer(ud->attacktimer, unit_attack_timer);
1321        ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,bl->id,0);
1322        return 1;
1323}
1324/*==========================================
1325 *
1326 *------------------------------------------*/
1327bool unit_can_reach_pos(struct block_list *bl,int x,int y, int easy)
1328{
1329        nullpo_retr(false, bl);
1330
1331        if( bl->x==x && bl->y==y )      // “¯‚¶ƒ}ƒX
1332                return true;
1333
1334        return path_search(NULL,bl->m,bl->x,bl->y,x,y,easy,CELL_CHKNOREACH);
1335}
1336
1337/*==========================================
1338 *
1339 *------------------------------------------*/
1340bool unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, int easy, short *x, short *y)
1341{
1342        int i;
1343        short dx,dy;
1344        nullpo_retr(false, bl);
1345        nullpo_retr(false, tbl);
1346
1347        if( bl->m != tbl->m)
1348                return false;
1349       
1350        if( bl->x==tbl->x && bl->y==tbl->y )
1351                return true;
1352
1353        if(range>0 && !check_distance_bl(bl, tbl, range))
1354                return false;
1355
1356        // It judges whether it can adjoin or not.
1357        dx=tbl->x - bl->x;
1358        dy=tbl->y - bl->y;
1359        dx=(dx>0)?1:((dx<0)?-1:0);
1360        dy=(dy>0)?1:((dy<0)?-1:0);
1361       
1362        if (map_getcell(tbl->m,tbl->x-dx,tbl->y-dy,CELL_CHKNOPASS))
1363        {       //Look for a suitable cell to place in.
1364                for(i=0;i<9 && map_getcell(tbl->m,tbl->x-dirx[i],tbl->y-diry[i],CELL_CHKNOPASS);i++);
1365                if (i==9) return false; //No valid cells.
1366                dx = dirx[i];
1367                dy = diry[i];
1368        }
1369
1370        if (x) *x = tbl->x-dx;
1371        if (y) *y = tbl->y-dy;
1372        return path_search(NULL,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH);
1373}
1374
1375
1376/*==========================================
1377 * PC‚̍UŒ‚ (timerŠÖ”)
1378 *------------------------------------------*/
1379static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int tick)
1380{
1381        struct block_list *target;
1382        struct unit_data *ud;
1383        struct status_data *sstatus;
1384        struct map_session_data *sd = NULL;
1385        struct mob_data *md = NULL;
1386        int range;
1387       
1388        if((ud=unit_bl2ud(src))==NULL)
1389                return 0;
1390        if(ud->attacktimer != tid){
1391                ShowError("unit_attack_timer %d != %d\n",ud->attacktimer,tid);
1392                return 0;
1393        }
1394        sd = BL_CAST(BL_PC, src);
1395        md = BL_CAST(BL_MOB, src);
1396        ud->attacktimer=-1;
1397        target=map_id2bl(ud->target);
1398
1399        if(src == NULL || src->prev == NULL || target==NULL || target->prev == NULL)
1400                return 0;
1401
1402        if(status_isdead(src) || status_isdead(target) || !status_check_skilluse(src, target, 0, 0))
1403                return 0; // can't attack under these conditions
1404
1405        if (src->m != target->m)
1406        {
1407                if (src->type == BL_MOB && mob_warpchase((TBL_MOB*)src, target))
1408                        return 1; // Follow up.
1409                return 0;
1410        }
1411
1412        if(ud->skilltimer != -1 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0))
1413                return 0; // can't attack while casting
1414       
1415        if(!battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0))
1416        {       // attacking when under cast delay has restrictions:
1417                if (tid == -1) { //requested attack.
1418                        if(sd) clif_skill_fail(sd,1,4,0);
1419                        return 0;
1420                }
1421                //Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex]
1422                if(ud->state.attack_continue) {
1423                        if (DIFF_TICK(ud->canact_tick, ud->attackabletime) > 0)
1424                                ud->attackabletime = ud->canact_tick;
1425                        ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1426                }
1427                return 1;
1428        }
1429
1430        sstatus = status_get_status_data(src);
1431        range = sstatus->rhw.range;
1432       
1433        if(!sd || sd->status.weapon != W_BOW) range++; //Dunno why everyone but bows gets this extra range...
1434        if(unit_is_walking(target)) range++; //Extra range when chasing
1435
1436        if(!check_distance_bl(src,target,range) ) {
1437                //Chase if required.
1438                if(sd)
1439                        clif_movetoattack(sd,target);
1440                else if(ud->state.attack_continue)
1441                        unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
1442                return 1;
1443        }
1444        if(!battle_check_range(src,target,range)) {
1445                //Within range, but no direct line of attack
1446                if(ud->state.attack_continue) {
1447                        if(ud->chaserange > 2) ud->chaserange-=2;
1448                        unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
1449                }
1450                return 1;
1451        }
1452
1453        //Sync packet only for players.
1454        //Non-players use the sync packet on the walk timer. [Skotlex]
1455        if (tid == -1 && sd) clif_fixpos(src);
1456
1457        if(DIFF_TICK(ud->attackabletime,tick) <= 0)
1458        {
1459                if (battle_config.attack_direction_change && (src->type&battle_config.attack_direction_change)) {
1460                        ud->dir = map_calc_dir(src, target->x,target->y );
1461                }
1462                if(ud->walktimer != -1)
1463                        unit_stop_walking(src,1);
1464                if(md) {
1465                        if (mobskill_use(md,tick,-1))
1466                                return 1;
1467                        if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
1468                        {       // Link monsters nearby [Skotlex]
1469                                md->last_linktime = tick;
1470                                map_foreachinrange(mob_linksearch, src, md->db->range2, BL_MOB, md->class_, target, tick);
1471                        }
1472                }
1473                if(src->type == BL_PET && pet_attackskill((TBL_PET*)src, target->id))
1474                        return 1;
1475               
1476                map_freeblock_lock();
1477                ud->attacktarget_lv = battle_weapon_attack(src,target,tick,0);
1478
1479                if(sd && sd->status.pet_id > 0 && sd->pd && battle_config.pet_attack_support)
1480                        pet_target_check(sd,target,0);
1481                map_freeblock_unlock();
1482
1483                ud->attackabletime = tick + sstatus->adelay;
1484//              You can't move if you can't attack neither.
1485                if (src->type&battle_config.attack_walk_delay)
1486                        unit_set_walkdelay(src, tick, sstatus->amotion, 1);
1487        }
1488
1489        if(ud->state.attack_continue)
1490                ud->attacktimer = add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1491
1492        return 1;
1493}
1494
1495static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data)
1496{
1497        struct block_list *bl;
1498        bl = map_id2bl(id);
1499        if(bl && unit_attack_timer_sub(bl, tid, tick) == 0)
1500                unit_unattackable(bl);
1501        return 0;
1502}
1503
1504/*==========================================
1505 * Cancels an ongoing skill cast.
1506 * flag&1: Cast-Cancel invoked.
1507 * flag&2: Cancel only if skill is cancellable.
1508 *------------------------------------------*/
1509int unit_skillcastcancel(struct block_list *bl,int type)
1510{
1511        struct map_session_data *sd = NULL;
1512        struct unit_data *ud = unit_bl2ud( bl);
1513        unsigned int tick=gettick();
1514        int ret=0, skill;
1515       
1516        nullpo_retr(0, bl);
1517        if (!ud || ud->skilltimer==-1)
1518                return 0; //Nothing to cancel.
1519
1520        sd = BL_CAST(BL_PC, bl);
1521
1522        if (type&2) {
1523                //See if it can be cancelled.
1524                if (!ud->state.skillcastcancel)
1525                        return 0;
1526
1527                if (sd && (sd->special_state.no_castcancel2 ||
1528                        (sd->special_state.no_castcancel && !map_flag_gvg(bl->m)))) //fixed flags being read the wrong way around [blackhole89]
1529                        return 0;
1530        }
1531       
1532        ud->canact_tick=tick;
1533        if(sd && pc_checkskill(sd,SA_FREECAST))
1534                status_freecast_switch(sd);
1535       
1536        if(type&1 && sd)
1537                skill = sd->skillid_old;
1538        else
1539                skill = ud->skillid;
1540       
1541        if (skill_get_inf(skill) & INF_GROUND_SKILL)
1542                ret=delete_timer( ud->skilltimer, skill_castend_pos );
1543        else
1544                ret=delete_timer( ud->skilltimer, skill_castend_id );
1545        if(ret<0)
1546                ShowError("delete timer error : skillid : %d\n",ret);
1547       
1548        if(bl->type==BL_MOB) ((TBL_MOB*)bl)->skillidx  = -1;
1549
1550        ud->skilltimer = -1;
1551        clif_skillcastcancel(bl);
1552        return 1;
1553}
1554
1555// unit_data ‚̏‰Šú‰»ˆ—
1556void unit_dataset(struct block_list *bl)
1557{
1558        struct unit_data *ud;
1559        nullpo_retv(ud = unit_bl2ud(bl));
1560
1561        memset( ud, 0, sizeof( struct unit_data) );
1562        ud->bl             = bl;
1563        ud->walktimer      = -1;
1564        ud->skilltimer     = -1;
1565        ud->attacktimer    = -1;
1566        ud->attackabletime = 
1567        ud->canact_tick    = 
1568        ud->canmove_tick   = gettick();
1569}
1570
1571/*==========================================
1572 * Returns 1 if this unit is attacking target 'id'
1573 *------------------------------------------*/
1574static int unit_counttargeted_sub(struct block_list* bl, va_list ap)
1575{
1576        int id = va_arg(ap, int);
1577        int target_lv = va_arg(ap, int); // extra condition
1578        struct unit_data* ud;
1579
1580        if(bl->id == id)
1581                return 0;
1582
1583        ud = unit_bl2ud(bl);
1584
1585        if (ud && ud->target == id && ud->attacktimer != -1 && ud->attacktarget_lv >= target_lv)
1586                return 1;
1587
1588        return 0;       
1589}
1590
1591/*==========================================
1592 * Counts the number of units attacking 'bl'
1593 *------------------------------------------*/
1594int unit_counttargeted(struct block_list* bl, int target_lv)
1595{
1596        nullpo_retr(0, bl);
1597        return (map_foreachinrange(unit_counttargeted_sub, bl, AREA_SIZE, BL_CHAR, bl->id, target_lv));
1598}
1599
1600/*==========================================
1601 *
1602 *------------------------------------------*/
1603int unit_fixdamage(struct block_list *src,struct block_list *target,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2)
1604{
1605        nullpo_retr(0, target);
1606
1607        if(damage+damage2 <= 0)
1608                return 0;
1609       
1610        return status_fix_damage(src,target,damage+damage2,clif_damage(target,target,tick,sdelay,ddelay,damage,div,type,damage2));
1611}
1612
1613/*==========================================
1614 * Œ©‚œ–ڂ̃TƒCƒY‚ð•ύX‚·‚é
1615 *------------------------------------------*/
1616int unit_changeviewsize(struct block_list *bl,short size)
1617{
1618        nullpo_retr(0, bl);
1619
1620        size=(size<0)?-1:(size>0)?1:0;
1621
1622        if(bl->type == BL_PC) {
1623                ((TBL_PC*)bl)->state.size=size;
1624        } else if(bl->type == BL_MOB) {
1625                ((TBL_MOB*)bl)->special_state.size=size;
1626        } else
1627                return 0;
1628        if(size!=0)
1629                clif_misceffect2(bl,421+size);
1630        return 0;
1631}
1632
1633/*==========================================
1634 * Removes a bl/ud from the map.
1635 * Returns 1 on success. 0 if it couldn't be removed or the bl was free'd
1636 * if clrtype is 1 (death), appropiate cleanup is performed.
1637 * Otherwise it is assumed bl is being warped.
1638 * On-Kill specific stuff is not performed here, look at status_damage for that.
1639 *------------------------------------------*/
1640int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int line, const char* func)
1641{
1642        struct unit_data *ud = unit_bl2ud(bl);
1643        struct status_change *sc = status_get_sc(bl);
1644        nullpo_retr(0, ud);
1645
1646        if(bl->prev == NULL)
1647                return 0; //Already removed?
1648
1649        map_freeblock_lock();
1650
1651        ud->target = 0; //Unlock walk/attack target.
1652        if (ud->walktimer != -1)
1653                unit_stop_walking(bl,0);
1654        if (ud->attacktimer != -1)
1655                unit_stop_attack(bl);
1656        if (ud->skilltimer != -1)
1657                unit_skillcastcancel(bl,0);
1658// Do not reset can-act delay. [Skotlex]
1659        ud->attackabletime = ud->canmove_tick /*= ud->canact_tick*/ = gettick();
1660       
1661        if(sc && sc->count ) { //map-change/warp dispells.
1662                status_change_end(bl,SC_BLADESTOP,-1);
1663                status_change_end(bl,SC_BASILICA,-1);
1664                status_change_end(bl,SC_ANKLE,-1);
1665                status_change_end(bl,SC_TRICKDEAD,-1);
1666                status_change_end(bl,SC_BLADESTOP,-1);
1667                status_change_end(bl,SC_RUN,-1);
1668                skill_stop_dancing(bl);
1669                status_change_end(bl,SC_WARM,-1);
1670                status_change_end(bl,SC_DEVOTION,-1);
1671                status_change_end(bl,SC_MARIONETTE,-1);
1672                status_change_end(bl,SC_MARIONETTE2,-1);
1673                status_change_end(bl,SC_CLOSECONFINE,-1);
1674                status_change_end(bl,SC_CLOSECONFINE2,-1);
1675                status_change_end(bl,SC_HIDING,-1);
1676                status_change_end(bl,SC_CLOAKING,-1);
1677                status_change_end(bl,SC_CHASEWALK,-1);
1678                if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)
1679                        status_change_end(bl,SC_GOSPEL,-1);
1680                status_change_end(bl,SC_CHANGE,-1);
1681                status_change_end(bl,SC_MIRACLE,-1);
1682        }
1683
1684        if (bl->type&BL_CHAR) {
1685                skill_unit_move(bl,gettick(),4);
1686                skill_cleartimerskill(bl);
1687        }
1688
1689        switch( bl->type )
1690        {
1691        case BL_PC:
1692        {
1693                struct map_session_data *sd = (struct map_session_data*)bl;
1694
1695                //Leave/reject all invitations.
1696                if(sd->chatID)
1697                        chat_leavechat(sd,0);
1698                if(sd->trade_partner)
1699                        trade_tradecancel(sd);
1700                if(sd->vender_id)
1701                        vending_closevending(sd);
1702                if(sd->state.storage_flag == 1)
1703                        storage_storage_quit(sd,0);
1704                else if (sd->state.storage_flag == 2)
1705                        storage_guild_storage_quit(sd,0);
1706                sd->state.storage_flag = 0; //Force close it when being warped.
1707                if(sd->party_invite>0)
1708                        party_reply_invite(sd,sd->party_invite_account,0);
1709                if(sd->guild_invite>0)
1710                        guild_reply_invite(sd,sd->guild_invite,0);
1711                if(sd->guild_alliance>0)
1712                        guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
1713                if(sd->menuskill_id)
1714                        sd->menuskill_id = sd->menuskill_val = 0;
1715
1716                sd->npc_shopid = 0;
1717                sd->adopt_invite = 0;
1718
1719                if(sd->pvp_timer!=-1) {
1720                        delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
1721                        sd->pvp_timer = -1;
1722                        sd->pvp_rank = 0;
1723                }
1724                if(sd->duel_group > 0)
1725                        duel_leave(sd->duel_group, sd);
1726
1727                if(pc_issit(sd)) {
1728                        pc_setstand(sd);
1729                        skill_sit(sd,0);
1730                }
1731                party_send_dot_remove(sd);//minimap dot fix [Kevin]
1732                guild_send_dot_remove(sd);
1733
1734                if( map[bl->m].users <= 0 || sd->state.debug_remove_map )
1735                {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS]
1736                        if( sd->debug_file == NULL || !(sd->state.debug_remove_map) )
1737                        {
1738                                sd->debug_file = "";
1739                                sd->debug_line = 0;
1740                                sd->debug_func = "";
1741                        }
1742                        ShowDebug("unit_remove_map: unexpected state when removing player AID/CID:%d/%d"
1743                                " (active=%d connect_new=%d rewarp=%d changemap=%d debug_remove_map=%d)"
1744                                " from map=%s (users=%d)."
1745                                " Previous call from %s:%d(%s), current call from %s:%d(%s)."
1746                                " Please report this!!!\n",
1747                                sd->status.account_id, sd->status.char_id,
1748                                sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map,
1749                                map[bl->m].name, map[bl->m].users,
1750                                sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
1751                }
1752                else
1753                if (--map[bl->m].users == 0 && battle_config.dynamic_mobs)      //[Skotlex]
1754                        map_removemobs(bl->m);
1755                sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
1756                sd->debug_file = file;
1757                sd->debug_line = line;
1758                sd->debug_func = func;
1759
1760                break;
1761        }
1762        case BL_MOB:
1763        {
1764                struct mob_data *md = (struct mob_data*)bl;
1765                md->target_id=0;
1766                md->attacked_id=0;
1767                md->state.skillstate= MSS_IDLE;
1768
1769                break;
1770        }
1771        case BL_PET:
1772        {
1773                struct pet_data *pd = (struct pet_data*)bl;
1774                if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) )
1775                {       //If logging out, this is deleted on unit_free
1776                        clif_clearunit_area(bl,clrtype);
1777                        map_delblock(bl);
1778                        unit_free(bl,0);
1779                        map_freeblock_unlock();
1780                        return 0;
1781                }
1782
1783                break;
1784        }
1785        case BL_HOM:
1786        {
1787                struct homun_data *hd = (struct homun_data *) bl;
1788                ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
1789                if(!hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) )
1790                {       //If logging out, this is deleted on unit_free
1791                        clif_emotion(bl, 28) ;  //sob
1792                        clif_clearunit_area(bl,clrtype);
1793                        map_delblock(bl);
1794                        unit_free(bl,0);
1795                        map_freeblock_unlock();
1796                        return 0;
1797                }
1798
1799                break;
1800        }
1801        default: ;// do nothing
1802        }
1803
1804        clif_clearunit_area(bl,clrtype);
1805        map_delblock(bl);
1806        map_freeblock_unlock();
1807        return 1;
1808}
1809
1810void unit_remove_map_pc(struct map_session_data *sd, int clrtype)
1811{
1812        unit_remove_map(&sd->bl,clrtype);
1813
1814        if (clrtype == 3) clrtype = 0; //3 is the warp from logging out, but pets/homunc need to just 'vanish' instead of showing the warping out animation.
1815
1816        if(sd->pd)
1817                unit_remove_map(&sd->pd->bl, clrtype);
1818        if(merc_is_hom_active(sd->hd))
1819                unit_remove_map(&sd->hd->bl, clrtype);
1820}
1821
1822void unit_free_pc(struct map_session_data *sd)
1823{
1824        if (sd->pd) unit_free(&sd->pd->bl,0);
1825        if (sd->hd) unit_free(&sd->hd->bl,0);
1826        unit_free(&sd->bl,3);
1827}
1828
1829/*==========================================
1830 * Function to free all related resources to the bl
1831 * if unit is on map, it is removed using the clrtype specified
1832 * If clrtype is <0, no saving is performed. This is only for non-authed
1833 * objects that shouldn't be on a map yet.
1834 *------------------------------------------*/
1835int unit_free(struct block_list *bl, int clrtype)
1836{
1837        struct unit_data *ud = unit_bl2ud( bl );
1838        nullpo_retr(0, ud);
1839
1840        map_freeblock_lock();
1841        if( bl->prev )  //Players are supposed to logout with a "warp" effect.
1842                unit_remove_map(bl, clrtype);
1843       
1844        if( bl->type == BL_PC ) {
1845                struct map_session_data *sd = (struct map_session_data*)bl;
1846                if(status_isdead(bl))
1847                        pc_setrestartvalue(sd,2);
1848
1849                pc_delinvincibletimer(sd);
1850       
1851                pc_autoscript_clear(sd->autoscript, ARRAYLENGTH(sd->autoscript));
1852                pc_autoscript_clear(sd->autoscript2, ARRAYLENGTH(sd->autoscript2));
1853
1854                if (sd->followtimer != -1)
1855                        pc_stop_following(sd);
1856                       
1857                if(sd->duel_invite > 0)
1858                        duel_reject(sd->duel_invite, sd);
1859       
1860                // Notify friends that this char logged out. [Skotlex]
1861                map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);
1862                party_send_logout(sd);
1863                guild_send_memberinfoshort(sd,0);
1864                pc_cleareventtimer(sd);
1865                pc_delspiritball(sd,sd->spiritball,1);
1866
1867                if(sd->reg)
1868                {       //Double logout already freed pointer fix... [Skotlex]
1869                        aFree(sd->reg);
1870                        sd->reg = NULL;
1871                        sd->reg_num = 0;
1872                }
1873                if(sd->regstr)
1874                {
1875                        int i;
1876                        for( i = 0; i < sd->regstr_num; ++i )
1877                                if( sd->regstr[i].data )
1878                                        aFree(sd->regstr[i].data);
1879                        aFree(sd->regstr);
1880                        sd->regstr = NULL;
1881                        sd->regstr_num = 0;
1882                }
1883
1884                //Tell the script to end, not delete it, it will free itself when necessary [Kevin]
1885                if (sd->st) {
1886                        sd->st->rid = 0;
1887                        sd->st->state = 2;
1888                }
1889        } else if( bl->type == BL_PET ) {
1890                struct pet_data *pd = (struct pet_data*)bl;
1891                struct map_session_data *sd = pd->msd;
1892                pet_hungry_timer_delete(pd);
1893                if (pd->a_skill)
1894                {
1895                        aFree(pd->a_skill);
1896                        pd->a_skill = NULL;
1897                }
1898                if (pd->s_skill)
1899                {
1900                        if (pd->s_skill->timer != -1) {
1901                                if (pd->s_skill->id)
1902                                        delete_timer(pd->s_skill->timer, pet_skill_support_timer);
1903                                else
1904                                        delete_timer(pd->s_skill->timer, pet_heal_timer);
1905                        }
1906                        aFree(pd->s_skill);
1907                        pd->s_skill = NULL;
1908                }
1909                if(pd->recovery)
1910                {
1911                        if(pd->recovery->timer != -1)
1912                                delete_timer(pd->recovery->timer, pet_recovery_timer);
1913                        aFree(pd->recovery);
1914                        pd->recovery = NULL;
1915                }
1916                if(pd->bonus)
1917                {
1918                        if (pd->bonus->timer != -1)
1919                                delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
1920                        aFree(pd->bonus);
1921                        pd->bonus = NULL;
1922                }
1923                if (pd->loot)
1924                {
1925                        pet_lootitem_drop(pd,sd);
1926                        if (pd->loot->item)
1927                                aFree(pd->loot->item);
1928                        aFree (pd->loot);
1929                        pd->loot = NULL;
1930                }
1931                if (clrtype >= 0) {
1932                        if(pd->pet.intimate > 0)
1933                                intif_save_petdata(pd->pet.account_id,&pd->pet);
1934                        else
1935                        {       //Remove pet.
1936                                intif_delete_petdata(pd->pet.pet_id);
1937                                if (sd) sd->status.pet_id = 0;
1938                        }
1939                }
1940                if (sd) sd->pd = NULL;
1941        } else if(bl->type == BL_MOB) {
1942                struct mob_data *md = (struct mob_data*)bl;
1943                if(md->deletetimer!=-1) {
1944                        delete_timer(md->deletetimer,mob_timer_delete);
1945                        md->deletetimer=-1;
1946                }
1947                if(md->lootitem) {
1948                        aFree(md->lootitem);
1949                        md->lootitem=NULL;
1950                }
1951                if( md->guardian_data )
1952                {
1953                        struct guild_castle* gc = md->guardian_data->castle;
1954                        if( md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS )
1955                        {
1956                                gc->guardian[md->guardian_data->number].id = 0;
1957                        }
1958                        else
1959                        {
1960                                int i;
1961                                ARR_FIND(0, gc->temp_guardians_max, i, gc->temp_guardians[i] == md->bl.id);
1962                                if( i < gc->temp_guardians_max )
1963                                        gc->temp_guardians[i] = 0;
1964                        }
1965                        aFree(md->guardian_data);
1966                        md->guardian_data = NULL;
1967                }
1968                if(md->spawn)
1969                {
1970                        md->spawn->active--;
1971
1972                        if( !md->spawn->state.dynamic )
1973                        {// permanently remove the mob
1974                                if( --md->spawn->num == 0 )
1975                                {// Last freed mob is responsible for deallocating the group's spawn data.
1976                                        aFree(md->spawn);
1977                                        md->spawn = NULL;
1978                                }
1979                        }
1980                }
1981                if(md->base_status) {
1982                        aFree(md->base_status);
1983                        md->base_status = NULL;
1984                }
1985                if(mob_is_clone(md->class_))
1986                        mob_clone_delete(md->class_);
1987        } else if(bl->type == BL_HOM) {
1988                struct homun_data *hd = (TBL_HOM*)bl;
1989                struct map_session_data *sd = hd->master;
1990                // Desactive timers
1991                merc_hom_hungry_timer_delete(hd);
1992                if (clrtype >= 0) {
1993                        if (hd->homunculus.intimacy > 0)
1994                                merc_save(hd); 
1995                        else
1996                        {
1997                                intif_homunculus_requestdelete(hd->homunculus.hom_id);
1998                                if (sd) sd->status.hom_id = 0;
1999                        }
2000                }
2001                if(sd) sd->hd = NULL;
2002        }
2003
2004        skill_clear_unitgroup(bl);
2005        status_change_clear(bl,1);
2006        map_deliddb(bl);
2007        if (bl->type != BL_PC) //Players are handled by map_quit
2008                map_freeblock(bl);
2009        map_freeblock_unlock();
2010        return 0;
2011}
2012
2013int do_init_unit(void)
2014{
2015        add_timer_func_list(unit_attack_timer,  "unit_attack_timer");
2016        add_timer_func_list(unit_walktoxy_timer,"unit_walktoxy_timer");
2017        add_timer_func_list(unit_walktobl_sub, "unit_walktobl_sub");
2018        add_timer_func_list(unit_delay_walktoxy_timer,"unit_delay_walktoxy_timer");
2019        return 0;
2020}
2021
2022int do_final_unit(void)
2023{
2024        // nothing to do
2025        return 0;
2026}
Note: See TracBrowser for help on using the browser.