root/src/map/unit.c @ 6

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