root/src/map/unit.c @ 23

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