root/src/map/unit.c @ 18

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