root/src/map/unit.c @ 24

Revision 24, 56.8 kB (checked in by jinshiro, 17 years ago)
RevLine 
[1]1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/showmsg.h"
5#include "../common/timer.h"
6#include "../common/nullpo.h"
7#include "../common/db.h"
8#include "../common/malloc.h"
9#include "unit.h"
10#include "map.h"
11#include "path.h"
12#include "pc.h"
13#include "mob.h"
14#include "pet.h"
15#include "mercenary.h"
16#include "skill.h"
17#include "clif.h"
18#include "npc.h"
19#include "guild.h"
20#include "status.h"
21#include "battle.h"
22#include "chat.h"
23#include "trade.h"
24#include "vending.h"
25#include "party.h"
26#include "intif.h"
27#include "chrif.h"
28#include "script.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34
35const short dirx[8]={0,-1,-1,-1,0,1,1,1};
36const short diry[8]={1,1,0,-1,-1,-1,0,1};
37
38struct unit_data* unit_bl2ud(struct block_list *bl)
39{
40        if( bl == NULL) return NULL;
41        if( bl->type == BL_PC)  return &((struct map_session_data*)bl)->ud;
42        if( bl->type == BL_MOB) return &((struct mob_data*)bl)->ud;
43        if( bl->type == BL_PET) return &((struct pet_data*)bl)->ud;
44        if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud;
45        if( bl->type == BL_HOM) return &((struct homun_data*)bl)->ud;   //[orn]
46        return NULL;
47}
48
49static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data);
50static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr data);
51
52int unit_walktoxy_sub(struct block_list *bl)
53{
54        int i;
55        struct walkpath_data wpd;
56        struct unit_data *ud = NULL;
57
58        nullpo_retr(1, bl);
59        ud = unit_bl2ud(bl);
60        if(ud == NULL) return 0;
61
62        if( !path_search(&wpd,bl->m,bl->x,bl->y,ud->to_x,ud->to_y,ud->state.walk_easy,CELL_CHKNOPASS) )
63                return 0;
64
65        memcpy(&ud->walkpath,&wpd,sizeof(wpd));
66       
67        if (ud->target && ud->chaserange>1) {
68                //Generally speaking, the walk path is already to an adjacent tile
69                //so we only need to shorten the path if the range is greater than 1.
70                int dir;
71                //Trim the last part of the path to account for range,
72                //but always move at least one cell when requested to move.
73                for (i = ud->chaserange*10; i > 0 && ud->walkpath.path_len>1;) {
74                   ud->walkpath.path_len--;
75                        dir = ud->walkpath.path[ud->walkpath.path_len];
76                        if(dir&1)
77                                i-=14;
78                        else
79                                i-=10;
80                        ud->to_x -= dirx[dir];
81                        ud->to_y -= diry[dir];
82                }
83        }
84
85        ud->state.change_walk_target=0;
86
87        if (bl->type == BL_PC) {
88                ((TBL_PC *)bl)->head_dir = 0;
89                clif_walkok((TBL_PC*)bl);
90        }
91        clif_move(ud);
92
93        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
94                i = -1;
95        else if(ud->walkpath.path[ud->walkpath.path_pos]&1)
96                i = status_get_speed(bl)*14/10;
97        else
98                i = status_get_speed(bl);
99        if( i > 0)
100                ud->walktimer = add_timer(gettick()+i,unit_walktoxy_timer,bl->id,i);
101        return 1;
102}
103
104static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr data)
105{
106        int i;
107        int x,y,dx,dy;
108        uint8 dir;
109        struct block_list       *bl;
110        struct map_session_data *sd;
111        struct mob_data         *md;
112        struct unit_data        *ud;
113
114        bl = map_id2bl(id);
115        if(bl == NULL)
116                return 0;
117        sd = BL_CAST(BL_PC, bl);
118        md = BL_CAST(BL_MOB, bl);
119        ud = unit_bl2ud(bl);
120       
121        if(ud == NULL) return 0;
122
123        if(ud->walktimer != tid){
124                ShowError("unit_walk_timer mismatch %d != %d\n",ud->walktimer,tid);
125                return 0;
126        }
127        ud->walktimer=-1;
128        if( bl->prev == NULL ) return 0; // block_list ‚©‚甲‚¯‚Ä‚¢‚é‚̂ňړ®’âŽ~‚·‚é
129
130        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
131                return 0;
132
133        if(ud->walkpath.path[ud->walkpath.path_pos]>=8)
134                return 1;
135        x = bl->x;
136        y = bl->y;
137
138        dir = ud->walkpath.path[ud->walkpath.path_pos];
139        ud->dir = dir;
140
141        dx = dirx[(int)dir];
142        dy = diry[(int)dir];
143
144        if(map_getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS))
145                return unit_walktoxy_sub(bl);
146       
147        // ƒoƒVƒŠƒJ”»’è
148
149        map_foreachinmovearea(clif_outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
150
151        x += dx;
152        y += dy;
153        map_moveblock(bl, x, y, tick);
154        ud->walk_count++; //walked cell counter, to be used for walk-triggered skills. [Skotlex]
155
156        if (bl->x != x || bl->y != y || ud->walktimer != -1)
157                return 0; //map_moveblock has altered the object beyond what we expected (moved/warped it)
158
159        ud->walktimer = 1;
160        map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl);
161        ud->walktimer = -1;
162       
163        if(sd) {
164                if(map_getcell(bl->m,x,y,CELL_CHKNPC)) {
165                        npc_touch_areanpc(sd,bl->m,x,y);
166                        if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
167                                return 0;
168                } else
169                        sd->areanpc_id=0;
170                if (sd->state.gmaster_flag &&
171                        (battle_config.guild_aura&(agit_flag?2:1)) &&
172                        (battle_config.guild_aura&(map_flag_gvg2(bl->m)?8:4))
173                )
174                { //Guild Aura: Likely needs to be recoded, this method seems inefficient.
175                        struct guild *g = sd->state.gmaster_flag;
176                        int skill, strvit= 0, agidex = 0;
177                        if ((skill = guild_checkskill(g, GD_LEADERSHIP)) > 0) strvit |= (skill&0xFFFF)<<16;
178                        if ((skill = guild_checkskill(g, GD_GLORYWOUNDS)) > 0) strvit |= (skill&0xFFFF);
179                        if ((skill = guild_checkskill(g, GD_SOULCOLD)) > 0) agidex |= (skill&0xFFFF)<<16;
180                        if ((skill = guild_checkskill(g, GD_HAWKEYES)) > 0) agidex |= skill&0xFFFF;
181                        if (strvit || agidex)
182                                map_foreachinrange(skill_guildaura_sub, bl,2, BL_PC,
183                                        bl->id, sd->status.guild_id, strvit, agidex);
184                }
185        } else if (md) {
186                if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) {
187                        if( npc_touch_areanpc2(md) ) return 0; // Warped
188                } else
189                        md->areanpc_id = 0;
190                if (md->min_chase > md->db->range3) md->min_chase--;
191                //Walk skills are triggered regardless of target due to the idle-walk mob state.
192                //But avoid triggering on stop-walk calls.
193                if(tid != -1 &&
194                        !(ud->walk_count%WALK_SKILL_INTERVAL) &&
195                        mobskill_use(md, tick, -1))
196                {
197                        if (!(ud->skillid == NPC_SELFDESTRUCTION && ud->skilltimer != -1))
198                        {       //Skill used, abort walking
199                                clif_fixpos(bl); //Fix position as walk has been cancelled.
200                                return 0;
201                        }
202                        //Resend walk packet for proper Self Destruction display.
203                        clif_move(ud);
204                }
205        }
206
207        if(tid == -1) //A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant.
208                return 0;
209               
210        if(ud->state.change_walk_target)
211                return unit_walktoxy_sub(bl);
212
213        ud->walkpath.path_pos++;
214        if(ud->walkpath.path_pos>=ud->walkpath.path_len)
215                i = -1;
216        else if(ud->walkpath.path[ud->walkpath.path_pos]&1)
217                i = status_get_speed(bl)*14/10;
218        else
219                i = status_get_speed(bl);
220
221        if( md && map_getcell(bl->m,x,y,CELL_CHKBASILICA) ) {
222                skill_blown(bl,bl,2,unit_getdir(bl),0);
223                clif_fixpos(bl);
224        }
225       
226        if(i > 0)
227                ud->walktimer = add_timer(tick+i,unit_walktoxy_timer,id,i);
228        else if(ud->state.running) {
229                //Keep trying to run.
230                if (!unit_run(bl))
231                        ud->state.running = 0;
232        }
233        else if (ud->target) {
234                //Update target trajectory.
235                struct block_list *tbl = map_id2bl(ud->target);
236                if (!tbl || !status_check_visibility(bl, tbl)) {        //Cancel chase.
237                        ud->to_x = bl->x;
238                        ud->to_y = bl->y;
239                        if (tbl && bl->type == BL_MOB) //See if the mob can do a warp chase.
240                                mob_warpchase((TBL_MOB*)bl, tbl);
241                        return 0;
242                }
243                if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange))
244                {       //Reached destination.
245                        if (ud->state.attack_continue)
246                        {       //Aegis uses one before every attack, we should
247                                //only need this one for syncing purposes. [Skotlex]
248                                clif_fixpos(bl);
249                                unit_attack(bl, tbl->id, ud->state.attack_continue);
250                        }
251                } else { //Update chase-path
252                        unit_walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue?2:0));
253                        return 0;
254                }
255        }
256        else {  //Stopped walking. Update to_x and to_y to current location [Skotlex]
257                ud->to_x = bl->x;
258                ud->to_y = bl->y;
259                if(md && md->nd) // Tell the script engine we've finished walking (for AI pathfinding)
260                        mob_script_callback(md, NULL, CALLBACK_WALKACK);
261        }
262        return 0;
263}
264
265static int unit_delay_walktoxy_timer(int tid, unsigned int tick, int id, intptr data)
266{
267        struct block_list *bl = map_id2bl(id);
268
269        if (!bl || bl->prev == NULL)
270                return 0;
271        unit_walktoxy(bl, (short)((data>>16)&0xffff), (short)(data&0xffff), 0);
272        return 1;
273}
274
275//flag parameter:
276//&1 -> 1/0 = easy/hard
277//&2 -> force walking
278//&4 -> Delay walking if the reason you can't walk is the canwalk delay
279int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
280{
281        struct unit_data* ud = NULL;
282        struct status_change* sc = NULL;
283
284        nullpo_retr(0, bl);
285       
286        ud = unit_bl2ud(bl);
287       
288        if( ud == NULL) return 0;
289
290        if (flag&4 && DIFF_TICK(ud->canmove_tick, gettick()) > 0 &&
291                DIFF_TICK(ud->canmove_tick, gettick()) < 2000)
292        {       // Delay walking command. [Skotlex]
293                add_timer(ud->canmove_tick+1, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF));
294                return 1;
295        }
296
297        if(!(flag&2) && (!(status_get_mode(bl)&MD_CANMOVE) || !unit_can_move(bl)))
298                return 0;
299       
300        ud->state.walk_easy = flag&1;
301        ud->target = 0;
302        ud->to_x = x;
303        ud->to_y = y;
304       
305        sc = status_get_sc(bl);
306        if (sc && sc->data[SC_CONFUSION]) //Randomize the target position
307                map_random_dir(bl, &ud->to_x, &ud->to_y);
308
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);
[19]376        if (sc && sc->data[SC_FEAR]) //Flee from my target [Brainstorm]
[24]377                unit_escape(bl, tbl, 15);
[19]378
[1]379        if(ud->walktimer != -1) {
380                ud->state.change_walk_target = 1;
381                set_mobstate(bl, flag&2);
382                return 1;
383        }
384
385        if(DIFF_TICK(ud->canmove_tick, gettick()) > 0)
386        {       //Can't move, wait a bit before invoking the movement.
387                add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->target);
388                return 1;
389        }
390
391        if(!unit_can_move(bl))
392                return 0;
393
394        if(ud->attacktimer != -1) {
395                delete_timer( ud->attacktimer, unit_attack_timer );
396                ud->attacktimer = -1;
397        }
398
399        if (unit_walktoxy_sub(bl)) {
400                set_mobstate(bl, flag&2);
401                return 1;
402        }
403        return 0;
404}
405#undef set_mobstate
406
407int unit_run(struct block_list *bl)
408{
409        struct status_change *sc = status_get_sc(bl);
410        short to_x,to_y,dir_x,dir_y;
411        int lv;
412        int i;
413
414        if (!(sc && sc->data[SC_RUN]))
415                return 0;
416       
417        if (!unit_can_move(bl)) {
418                status_change_end(bl,SC_RUN,-1);
419                return 0;
420        }
421       
422        lv = sc->data[SC_RUN]->val1;
423        dir_x = dirx[sc->data[SC_RUN]->val2];
424        dir_y = diry[sc->data[SC_RUN]->val2];
425
426        // determine destination cell
427        to_x = bl->x;
428        to_y = bl->y;
429        for(i=0;i<AREA_SIZE;i++)
430        {
431                if(!map_getcell(bl->m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
432                        break;
433
434                //if sprinting and there's a PC/Mob/NPC, block the path [Kevin]
435                if(sc->data[SC_RUN] && map_count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC))
436                        break;
437                       
438                to_x += dir_x;
439                to_y += dir_y;
440        }
441
442        if(to_x == bl->x && to_y == bl->y) {
443                //If you can't run forward, you must be next to a wall, so bounce back. [Skotlex]
444                clif_status_change(bl, SI_BUMP, 1);
445
446                //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin]
447                unit_bl2ud(bl)->state.running = 0;
448                status_change_end(bl,SC_RUN,-1);
449
450                skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0);
451                clif_fixpos(bl); //Why is a clif_slide (skill_blown) AND a fixpos needed? Ask Aegis.
452                clif_status_change(bl, SI_BUMP, 0);
453                return 0;
454        }
455        if (unit_walktoxy(bl, to_x, to_y, 1))
456                return 1;
457        //There must be an obstacle nearby. Attempt walking one cell at a time.
458        do {
459                to_x -= dir_x;
460                to_y -= dir_y;
461        } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1));
462        if (i==0) {
463                // copy-paste from above
464                clif_status_change(bl, SI_BUMP, 1);
465
466                //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin]
467                unit_bl2ud(bl)->state.running = 0;
468                status_change_end(bl,SC_RUN,-1);
469
470                skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0);
471                clif_fixpos(bl);
472                clif_status_change(bl, SI_BUMP, 0);
473                return 0;
474        }
475        return 1;
476}
477
478//Makes bl attempt to run dist cells away from target. Uses hard-paths.
479int unit_escape(struct block_list *bl, struct block_list *target, short dist)
480{
481        int dir = map_calc_dir(target, bl->x, bl->y);
482        while( dist > 0 && map_getcell(bl->m, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], CELL_CHKNOREACH) )
483                dist--;
484        return ( dist > 0 && unit_walktoxy(bl, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], 0) );
485}
486
487//Instant warp function.
488int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath)
489{
490        short dx,dy;
491        uint8 dir;
492        struct unit_data        *ud = NULL;
493        struct map_session_data *sd = NULL;
494
495        nullpo_retr(0, bl);
496        sd = BL_CAST(BL_PC, bl);
497        ud = unit_bl2ud(bl);
498
499        if( ud == NULL) return 0;
500
501        unit_stop_walking(bl,1);
502        unit_stop_attack(bl);
503
504        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)) )
505                return 0; // unreachable
506
507        dir = map_calc_dir(bl, dst_x,dst_y);
508        ud->dir = dir;
509
510        dx = dst_x - bl->x;
511        dy = dst_y - bl->y;
512
513        map_foreachinmovearea(clif_outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
514
515        map_moveblock(bl, dst_x, dst_y, gettick());
516       
517        ud->walktimer = 1;
518        map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl);
519        ud->walktimer = -1;
520               
521        if(sd) {
522                if(map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC)) {
523                        npc_touch_areanpc(sd,bl->m,bl->x,bl->y);
524                        if (bl->prev == NULL) //Script could have warped char, abort remaining of the function.
525                                return 0;
526                } else
527                        sd->areanpc_id=0;
528                if(sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0)
529                {       //Check if pet needs to be teleported. [Skotlex]
530                        int flag = 0;
531                        struct block_list* bl = &sd->pd->bl;
532                        if( !checkpath && !path_search(NULL,bl->m,bl->x,bl->y,dst_x,dst_y,0,CELL_CHKNOPASS) )
533                                flag = 1;
534                        else if (!check_distance_bl(&sd->bl, bl, AREA_SIZE)) //Too far, teleport.
535                                flag = 2;
536                        if (flag) {
537                                unit_movepos(bl,sd->bl.x,sd->bl.y, 0, 0);
538                                clif_slide(bl,bl->x,bl->y);
539                        }
540                }
541        }
542        return 1;
543}
544
545int unit_setdir(struct block_list *bl,unsigned char dir)
546{
547        struct unit_data *ud;
548        nullpo_retr( 0, bl );
549        ud = unit_bl2ud(bl);
550        if (!ud) return 0;
551        ud->dir = dir;
552        if (bl->type == BL_PC) 
553                ((TBL_PC *)bl)->head_dir = 0;
554        clif_changed_dir(bl, AREA);
555        return 0;
556}
557
558uint8 unit_getdir(struct block_list *bl)
559{
560        struct unit_data *ud;
561        nullpo_retr( 0, bl );
562        ud = unit_bl2ud(bl);
563        if (!ud) return 0;
564        return ud->dir;
565}
566
567//Warps a unit/ud to a given map/position.
568//In the case of players, pc_setpos is used.
569//it respects the no warp flags, so it is safe to call this without doing nowarpto/nowarp checks.
570int unit_warp(struct block_list *bl,short m,short x,short y,int type)
571{
572        struct unit_data *ud;
573        nullpo_retr(0, bl);
574        ud = unit_bl2ud(bl);
575       
576        if(bl->prev==NULL || !ud)
577                return 1;
578
579        if (type < 0 || type == 1)
580                //Type 1 is invalid, since you shouldn't warp a bl with the "death"
581                //animation, it messes up with unit_remove_map! [Skotlex]
582                return 1;
583       
584        if( m<0 ) m=bl->m;
585       
586        switch (bl->type) {
587                case BL_MOB:
588                        if (map[bl->m].flag.monster_noteleport)
589                                return 1;
590                        if (m != bl->m && map[m].flag.nobranch && battle_config.mob_warp&4)
591                                return 1;
592                        break;
593                case BL_PC:
594                        if (map[bl->m].flag.noteleport)
595                                return 1;
596                        break;
597        }
598       
599        if (x<0 || y<0)
600        {       //Random map position.
601                if (!map_search_freecell(NULL, m, &x, &y, -1, -1, 1)) {
602                        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);
603                        return 2;
604                       
605                }
606        } else if (map_getcell(m,x,y,CELL_CHKNOREACH))
607        {       //Invalid target cell
608                ShowWarning("unit_warp: Specified non-walkable target cell: %d (%s) at [%d,%d]\n", m, map[m].name, x,y);
609               
610                if (!map_search_freecell(NULL, m, &x, &y, 4, 4, 1))
611                {       //Can't find a nearby cell
612                        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);
613                        return 2;
614                }
615        }
616
617        if (bl->type == BL_PC) //Use pc_setpos
618                return pc_setpos((TBL_PC*)bl, map_id2index(m), x, y, type);
619       
620        if (!unit_remove_map(bl, type))
621                return 3;
622       
623        if (bl->m != m && battle_config.clear_unit_onwarp &&
624                battle_config.clear_unit_onwarp&bl->type)
625                skill_clear_unitgroup(bl);
626
627        bl->x=ud->to_x=x;
628        bl->y=ud->to_y=y;
629        bl->m=m;
630
631        map_addblock(bl);
632        clif_spawn(bl);
633        skill_unit_move(bl,gettick(),1);
634
635        if(bl->type == BL_MOB){
636                TBL_MOB *md = (TBL_MOB *)bl;
637                if(md->nd) // Tell the script engine we've warped
638                        mob_script_callback(md, NULL, CALLBACK_WARPACK);
639        }
640        return 0;
641}
642
643/*==========================================
644 * •às’âŽ~
645 *------------------------------------------*/
646int unit_stop_walking(struct block_list *bl,int type)
647{
648        struct unit_data *ud;
649        struct TimerData *data;
650        unsigned int tick;
651        nullpo_retr(0, bl);
652
653        ud = unit_bl2ud(bl);
654        if(!ud || ud->walktimer == -1)
655                return 0;
656        //NOTE: We are using timer data after deleting it because we know the
657        //delete_timer function does not messes with it. If the function's
658        //behaviour changes in the future, this code could break!
659        data = get_timer(ud->walktimer);
660        delete_timer(ud->walktimer, unit_walktoxy_timer);
661        ud->walktimer = -1;
662        ud->state.change_walk_target = 0;
663        tick = gettick();
664        if ((type&0x02 && !ud->walkpath.path_pos) //Force moving at least one cell.
665                || (data && DIFF_TICK(data->tick, tick) <= data->data/2)) //Enough time has passed to cover half-cell
666        {       
667                ud->walkpath.path_len = ud->walkpath.path_pos+1;
668                unit_walktoxy_timer(-1, tick, bl->id, ud->walkpath.path_pos);
669        }
670
671        if(type&0x01)
672                clif_fixpos(bl);
673       
674        ud->walkpath.path_len = 0;
675        ud->walkpath.path_pos = 0;
676        ud->to_x = bl->x;
677        ud->to_y = bl->y;
678        if(bl->type == BL_PET && type&~0xff)
679                ud->canmove_tick = gettick() + (type>>8);
680
681        //Readded, the check in unit_set_walkdelay means dmg during running won't fall through to this place in code [Kevin]
682        if (ud->state.running)
683                status_change_end(bl, SC_RUN, -1);
684        return 1;
685}
686
687int unit_skilluse_id(struct block_list *src, int target_id, short skill_num, short skill_lv)
688{
689        if(skill_num < 0) return 0;
690
691        return unit_skilluse_id2(
692                src, target_id, skill_num, skill_lv,
693                skill_castfix(src, skill_num, skill_lv),
694                skill_get_castcancel(skill_num)
695        );
696}
697
698int unit_is_walking(struct block_list *bl)
699{
700        struct unit_data *ud = unit_bl2ud(bl);
701        nullpo_retr(0, bl);
702        if(!ud) return 0;
703        return (ud->walktimer != -1);
704}
705
706/*==========================================
707 * Determines if the bl can move based on status changes. [Skotlex]
708 *------------------------------------------*/
709int unit_can_move(struct block_list *bl)
710{
711        struct map_session_data *sd;
712        struct unit_data *ud;
713        struct status_change *sc;
714       
715        nullpo_retr(0, bl);
716        ud = unit_bl2ud(bl);
717        sc = status_get_sc(bl);
718        sd = BL_CAST(BL_PC, bl);
719       
720        if (!ud)
721                return 0;
722       
723        if (ud->skilltimer != -1 && (!sd || !pc_checkskill(sd, SA_FREECAST) || skill_get_inf2(ud->skillid)&INF2_GUILD_SKILL))
724                return 0; // prevent moving while casting
725       
726        if (DIFF_TICK(ud->canmove_tick, gettick()) > 0)
727                return 0;
728       
729        if (sd && (
730                pc_issit(sd) ||
731                sd->vender_id ||
732                sd->state.blockedmove
733        ))
734                return 0; //Can't move
735       
736        if (sc) {
737                if (sc->opt1 > 0 && sc->opt1 != OPT1_STONEWAIT)
738                        return 0;
739
740                if ((sc->option & OPTION_HIDE) && (!sd || pc_checkskill(sd, RG_TUNNELDRIVE) <= 0))
741                        return 0;
742
743                if (sc->count && (
744                        sc->data[SC_ANKLE]
745                        || sc->data[SC_AUTOCOUNTER]
746                        || sc->data[SC_TRICKDEAD]
747                        || sc->data[SC_BLADESTOP]
748                        || sc->data[SC_BLADESTOP_WAIT]
749                        || sc->data[SC_SPIDERWEB]
750                        || (sc->data[SC_DANCING] && sc->data[SC_DANCING]->val4 && (
751                                !sc->data[SC_LONGING] ||
752                                (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT ||
753                                (sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE
754                        ))
755                        || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)       // cannot move while gospel is in effect
756                        || sc->data[SC_STOP]
757                        || sc->data[SC_CLOSECONFINE]
758                        || sc->data[SC_CLOSECONFINE2]
759                        || (sc->data[SC_CLOAKING] && //Need wall at level 1-2
760                                sc->data[SC_CLOAKING]->val1 < 3 && !(sc->data[SC_CLOAKING]->val4&1))
761                        || sc->data[SC_MADNESSCANCEL]
762                        || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
763                ))
764                        return 0;
765        }
766        return 1;
767}
768
769
770/*==========================================
771 * Resume running after a walk delay
772 *------------------------------------------*/
773
774int unit_resume_running(int tid, unsigned int tick, int id, intptr data)
775{
776
777        struct unit_data *ud = (struct unit_data *)data;
778        TBL_PC * sd = map_id2sd(id);
779
780        clif_skill_nodamage(ud->bl,ud->bl,TK_RUN,ud->skilllv,
781                        sc_start4(ud->bl,status_skill2sc(TK_RUN),100,ud->skilllv,unit_getdir(ud->bl),0,0,0));
782
783        if (sd) clif_walkok(sd);
784
785        return 0;
786
787}
788
789
790/*==========================================
791 * Applies walk delay to character, considering that
792 * if type is 0, this is a damage induced delay: if previous delay is active, do not change it.
793 * if type is 1, this is a skill induced delay: walk-delay may only be increased, not decreased.
794 *------------------------------------------*/
795int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int type)
796{
797        struct unit_data *ud = unit_bl2ud(bl);
798        if (delay <= 0 || !ud) return 0;
799       
800        if (type) {
801                if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
802                        return 0;
803        } else {
804                if (DIFF_TICK(ud->canmove_tick, tick) > 0)
805                        return 0;
806        }
807        ud->canmove_tick = tick + delay;
808        if (ud->walktimer != -1)
809        {       //Stop walking, if chasing, readjust timers.
810                if (delay == 1)
811                {       //Minimal delay (walk-delay) disabled. Just stop walking.
812                        unit_stop_walking(bl,0);
813                } else {
814                        //Resume running after can move again [Kevin]
815                        if(ud->state.running)
816                        {
817                                add_timer(ud->canmove_tick, unit_resume_running, bl->id, (int)ud);
818                        }
819                        else
820                        {
821                                unit_stop_walking(bl,2);
822                                if(ud->target)
823                                        add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->target);
824                        }
825                }
826        }
827        return 1;
828}
829
830int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, short skill_lv, int casttime, int castcancel)
831{
832        struct unit_data *ud;
833        struct status_data *tstatus;
834        struct status_change *sc;
835        struct map_session_data *sd = NULL;
836        struct block_list * target = NULL;
837        unsigned int tick = gettick();
838        int temp;
839
840        nullpo_retr(0, src);
841        if(status_isdead(src))
842                return 0; // Ž€‚ñ‚Å‚¢‚È‚¢‚©
843
844        sd = BL_CAST(BL_PC, src);
845        ud = unit_bl2ud(src);
846
847        if(ud == NULL) return 0;
848        sc = status_get_sc(src);       
849        if (sc && !sc->count)
850                sc = NULL; //Unneeded
851        //temp: used to signal combo-skills right now.
852        temp = (target_id == src->id && !(sd && sd->state.skill_flag)
853                && skill_get_inf(skill_num)&INF_SELF_SKILL
854                && skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF);
855        if (temp)
856                target_id = ud->target; //Auto-select skills. [Skotlex]
857
858        if (sd) {
859                //Target_id checking.
860                if(skillnotok(skill_num, sd)) // [MouseJstr]
861                        return 0;
862
863                mob_ksprotected(src, map_id2bl(target_id));
864
865                switch(skill_num)
866                {       //Check for skills that auto-select target
867                case MO_CHAINCOMBO:
868                        if (sc && sc->data[SC_BLADESTOP]){
869                                if ((target=(struct block_list *)sc->data[SC_BLADESTOP]->val4) == NULL)
870                                        return 0;
871                        }
872                        break;
873                case TK_JUMPKICK:
874                case TK_COUNTER:
875                case HT_POWER:
876                        if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_num)
877                                target_id = sc->data[SC_COMBO]->val2;
878                        break;
879                case WE_MALE:
880                case WE_FEMALE:
881                        if (!sd->status.partner_id)
882                                return 0;
883                        target = (struct block_list*)map_charid2sd(sd->status.partner_id);
884                        if (!target) {
885                                clif_skill_fail(sd,skill_num,0,0);
886                                return 0;
887                        }
888                        break;
889                }
890                if (target)
891                        target_id = target->id;
892        }
893        if (src->type==BL_HOM)
894        switch(skill_num)
895        { //Homun-auto-target skills.
896                case HLIF_HEAL:
897                case HLIF_AVOID:
898                case HAMI_DEFENCE:
899                case HAMI_CASTLE:
900                        target = battle_get_master(src);
901                        if (!target) return 0;
902                        target_id = target->id;
903        }
904
905        if( !target ) // choose default target
906                target = map_id2bl(target_id);
907
908        if( !target || src->m != target->m || !src->prev || !target->prev )
909                return 0;
910
911        //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex]
912        if(ud->skilltimer != -1 && skill_num != SA_CASTCANCEL)
913                return 0;
914
915        if(skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF && src->id == target_id)
916                return 0;
917
918        if(!status_check_skilluse(src, target, skill_num, 0))
919                return 0;
920
921        tstatus = status_get_status_data(target);
922        //’Œ‘O‚̃XƒLƒ‹ó‹µ‚Ì‹L˜^
923        if(sd) {
924                switch(skill_num){
925                case SA_CASTCANCEL:
926                        if(ud->skillid != skill_num){
927                                sd->skillid_old = ud->skillid;
928                                sd->skilllv_old = ud->skilllv;
929                        }
930                        break;
931                case BD_ENCORE:
932                        //Prevent using the dance skill if you no longer have the skill in your tree.
933                        if(!sd->skillid_dance || pc_checkskill(sd,sd->skillid_dance)<=0){
934                                clif_skill_fail(sd,skill_num,0,0);
935                                return 0;
936                        }
937                        sd->skillid_old = skill_num;
938                        break;
939                case BD_LULLABY:
940                case BD_RICHMANKIM:
941                case BD_ETERNALCHAOS:
942                case BD_DRUMBATTLEFIELD:
943                case BD_RINGNIBELUNGEN:
944                case BD_ROKISWEIL:
945                case BD_INTOABYSS:
946                case BD_SIEGFRIED:
947                case CG_MOONLIT:
948                        if (skill_check_pc_partner(sd, skill_num, &skill_lv, 1, 0) < 1)
949                        {
950                                clif_skill_fail(sd,skill_num,0,0);
951                                return 0;
952                        }
953                        break;
954                }
955                if (!skill_check_condition(sd, skill_num, skill_lv, 0))
956                        return 0;
957        }
958        //TODO: Add type-independant skill_check_condition function.
959        if (src->type == BL_MOB) {
960                switch (skill_num) {
961                        case NPC_SUMMONSLAVE:
962                        case NPC_SUMMONMONSTER:
963                        case AL_TELEPORT:
964                                if (((TBL_MOB*)src)->master_id && ((TBL_MOB*)src)->special_state.ai)
965                                        return 0;
966                }
967        }
968
969        //Check range when not using skill on yourself or is a combo-skill during attack
970        //(these are supposed to always have the same range as your attack)
971        if(src->id != target_id && (!temp || ud->attacktimer == -1))
972        {
973                if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
974                {
975                        if (!unit_can_reach_bl(src, target, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
976                                return 0; //Walk-path check failed.
977                } else
978                if      (!battle_check_range(src, target, skill_get_range2(src, skill_num,skill_lv)
979                        +(skill_num==RG_CLOSECONFINE?0:1)))
980                        //Close confine is exploitable thanks to this extra range "feature" of the client. [Skotlex]
981                        return 0; //Arrow-path check failed.
982        }
983
984        if (!temp) //Stop attack on non-combo skills [Skotlex]
985                unit_stop_attack(src);
986        else if(ud->attacktimer != -1) //Elsewise, delay current attack sequence
987                ud->attackabletime = tick + status_get_adelay(src);
988       
989        ud->state.skillcastcancel = castcancel;
990
991        //temp: Used to signal force cast now.
992        temp = 0;
993       
994        switch(skill_num){
995        case ALL_RESURRECTION:
996                if(battle_check_undead(tstatus->race,tstatus->def_ele)) {       
997                        temp = 1;
998                        casttime = skill_castfix(src, PR_TURNUNDEAD, skill_lv);
999                } else if (!status_isdead(target))
1000                        return 0; //Can't cast on non-dead characters.
1001        break;
1002        case MO_FINGEROFFENSIVE:
1003                if(sd)
1004                        casttime += casttime * min(skill_lv, sd->spiritball);
1005        break;
1006        case MO_EXTREMITYFIST:
1007                if (sc && sc->data[SC_COMBO] &&
1008                   (sc->data[SC_COMBO]->val1 == MO_COMBOFINISH ||
1009                        sc->data[SC_COMBO]->val1 == CH_TIGERFIST ||
1010                        sc->data[SC_COMBO]->val1 == CH_CHAINCRUSH))
1011                        casttime = 0;
1012                temp = 1;
1013        break;
1014        case SA_SPELLBREAKER:
1015                temp = 1;
1016        break;
1017        case ST_CHASEWALK:
1018                if (sc && sc->data[SC_CHASEWALK])
1019                        casttime = 0;
1020        break;
1021        case TK_RUN:
1022                if (sc && sc->data[SC_RUN])
1023                        casttime = 0;
1024        break;
1025        case KN_CHARGEATK:
1026                {
1027                unsigned int k = (distance_bl(src,target)-1)/3; //+100% every 3 cells of distance
1028                if( k > 2 ) k = 2; // ...but hard-limited to 300%.
1029                casttime += casttime * k; 
1030                }
1031        break;
1032        }
1033       
1034        // moved here to prevent Suffragium from ending if skill fails
1035        if (!(skill_get_castnodex(skill_num, skill_lv)&2))
1036                casttime = skill_castfix_sc(src, casttime);
1037
1038        if( casttime>0 || temp){ 
1039
1040                clif_skillcasting(src, src->id, target_id, 0,0, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
1041
1042                if (sd && target->type == BL_MOB)
1043                {
1044                        TBL_MOB *md = (TBL_MOB*)target;
1045                        mobskill_event(md, src, tick, -1); //Cast targetted skill event.
1046                        //temp: used to store mob's mode now.
1047                        if (tstatus->mode&(MD_CASTSENSOR_IDLE|MD_CASTSENSOR_CHASE) &&
1048                                battle_check_target(target, src, BCT_ENEMY) > 0)
1049                        {
1050                                switch (md->state.skillstate) {
1051                                case MSS_RUSH:
1052                                case MSS_FOLLOW:
1053                                        if (!(tstatus->mode&MD_CASTSENSOR_CHASE))
1054                                                break;
1055                                        md->target_id = src->id;
1056                                        md->state.aggressive = (temp&MD_ANGRY)?1:0;
1057                                        md->min_chase = md->db->range3;
1058                                        break;
1059                                case MSS_IDLE:
1060                                case MSS_WALK:
1061                                        if (!(tstatus->mode&MD_CASTSENSOR_IDLE))
1062                                                break;
1063                                        md->target_id = src->id;
1064                                        md->state.aggressive = (temp&MD_ANGRY)?1:0;
1065                                        md->min_chase = md->db->range3;
1066                                        break;
1067                                }
1068                        }
1069                }
1070        }
1071
1072        if( casttime<=0 )
1073                ud->state.skillcastcancel=0;
1074
1075        ud->canact_tick  = tick + casttime + 100;
1076        ud->skilltarget  = target_id;
1077        ud->skillx       = 0;
1078        ud->skilly       = 0;
1079        ud->skillid      = skill_num;
1080        ud->skilllv      = skill_lv;
1081
1082        if(sc && sc->data[SC_CLOAKING] &&
1083                !(sc->data[SC_CLOAKING]->val4&4) && skill_num != AS_CLOAKING)
1084        {
1085                status_change_end(src,SC_CLOAKING,-1);
1086                if (!src->prev) return 0; //Warped away!
1087        }
1088
1089        if(casttime > 0) {
1090                ud->skilltimer = add_timer( tick+casttime, skill_castend_id, src->id, 0 );
[24]1091                if( sd && pc_checkskill(sd,SA_FREECAST) > 0 )
1092                        status_calc_bl(&sd->bl, SCB_SPEED);
[1]1093                else
1094                        unit_stop_walking(src,1);
1095        }
1096        else
1097                skill_castend_id(ud->skilltimer,tick,src->id,0);
1098        return 1;
1099}
1100
1101int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, short skill_num, short skill_lv)
1102{
1103        if(skill_num < 0)
1104                return 0;
1105        return unit_skilluse_pos2(
1106                src, skill_x, skill_y, skill_num, skill_lv,
1107                skill_castfix(src, skill_num, skill_lv),
1108                skill_get_castcancel(skill_num)
1109        );
1110}
1111
1112int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, short skill_num, short skill_lv, int casttime, int castcancel)
1113{
1114        struct map_session_data *sd = NULL;
1115        struct unit_data        *ud = NULL;
1116        struct status_change *sc;
1117        struct block_list    bl;
1118        unsigned int tick = gettick();
1119
1120        nullpo_retr(0, src);
1121
1122        if(!src->prev) return 0; // map ã‚É‘¶Ý‚·‚é‚©
1123        if(status_isdead(src)) return 0;
1124
1125        sd = BL_CAST(BL_PC, src);
1126        ud = unit_bl2ud(src);
1127        if(ud == NULL) return 0;
1128
1129        if(ud->skilltimer != -1) //Normally not needed since clif.c checks for it, but at/char/script commands don't! [Skotlex]
1130                return 0;
1131       
1132        sc = status_get_sc(src);
1133        if (sc && !sc->count)
1134                sc = NULL;
1135       
1136        if(sd) {
1137                if (skillnotok(skill_num, sd) || !skill_check_condition(sd, skill_num, skill_lv,0))
1138                return 0;
1139        } 
1140       
1141        if (!status_check_skilluse(src, NULL, skill_num, 0))
1142                return 0;
1143
1144        if( map_getcell(src->m, skill_x, skill_y, CELL_CHKWALL) )
1145        {// can't cast ground targeted spells on wall cells
1146                if (sd) clif_skill_fail(sd,skill_num,0,0);
1147                return 0;
1148        }
1149
1150        /* ŽË’ö‚ƏáŠQ•šƒ`ƒFƒbƒN */
1151        bl.type = BL_NUL;
1152        bl.m = src->m;
1153        bl.x = skill_x;
1154        bl.y = skill_y;
1155
1156        if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
1157        {
1158                if (!unit_can_reach_bl(src, &bl, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
1159                        return 0; //Walk-path check failed.
1160        } else
1161        if      (!battle_check_range(src,&bl,skill_get_range2(src, skill_num,skill_lv)+1))
1162                return 0; //Arrow-path check failed.
1163
1164        unit_stop_attack(src);
1165        ud->state.skillcastcancel = castcancel;
1166
1167        // moved here to prevent Suffragium from ending if skill fails
1168        if (!(skill_get_castnodex(skill_num, skill_lv)&2))
1169                casttime = skill_castfix_sc(src, casttime);
1170
1171        if( casttime>0 ) {
1172                unit_stop_walking( src, 1);
1173                clif_skillcasting(src, src->id, 0, skill_x, skill_y, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
1174        } else
1175                ud->state.skillcastcancel=0;
1176
1177        ud->canact_tick  = tick + casttime + 100;
1178        ud->skillid      = skill_num;
1179        ud->skilllv      = skill_lv;
1180        ud->skillx       = skill_x;
1181        ud->skilly       = skill_y;
1182        ud->skilltarget  = 0;
1183
1184        if (sc && sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4))
1185        {
1186                status_change_end(src,SC_CLOAKING,-1);
1187                if (!src->prev) return 0; //Warped away!
1188        }
1189
1190        if(casttime > 0) {
1191                ud->skilltimer = add_timer( tick+casttime, skill_castend_pos, src->id, 0 );
[24]1192                if( sd && pc_checkskill(sd,SA_FREECAST) > 0 )
1193                        status_calc_bl(&sd->bl, SCB_SPEED);
[1]1194                else
1195                        unit_stop_walking(src,1);
1196        }
1197        else {
1198                ud->skilltimer = -1;
1199                skill_castend_pos(ud->skilltimer,tick,src->id,0);
1200        }
1201        return 1;
1202}
1203
1204static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data);
1205
1206int unit_stop_attack(struct block_list *bl)
1207{
1208        struct unit_data *ud = unit_bl2ud(bl);
1209        nullpo_retr(0, bl);
1210
1211        if(!ud || ud->attacktimer == -1)
1212                return 0;
1213
1214        delete_timer( ud->attacktimer, unit_attack_timer );
1215        ud->attacktimer = -1;
1216        ud->target = 0;
1217        return 0;
1218}
1219
1220//Means current target is unattackable. For now only unlocks mobs.
1221int unit_unattackable(struct block_list *bl)
1222{
1223        struct unit_data *ud = unit_bl2ud(bl);
1224        if (ud) {
1225                ud->target = 0;
1226                ud->state.attack_continue = 0;
1227        }
1228       
1229        if(bl->type == BL_MOB)
1230                mob_unlocktarget((struct mob_data*)bl, gettick()) ;
1231        else if(bl->type == BL_PET)
1232                pet_unlocktarget((struct pet_data*)bl);
1233        return 0;
1234}
1235
1236/*==========================================
1237 * UŒ‚—v‹
1238 * type‚ª1‚È‚çŒp‘±UŒ‚
1239 *------------------------------------------*/
1240int unit_attack(struct block_list *src,int target_id,int continuous)
1241{
1242        struct block_list *target;
1243        struct unit_data  *ud;
1244
1245        nullpo_retr(0, ud = unit_bl2ud(src));
1246
1247        target=map_id2bl(target_id);
1248        if(target==NULL || status_isdead(target)) {
1249                unit_unattackable(src);
1250                return 1;
1251        }
1252
1253        if( src->type == BL_PC ){
1254                TBL_PC* sd = (TBL_PC*)src;
1255                if( target->type == BL_NPC )
1256                {// monster npcs [Valaris]
1257                        npc_click(sd,(TBL_NPC*)target); // submitted by leinsirk10 [Celest]
1258                        return 0;
1259                } else if( pc_is90overweight(sd) )
1260                {// overwheight - stop attacking and walking
1261                        unit_stop_attack(src);
1262                        unit_stop_walking(src,1);
1263                        return 0;
1264                }
1265        }
1266
1267        if(battle_check_target(src,target,BCT_ENEMY)<=0 ||
1268                !status_check_skilluse(src, target, 0, 0)
1269        ) {
1270                unit_unattackable(src);
1271                return 1;
1272        }
1273
1274        ud->target = target_id;
1275        ud->state.attack_continue = continuous;
1276        if (continuous) //If you're to attack continously, set to auto-case character
1277                ud->chaserange = status_get_range(src);
1278
1279        //Just change target/type. [Skotlex]
1280        if(ud->attacktimer != -1)
1281                return 0;
1282
1283        //Set Mob's ANGRY/BERSERK states.
1284        if(src->type == BL_MOB)
1285                ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK;
1286
1287        if(DIFF_TICK(ud->attackabletime, gettick()) > 0)
1288                //Do attack next time it is possible. [Skotlex]
1289                ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1290        else //Attack NOW.
1291                unit_attack_timer(-1,gettick(),src->id,0);
1292
1293        return 0;
1294}
1295
1296//Cancels an ongoing combo, resets attackable time and restarts the
1297//attack timer to resume attacking after amotion time. [Skotlex]
1298int unit_cancel_combo(struct block_list *bl)
1299{
1300        struct unit_data  *ud;
1301
1302        if (!status_change_end(bl, SC_COMBO, -1))
1303                return 0; //Combo wasn't active.
1304
1305        ud = unit_bl2ud(bl);
1306        nullpo_retr(0, ud);
1307
1308        ud->attackabletime = gettick() + status_get_amotion(bl);
1309
1310        if (ud->attacktimer == -1)
1311                return 1; //Nothing more to do.
1312       
1313        delete_timer(ud->attacktimer, unit_attack_timer);
1314        ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,bl->id,0);
1315        return 1;
1316}
1317/*==========================================
1318 *
1319 *------------------------------------------*/
1320bool unit_can_reach_pos(struct block_list *bl,int x,int y, int easy)
1321{
1322        nullpo_retr(false, bl);
1323
1324        if( bl->x==x && bl->y==y )      // “¯‚¶ƒ}ƒX
1325                return true;
1326
1327        return path_search(NULL,bl->m,bl->x,bl->y,x,y,easy,CELL_CHKNOREACH);
1328}
1329
1330/*==========================================
1331 *
1332 *------------------------------------------*/
1333bool unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, int easy, short *x, short *y)
1334{
1335        int i;
1336        short dx,dy;
1337        nullpo_retr(false, bl);
1338        nullpo_retr(false, tbl);
1339
1340        if( bl->m != tbl->m)
1341                return false;
1342       
1343        if( bl->x==tbl->x && bl->y==tbl->y )
1344                return true;
1345
1346        if(range>0 && !check_distance_bl(bl, tbl, range))
1347                return false;
1348
1349        // It judges whether it can adjoin or not.
1350        dx=tbl->x - bl->x;
1351        dy=tbl->y - bl->y;
1352        dx=(dx>0)?1:((dx<0)?-1:0);
1353        dy=(dy>0)?1:((dy<0)?-1:0);
1354       
1355        if (map_getcell(tbl->m,tbl->x-dx,tbl->y-dy,CELL_CHKNOPASS))
1356        {       //Look for a suitable cell to place in.
1357                for(i=0;i<9 && map_getcell(tbl->m,tbl->x-dirx[i],tbl->y-diry[i],CELL_CHKNOPASS);i++);
1358                if (i==9) return false; //No valid cells.
1359                dx = dirx[i];
1360                dy = diry[i];
1361        }
1362
1363        if (x) *x = tbl->x-dx;
1364        if (y) *y = tbl->y-dy;
1365        return path_search(NULL,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH);
1366}
1367
1368
1369/*==========================================
1370 * PC‚̍UŒ‚ (timerŠÖ”)
1371 *------------------------------------------*/
1372static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int tick)
1373{
1374        struct block_list *target;
1375        struct unit_data *ud;
1376        struct status_data *sstatus;
1377        struct map_session_data *sd = NULL;
1378        struct mob_data *md = NULL;
1379        int range;
1380       
1381        if((ud=unit_bl2ud(src))==NULL)
1382                return 0;
1383        if(ud->attacktimer != tid){
1384                ShowError("unit_attack_timer %d != %d\n",ud->attacktimer,tid);
1385                return 0;
1386        }
1387        sd = BL_CAST(BL_PC, src);
1388        md = BL_CAST(BL_MOB, src);
1389        ud->attacktimer=-1;
1390        target=map_id2bl(ud->target);
1391
1392        if(src == NULL || src->prev == NULL || target==NULL || target->prev == NULL)
1393                return 0;
1394
1395        if(status_isdead(src) || status_isdead(target) || !status_check_skilluse(src, target, 0, 0))
1396                return 0; // can't attack under these conditions
1397
1398        if (src->m != target->m)
1399        {
1400                if (src->type == BL_MOB && mob_warpchase((TBL_MOB*)src, target))
1401                        return 1; // Follow up.
1402                return 0;
1403        }
1404
1405        if(ud->skilltimer != -1 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0))
1406                return 0; // can't attack while casting
1407       
1408        if(!battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0))
1409        {       // attacking when under cast delay has restrictions:
1410                if (tid == -1) { //requested attack.
1411                        if(sd) clif_skill_fail(sd,1,4,0);
1412                        return 0;
1413                }
1414                //Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex]
1415                if(ud->state.attack_continue) {
1416                        if (DIFF_TICK(ud->canact_tick, ud->attackabletime) > 0)
1417                                ud->attackabletime = ud->canact_tick;
1418                        ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1419                }
1420                return 1;
1421        }
1422
1423        sstatus = status_get_status_data(src);
1424        range = sstatus->rhw.range;
1425       
1426        if(!sd || sd->status.weapon != W_BOW) range++; //Dunno why everyone but bows gets this extra range...
1427        if(unit_is_walking(target)) range++; //Extra range when chasing
1428
1429        if(!check_distance_bl(src,target,range) ) {
1430                //Chase if required.
1431                if(sd)
1432                        clif_movetoattack(sd,target);
1433                else if(ud->state.attack_continue)
1434                        unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
1435                return 1;
1436        }
1437        if(!battle_check_range(src,target,range)) {
1438                //Within range, but no direct line of attack
1439                if(ud->state.attack_continue) {
1440                        if(ud->chaserange > 2) ud->chaserange-=2;
1441                        unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
1442                }
1443                return 1;
1444        }
1445
1446        //Sync packet only for players.
1447        //Non-players use the sync packet on the walk timer. [Skotlex]
1448        if (tid == -1 && sd) clif_fixpos(src);
1449
1450        if(DIFF_TICK(ud->attackabletime,tick) <= 0)
1451        {
1452                if (battle_config.attack_direction_change && (src->type&battle_config.attack_direction_change)) {
1453                        ud->dir = map_calc_dir(src, target->x,target->y );
1454                }
1455                if(ud->walktimer != -1)
1456                        unit_stop_walking(src,1);
1457                if(md) {
1458                        if (mobskill_use(md,tick,-1))
1459                                return 1;
1460                        if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
1461                        {       // Link monsters nearby [Skotlex]
1462                                md->last_linktime = tick;
1463                                map_foreachinrange(mob_linksearch, src, md->db->range2, BL_MOB, md->class_, target, tick);
1464                        }
1465                }
1466                if(src->type == BL_PET && pet_attackskill((TBL_PET*)src, target->id))
1467                        return 1;
1468               
1469                map_freeblock_lock();
1470                ud->attacktarget_lv = battle_weapon_attack(src,target,tick,0);
1471
1472                if(sd && sd->status.pet_id > 0 && sd->pd && battle_config.pet_attack_support)
1473                        pet_target_check(sd,target,0);
1474                map_freeblock_unlock();
1475
1476                ud->attackabletime = tick + sstatus->adelay;
1477//              You can't move if you can't attack neither.
1478                if (src->type&battle_config.attack_walk_delay)
1479                        unit_set_walkdelay(src, tick, sstatus->amotion, 1);
1480        }
1481
1482        if(ud->state.attack_continue)
1483                ud->attacktimer = add_timer(ud->attackabletime,unit_attack_timer,src->id,0);
1484
1485        return 1;
1486}
1487
1488static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data)
1489{
1490        struct block_list *bl;
1491        bl = map_id2bl(id);
1492        if(bl && unit_attack_timer_sub(bl, tid, tick) == 0)
1493                unit_unattackable(bl);
1494        return 0;
1495}
1496
1497/*==========================================
1498 * Cancels an ongoing skill cast.
1499 * flag&1: Cast-Cancel invoked.
1500 * flag&2: Cancel only if skill is cancellable.
1501 *------------------------------------------*/
1502int unit_skillcastcancel(struct block_list *bl,int type)
1503{
1504        struct map_session_data *sd = NULL;
1505        struct unit_data *ud = unit_bl2ud( bl);
1506        unsigned int tick=gettick();
1507        int ret=0, skill;
1508       
1509        nullpo_retr(0, bl);
1510        if (!ud || ud->skilltimer==-1)
1511                return 0; //Nothing to cancel.
1512
1513        sd = BL_CAST(BL_PC, bl);
1514
1515        if (type&2) {
1516                //See if it can be cancelled.
1517                if (!ud->state.skillcastcancel)
1518                        return 0;
1519
1520                if (sd && (sd->special_state.no_castcancel2 ||
1521                        (sd->special_state.no_castcancel && !map_flag_gvg(bl->m)))) //fixed flags being read the wrong way around [blackhole89]
1522                        return 0;
1523        }
1524       
[24]1525        ud->canact_tick = tick;
1526
[1]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);
[24]1538
1539        ud->skilltimer = -1;
1540
1541        if( sd && pc_checkskill(sd,SA_FREECAST) > 0 )
1542                status_calc_bl(&sd->bl, SCB_SPEED);
1543
[1]1544        if(bl->type==BL_MOB) ((TBL_MOB*)bl)->skillidx  = -1;
1545
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.