diff options
author | shennetsind <ind@henn.et> | 2014-01-24 18:18:13 -0200 |
---|---|---|
committer | shennetsind <ind@henn.et> | 2014-01-24 18:18:13 -0200 |
commit | 7438e401b4209198691d3c8ca65b6c702338fa41 (patch) | |
tree | 59b2f8f11d2a64d8eeb6a05f4e9fef074a1cf8c9 | |
parent | acc992ac2838f6380ebf2b2f8a514e86c2b750d9 (diff) | |
download | hercules-7438e401b4209198691d3c8ca65b6c702338fa41.tar.gz hercules-7438e401b4209198691d3c8ca65b6c702338fa41.tar.bz2 hercules-7438e401b4209198691d3c8ca65b6c702338fa41.tar.xz hercules-7438e401b4209198691d3c8ca65b6c702338fa41.zip |
Follow up e587d715cbc3
Fixed an issue where character position would not be instantly updated upon using a skill with casttime while in hiding (e.g. cloaking -> soul break), Special Thanks to Michieru!
Turned the feature optional, by commenting out ANTI_MAYAP_CHEAT in src/config/secure.h due it not being entirely able to mimic skill casting out of hiding (read the description in secure.h for further details).
Also fixed some status.c indentation which, unfortunately, considerably increased this commits size.
Signed-off-by: shennetsind <ind@henn.et>
-rw-r--r-- | src/config/secure.h | 11 | ||||
-rw-r--r-- | src/map/clif.c | 28 | ||||
-rw-r--r-- | src/map/status.c | 2271 | ||||
-rw-r--r-- | src/map/unit.c | 27 |
4 files changed, 1185 insertions, 1152 deletions
diff --git a/src/config/secure.h b/src/config/secure.h index 7f16ba55a..5742ae30b 100644 --- a/src/config/secure.h +++ b/src/config/secure.h @@ -47,4 +47,15 @@ **/ #define SECURE_NPCTIMEOUT_INTERVAL 1 +/** + * Uncomment to disable + * while enabled, movement of invisible (cloaking, hide, etca [not chase walk]) units is not informed to nearby foes, + * rendering any client-side cheat, that would otherwise make these units visible, to + * - "Why is this a setting?" because theres a cost, while enabled if a hidden character uses a skill with cast time, + * - for example "cloaking -> walk a bit -> soul break another player" the character display will be momentarily abrupted + * - on the action of unhiding (its a quick effect, ~0.007s in duration), and due to the nature of the skill cast on the client + * - it was not possible to work around this, and thus why it is optional, comment the line to disable. + **/ +#define ANTI_MAYAP_CHEAT + #endif // _CONFIG_SECURE_H_ diff --git a/src/map/clif.c b/src/map/clif.c index 975a5aa01..c0b3f7f7f 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1550,11 +1550,12 @@ void clif_walkok(struct map_session_data *sd) void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud) { +#ifdef ANTI_MAYAP_CHEAT struct status_change *sc = NULL; - if( (sc = status->get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE|OPTION_CHASEWALK) ) + if( (sc = status->get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE) ) clif->ally_only = true; - +#endif clif->set_unit_walking(bl,NULL,ud,AREA_WOS); if(vd->cloth_color) @@ -1585,8 +1586,9 @@ void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *u clif->send_petdata(NULL, (TBL_PET*)bl, 3, vd->head_bottom); break; } - +#ifdef ANTI_MAYAP_CHEAT clif->ally_only = false; +#endif } @@ -1598,9 +1600,11 @@ void clif_move(struct unit_data *ud) unsigned char buf[16]; struct view_data *vd; struct block_list *bl = ud->bl; +#ifdef ANTI_MAYAP_CHEAT struct status_change *sc = NULL; - vd = status->get_viewdata(bl); - if (!vd || vd->class_ == INVISIBLE_CLASS) +#endif + + if ( !(vd = status->get_viewdata(bl)) || vd->class_ == INVISIBLE_CLASS ) return; //This performance check is needed to keep GM-hidden objects from being notified to bots. /** @@ -1616,21 +1620,27 @@ void clif_move(struct unit_data *ud) clif->move2(bl, vd, ud); return; } - - if( (sc = status->get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE|OPTION_CHASEWALK) ) - clif->ally_only = true; +#ifdef ANTI_MAYAP_CHEAT + if( (sc = status->get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE) ) + clif->ally_only = true; +#endif + WBUFW(buf,0)=0x86; WBUFL(buf,2)=bl->id; WBUFPOS2(buf,6,bl->x,bl->y,ud->to_x,ud->to_y,8,8); WBUFL(buf,12)=(unsigned int)timer->gettick(); + clif->send(buf, packet_len(0x86), bl, AREA_WOS); + if (disguised(bl)) { WBUFL(buf,2)=-bl->id; clif->send(buf, packet_len(0x86), bl, SELF); } - + +#ifdef ANTI_MAYAP_CHEAT clif->ally_only = false; +#endif } diff --git a/src/map/status.c b/src/map/status.c index e89f8d331..74b2571e1 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -9215,7 +9215,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val return 1; } - /*========================================== * Ending all status except those listed. * @TODO maybe usefull for dispel instead reseting a liste there. @@ -9241,11 +9240,11 @@ int status_change_clear(struct block_list* bl, int type) { if(type == 0){ if( status->get_sc_type(i)&SC_NO_REM_DEATH ) { switch (i) { - case SC_ARMOR_PROPERTY://Only when its Holy or Dark that it doesn't dispell on death - if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK ) - break; - default: - continue; + case SC_ARMOR_PROPERTY://Only when its Holy or Dark that it doesn't dispell on death + if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK ) + break; + default: + continue; } } } @@ -9289,7 +9288,10 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const struct status_data *st; struct view_data *vd; int opt_flag=0, calc_flag; - +#ifdef ANTI_MAYAP_CHEAT + bool invisible = false; +#endif + nullpo_ret(bl); sc = status->get_sc(bl); @@ -9314,19 +9316,19 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const //"Ugly workaround" [Skotlex] //delays status change ending so that a skill that sets opt1 fails to //trigger when it also removed one - case SC_STONE: - sce->val3 = 0; //Petrify time counter. - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - if (sce->val1) { - //Removing the 'level' shouldn't affect anything in the code - //since these SC are not affected by it, and it lets us know - //if we have already delayed this attack or not. - sce->val1 = 0; - sce->timer = timer->add(timer->gettick()+10, status->change_timer, bl->id, type); - return 1; - } + case SC_STONE: + sce->val3 = 0; //Petrify time counter. + case SC_FREEZE: + case SC_STUN: + case SC_SLEEP: + if (sce->val1) { + //Removing the 'level' shouldn't affect anything in the code + //since these SC are not affected by it, and it lets us know + //if we have already delayed this attack or not. + sce->val1 = 0; + sce->timer = timer->add(timer->gettick()+10, status->change_timer, bl->id, type); + return 1; + } } } @@ -9338,444 +9340,449 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const status->display_remove(sd,type); } +#ifdef ANTI_MAYAP_CHEAT + if( sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE) ) + invisible = true; +#endif + vd = status->get_viewdata(bl); calc_flag = status->ChangeFlagTable[type]; switch(type) { - case SC_GRANITIC_ARMOR: - { - int damage = st->max_hp*sce->val3/100; - if(st->hp < damage) //to not kill him - damage = st->hp-1; - status->damage(NULL, bl, damage,0,0,1); - } - break; - case SC_PYROCLASTIC: - if(bl->type == BL_PC) - skill->break_equip(bl,EQP_WEAPON,10000,BCT_SELF); - break; - case SC_RUN: + case SC_GRANITIC_ARMOR: { - struct unit_data *ud = unit->bl2ud(bl); - bool begin_spurt = true; - // Note: this int64 value is stored in two separate int32 variables (FIXME) - int64 starttick = (int64)sce->val3&0x00000000ffffffffLL; - starttick |= ((int64)sce->val4<<32)&0xffffffff00000000LL; - - if (ud) { - if(!ud->state.running) - begin_spurt = false; - ud->state.running = 0; - if (ud->walktimer != INVALID_TIMER) - unit->stop_walking(bl,1); - } - if (begin_spurt && sce->val1 >= 7 - && DIFF_TICK(timer->gettick(), starttick) <= 1000 - && (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0)) - ) - sc_start(bl,SC_STRUP,100,sce->val1,skill->get_time2(status->sc2skill(type), sce->val1)); + int damage = st->max_hp*sce->val3/100; + if(st->hp < damage) //to not kill him + damage = st->hp-1; + status->damage(NULL, bl, damage,0,0,1); } - break; - case SC_AUTOBERSERK: - if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1) - status_change_end(bl, SC_PROVOKE, INVALID_TIMER); - break; + break; + case SC_PYROCLASTIC: + if(bl->type == BL_PC) + skill->break_equip(bl,EQP_WEAPON,10000,BCT_SELF); + break; + case SC_RUN: + { + struct unit_data *ud = unit->bl2ud(bl); + bool begin_spurt = true; + // Note: this int64 value is stored in two separate int32 variables (FIXME) + int64 starttick = (int64)sce->val3&0x00000000ffffffffLL; + starttick |= ((int64)sce->val4<<32)&0xffffffff00000000LL; + + if (ud) { + if(!ud->state.running) + begin_spurt = false; + ud->state.running = 0; + if (ud->walktimer != INVALID_TIMER) + unit->stop_walking(bl,1); + } + if (begin_spurt && sce->val1 >= 7 + && DIFF_TICK(timer->gettick(), starttick) <= 1000 + && (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0)) + ) + sc_start(bl,SC_STRUP,100,sce->val1,skill->get_time2(status->sc2skill(type), sce->val1)); + } + break; + case SC_AUTOBERSERK: + if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1) + status_change_end(bl, SC_PROVOKE, INVALID_TIMER); + break; - case SC_ENDURE: - case SC_DEFENDER: - case SC_REFLECTSHIELD: - case SC_AUTOGUARD: - { - struct map_session_data *tsd; - if( bl->type == BL_PC ) { - // Clear Status from others - int i; - for( i = 0; i < 5; i++ ) { - if( sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) && tsd->sc.data[type] ) + case SC_ENDURE: + case SC_DEFENDER: + case SC_REFLECTSHIELD: + case SC_AUTOGUARD: + { + struct map_session_data *tsd; + if( bl->type == BL_PC ) { + // Clear Status from others + int i; + for( i = 0; i < 5; i++ ) { + if( sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) && tsd->sc.data[type] ) + status_change_end(&tsd->bl, type, INVALID_TIMER); + } + } else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag ) { + // Clear Status from Master + tsd = ((TBL_MER*)bl)->master; + if( tsd && tsd->sc.data[type] ) status_change_end(&tsd->bl, type, INVALID_TIMER); } - } else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag ) { - // Clear Status from Master - tsd = ((TBL_MER*)bl)->master; - if( tsd && tsd->sc.data[type] ) - status_change_end(&tsd->bl, type, INVALID_TIMER); - } - } - break; - case SC_DEVOTION: - { - struct block_list *d_bl = map->id2bl(sce->val1); - if( d_bl ) { - if( d_bl->type == BL_PC ) - ((TBL_PC*)d_bl)->devotion[sce->val2] = 0; - else if( d_bl->type == BL_MER ) - ((TBL_MER*)d_bl)->devotion_flag = 0; - clif->devotion(d_bl, NULL); } + break; + case SC_DEVOTION: + { + struct block_list *d_bl = map->id2bl(sce->val1); + if( d_bl ) { + if( d_bl->type == BL_PC ) + ((TBL_PC*)d_bl)->devotion[sce->val2] = 0; + else if( d_bl->type == BL_MER ) + ((TBL_MER*)d_bl)->devotion_flag = 0; + clif->devotion(d_bl, NULL); + } - status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER); - status_change_end(bl, SC_DEFENDER, INVALID_TIMER); - status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER); - status_change_end(bl, SC_ENDURE, INVALID_TIMER); - } - break; - - case SC_BLADESTOP: - if(sce->val4) { - int target_id = sce->val4; - struct block_list *tbl = map->id2bl(target_id); - struct status_change *tsc = status->get_sc(tbl); - sce->val4 = 0; - if(tbl && tsc && tsc->data[SC_BLADESTOP]) { - tsc->data[SC_BLADESTOP]->val4 = 0; - status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER); - } - clif->bladestop(bl, target_id, 0); - } - break; - case SC_DANCING: - { - const char* prevfile = "<unknown>"; - int prevline = 0; - struct map_session_data *dsd; - struct status_change_entry *dsc; - struct skill_unit_group *group; + status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER); + status_change_end(bl, SC_DEFENDER, INVALID_TIMER); + status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER); + status_change_end(bl, SC_ENDURE, INVALID_TIMER); + } + break; - if( sd ) - { - if( sd->delunit_prevfile ) - {// initially this is NULL, when a character logs in - prevfile = sd->delunit_prevfile; - prevline = sd->delunit_prevline; + case SC_BLADESTOP: + if(sce->val4) { + int target_id = sce->val4; + struct block_list *tbl = map->id2bl(target_id); + struct status_change *tsc = status->get_sc(tbl); + sce->val4 = 0; + if(tbl && tsc && tsc->data[SC_BLADESTOP]) { + tsc->data[SC_BLADESTOP]->val4 = 0; + status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER); } - else + clif->bladestop(bl, target_id, 0); + } + break; + case SC_DANCING: + { + const char* prevfile = "<unknown>"; + int prevline = 0; + struct map_session_data *dsd; + struct status_change_entry *dsc; + struct skill_unit_group *group; + + if( sd ) { - prevfile = "<none>"; + if( sd->delunit_prevfile ) + {// initially this is NULL, when a character logs in + prevfile = sd->delunit_prevfile; + prevline = sd->delunit_prevline; + } + else + { + prevfile = "<none>"; + } + sd->delunit_prevfile = file; + sd->delunit_prevline = line; } - sd->delunit_prevfile = file; - sd->delunit_prevline = line; - } - if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map->id2sd(sce->val4))) - {// end status on partner as well - dsc = dsd->sc.data[SC_DANCING]; - if(dsc) { + if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map->id2sd(sce->val4))) + {// end status on partner as well + dsc = dsd->sc.data[SC_DANCING]; + if(dsc) { - //This will prevent recursive loops. - dsc->val2 = dsc->val4 = 0; + //This will prevent recursive loops. + dsc->val2 = dsc->val4 = 0; - status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER); + status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER); + } } - } - if(sce->val2) - {// erase associated land skill - group = skill->id2group(sce->val2); + if(sce->val2) + {// erase associated land skill + group = skill->id2group(sce->val2); - if( group == NULL ) - { - ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n", - sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, - sd ? sd->status.char_id : 0, - mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y, - prevfile, prevline, - file, line); + if( group == NULL ) + { + ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n", + sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, + sd ? sd->status.char_id : 0, + mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y, + prevfile, prevline, + file, line); + } + + sce->val2 = 0; + skill->del_unitgroup(group,ALC_MARK); } - sce->val2 = 0; - skill->del_unitgroup(group,ALC_MARK); + if((sce->val1&0xFFFF) == CG_MOONLIT) + clif->sc_end(bl,bl->id,AREA,SI_MOON); + + status_change_end(bl, SC_LONGING, INVALID_TIMER); + } + break; + case SC_NOCHAT: + if (sd && sd->status.manner < 0 && tid != INVALID_TIMER) + sd->status.manner = 0; + if (sd && tid == INVALID_TIMER) + { + clif->changestatus(sd,SP_MANNER,sd->status.manner); + clif->updatestatus(sd,SP_MANNER); + } + break; + case SC_SPLASHER: + { + struct block_list *src=map->id2bl(sce->val3); + if(src && tid != INVALID_TIMER) + skill->castend_damage_id(src, bl, sce->val2, sce->val1, timer->gettick(), SD_LEVEL ); + } + break; + case SC_RG_CCONFINE_S: + { + struct block_list *src = sce->val2 ? map->id2bl(sce->val2) : NULL; + struct status_change *sc2 = src ? status->get_sc(src) : NULL; + if (src && sc2 && sc2->data[SC_RG_CCONFINE_M]) { + //If status was already ended, do nothing. + //Decrease count + if (--(sc2->data[SC_RG_CCONFINE_M]->val1) <= 0) //No more holds, free him up. + status_change_end(src, SC_RG_CCONFINE_M, INVALID_TIMER); + } + } + case SC_RG_CCONFINE_M: + if (sce->val2 > 0) { + //Caster has been unlocked... nearby chars need to be unlocked. + int range = 1 + +skill->get_range2(bl, status->sc2skill(type), sce->val1) + +skill->get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold.... + map->foreachinarea(status->change_timer_sub, + bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,timer->gettick()); } + break; + case SC_COMBOATTACK: + if( sd ) + switch (sce->val1) { + case MO_COMBOFINISH: + case CH_TIGERFIST: + case CH_CHAINCRUSH: + clif->skillinfo(sd, MO_EXTREMITYFIST, 0); + break; + case TK_JUMPKICK: + clif->skillinfo(sd, TK_JUMPKICK, 0); + break; + case MO_TRIPLEATTACK: + if (pc->checkskill(sd, SR_DRAGONCOMBO) > 0) + clif->skillinfo(sd, SR_DRAGONCOMBO, 0); + break; + case SR_FALLENEMPIRE: + clif->skillinfo(sd, SR_GATEOFHELL, 0); + clif->skillinfo(sd, SR_TIGERCANNON, 0); + break; + } + break; - if((sce->val1&0xFFFF) == CG_MOONLIT) - clif->sc_end(bl,bl->id,AREA,SI_MOON); + case SC_MARIONETTE_MASTER: + case SC_MARIONETTE: /// Marionette target + if (sce->val1) { + // check for partner and end their marionette status as well + enum sc_type type2 = (type == SC_MARIONETTE_MASTER) ? SC_MARIONETTE : SC_MARIONETTE_MASTER; + struct block_list *pbl = map->id2bl(sce->val1); + struct status_change* sc2 = pbl ? status->get_sc(pbl) : NULL; + + if (sc2 && sc2->data[type2]) + { + sc2->data[type2]->val1 = 0; + status_change_end(pbl, type2, INVALID_TIMER); + } + } + break; - status_change_end(bl, SC_LONGING, INVALID_TIMER); - } - break; - case SC_NOCHAT: - if (sd && sd->status.manner < 0 && tid != INVALID_TIMER) - sd->status.manner = 0; - if (sd && tid == INVALID_TIMER) - { - clif->changestatus(sd,SP_MANNER,sd->status.manner); - clif->updatestatus(sd,SP_MANNER); - } - break; - case SC_SPLASHER: - { - struct block_list *src=map->id2bl(sce->val3); - if(src && tid != INVALID_TIMER) - skill->castend_damage_id(src, bl, sce->val2, sce->val1, timer->gettick(), SD_LEVEL ); - } - break; - case SC_RG_CCONFINE_S: - { - struct block_list *src = sce->val2 ? map->id2bl(sce->val2) : NULL; - struct status_change *sc2 = src ? status->get_sc(src) : NULL; - if (src && sc2 && sc2->data[SC_RG_CCONFINE_M]) { - //If status was already ended, do nothing. - //Decrease count - if (--(sc2->data[SC_RG_CCONFINE_M]->val1) <= 0) //No more holds, free him up. - status_change_end(src, SC_RG_CCONFINE_M, INVALID_TIMER); + case SC_BERSERK: + case SC_SATURDAY_NIGHT_FEVER: + if(st->hp > 200 && sc && sc->data[SC__BLOODYLUST]) { + status_percent_heal(bl, 100, 0); + status_change_end(bl, SC__BLOODYLUST, INVALID_TIMER); + } else if(st->hp > 100 && sce->val2) //If val2 is removed, no HP penalty (dispelled?) [Skotlex] + status->set_hp(bl, 100, 0); + if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2) { + sc->data[SC_ENDURE]->val4 = 0; + status_change_end(bl, SC_ENDURE, INVALID_TIMER); } - } - case SC_RG_CCONFINE_M: - if (sce->val2 > 0) { - //Caster has been unlocked... nearby chars need to be unlocked. - int range = 1 - +skill->get_range2(bl, status->sc2skill(type), sce->val1) - +skill->get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold.... - map->foreachinarea(status->change_timer_sub, - bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,timer->gettick()); - } - break; - case SC_COMBOATTACK: - if( sd ) - switch (sce->val1) { - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - clif->skillinfo(sd, MO_EXTREMITYFIST, 0); - break; - case TK_JUMPKICK: - clif->skillinfo(sd, TK_JUMPKICK, 0); + sc_start4(bl, SC_GDSKILL_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill->get_time(LK_BERSERK, sce->val1)); + if( type == SC_SATURDAY_NIGHT_FEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds. + sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill->get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1)); + break; + case SC_GOSPEL: + if (sce->val3) { //Clear the group. + struct skill_unit_group* group = skill->id2group(sce->val3); + sce->val3 = 0; + skill->del_unitgroup(group,ALC_MARK); + } + break; + case SC_HERMODE: + if(sce->val3 == BCT_SELF) + skill->clear_unitgroup(bl); + break; + case SC_BASILICA: //Clear the skill area. [Skotlex] + skill->clear_unitgroup(bl); + break; + case SC_TRICKDEAD: + if (vd) vd->dead_sit = 0; + break; + case SC_WARM: + case SC__MANHOLE: + if (sce->val4) { //Clear the group. + struct skill_unit_group* group = skill->id2group(sce->val4); + sce->val4 = 0; + if( group ) /* might have been cleared before status ended, e.g. land protector */ + skill->del_unitgroup(group,ALC_MARK); + } + break; + case SC_KAAHI: + //Delete timer if it exists. + if (sce->val4 != INVALID_TIMER) + timer->delete(sce->val4,status->kaahi_heal_timer); + break; + case SC_JAILED: + if(tid == INVALID_TIMER) break; - case MO_TRIPLEATTACK: - if (pc->checkskill(sd, SR_DRAGONCOMBO) > 0) - clif->skillinfo(sd, SR_DRAGONCOMBO, 0); + //natural expiration. + if(sd && sd->mapindex == sce->val2) + pc->setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT); + break; //guess hes not in jail :P + case SC_HLIF_CHANGE: + if (tid == INVALID_TIMER) break; - case SR_FALLENEMPIRE: - clif->skillinfo(sd, SR_GATEOFHELL, 0); - clif->skillinfo(sd, SR_TIGERCANNON, 0); + // "lose almost all their HP and SP" on natural expiration. + status->set_hp(bl, 10, 0); + status->set_sp(bl, 10, 0); + break; + case SC_AUTOTRADE: + if (tid == INVALID_TIMER) break; - } - break; - - case SC_MARIONETTE_MASTER: - case SC_MARIONETTE: /// Marionette target - if (sce->val1) { - // check for partner and end their marionette status as well - enum sc_type type2 = (type == SC_MARIONETTE_MASTER) ? SC_MARIONETTE : SC_MARIONETTE_MASTER; - struct block_list *pbl = map->id2bl(sce->val1); - struct status_change* sc2 = pbl ? status->get_sc(pbl) : NULL; - - if (sc2 && sc2->data[type2]) - { - sc2->data[type2]->val1 = 0; - status_change_end(pbl, type2, INVALID_TIMER); + // Note: vending/buying is closed by unit_remove_map, no + // need to do it here. + map->quit(sd); + // Because map->quit calls status_change_end with tid -1 + // from here it's not neccesary to continue + return 1; + break; + case SC_STOP: + if( sce->val2 ) { + struct block_list* tbl = map->id2bl(sce->val2); + sce->val2 = 0; + if( tbl && (sc = status->get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id ) + status_change_end(tbl, SC_STOP, INVALID_TIMER); } - } - break; - - case SC_BERSERK: - case SC_SATURDAY_NIGHT_FEVER: - if(st->hp > 200 && sc && sc->data[SC__BLOODYLUST]) { - status_percent_heal(bl, 100, 0); - status_change_end(bl, SC__BLOODYLUST, INVALID_TIMER); - } else if(st->hp > 100 && sce->val2) //If val2 is removed, no HP penalty (dispelled?) [Skotlex] - status->set_hp(bl, 100, 0); - if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2) { - sc->data[SC_ENDURE]->val4 = 0; + break; + case SC_LKCONCENTRATION: status_change_end(bl, SC_ENDURE, INVALID_TIMER); - } - sc_start4(bl, SC_GDSKILL_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill->get_time(LK_BERSERK, sce->val1)); - if( type == SC_SATURDAY_NIGHT_FEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds. - sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill->get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1)); - break; - case SC_GOSPEL: - if (sce->val3) { //Clear the group. - struct skill_unit_group* group = skill->id2group(sce->val3); - sce->val3 = 0; - skill->del_unitgroup(group,ALC_MARK); - } - break; - case SC_HERMODE: - if(sce->val3 == BCT_SELF) - skill->clear_unitgroup(bl); - break; - case SC_BASILICA: //Clear the skill area. [Skotlex] - skill->clear_unitgroup(bl); - break; - case SC_TRICKDEAD: - if (vd) vd->dead_sit = 0; - break; - case SC_WARM: - case SC__MANHOLE: - if (sce->val4) { //Clear the group. - struct skill_unit_group* group = skill->id2group(sce->val4); - sce->val4 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); - } - break; - case SC_KAAHI: - //Delete timer if it exists. - if (sce->val4 != INVALID_TIMER) - timer->delete(sce->val4,status->kaahi_heal_timer); - break; - case SC_JAILED: - if(tid == INVALID_TIMER) - break; - //natural expiration. - if(sd && sd->mapindex == sce->val2) - pc->setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT); - break; //guess hes not in jail :P - case SC_HLIF_CHANGE: - if (tid == INVALID_TIMER) - break; - // "lose almost all their HP and SP" on natural expiration. - status->set_hp(bl, 10, 0); - status->set_sp(bl, 10, 0); - break; - case SC_AUTOTRADE: - if (tid == INVALID_TIMER) - break; - // Note: vending/buying is closed by unit_remove_map, no - // need to do it here. - map->quit(sd); - // Because map->quit calls status_change_end with tid -1 - // from here it's not neccesary to continue - return 1; - break; - case SC_STOP: - if( sce->val2 ) { - struct block_list* tbl = map->id2bl(sce->val2); - sce->val2 = 0; - if( tbl && (sc = status->get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id ) - status_change_end(tbl, SC_STOP, INVALID_TIMER); - } - break; - case SC_LKCONCENTRATION: - status_change_end(bl, SC_ENDURE, INVALID_TIMER); - break; - /** - * 3rd Stuff - **/ - case SC_MILLENNIUMSHIELD: - clif->millenniumshield(sd,0); - break; - case SC_HALLUCINATIONWALK: - sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill->get_time2(GC_HALLUCINATIONWALK,sce->val1)); - break; - case SC_WHITEIMPRISON: - { - struct block_list* src = map->id2bl(sce->val2); - if( tid == -1 || !src) - break; // Terminated by Damage - status_fix_damage(src,bl,400*sce->val1,clif->damage(bl,bl,0,0,400*sce->val1,0,0,0)); - } - break; - case SC_WUGDASH: - { - struct unit_data *ud = unit->bl2ud(bl); - if (ud) { - ud->state.running = 0; - if (ud->walktimer != -1) - unit->stop_walking(bl,1); + break; + /** + * 3rd Stuff + **/ + case SC_MILLENNIUMSHIELD: + clif->millenniumshield(sd,0); + break; + case SC_HALLUCINATIONWALK: + sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill->get_time2(GC_HALLUCINATIONWALK,sce->val1)); + break; + case SC_WHITEIMPRISON: + { + struct block_list* src = map->id2bl(sce->val2); + if( tid == -1 || !src) + break; // Terminated by Damage + status_fix_damage(src,bl,400*sce->val1,clif->damage(bl,bl,0,0,400*sce->val1,0,0,0)); } - } - break; - case SC_ADORAMUS: - status_change_end(bl, SC_BLIND, INVALID_TIMER); - break; - case SC__SHADOWFORM: - { - struct map_session_data *s_sd = map->id2sd(sce->val2); - if( !s_sd ) - break; - s_sd->shadowform_id = 0; - } - break; - case SC_SITDOWN_FORCE: - if( sd && pc_issit(sd) ) { - pc->setstand(sd); - clif->standing(bl); - } - break; - case SC_NEUTRALBARRIER_MASTER: - case SC_STEALTHFIELD_MASTER: - if( sce->val2 ) { - struct skill_unit_group* group = skill->id2group(sce->val2); - sce->val2 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); - } - break; - case SC_BANDING: - if(sce->val4) { - struct skill_unit_group *group = skill->id2group(sce->val4); - sce->val4 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill->del_unitgroup(group,ALC_MARK); - } - break; - case SC_CURSEDCIRCLE_ATKER: - if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target. - map->foreachinrange(status->change_timer_sub, bl, battle_config.area_size,BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, timer->gettick()); - break; - case SC_RAISINGDRAGON: - if( sd && sce->val2 && !pc_isdead(sd) ) { - int i; - i = min(sd->spiritball,5); - pc->delspiritball(sd, sd->spiritball, 0); - status_change_end(bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER); - while( i > 0 ) { - pc->addspiritball(sd, skill->get_time(MO_CALLSPIRITS, pc->checkskill(sd,MO_CALLSPIRITS)), 5); - --i; + break; + case SC_WUGDASH: + { + struct unit_data *ud = unit->bl2ud(bl); + if (ud) { + ud->state.running = 0; + if (ud->walktimer != -1) + unit->stop_walking(bl,1); + } } - } - break; - case SC_CURSEDCIRCLE_TARGET: - { - struct block_list *src = map->id2bl(sce->val2); - struct status_change *ssc = status->get_sc(src); - if( ssc && ssc->data[SC_CURSEDCIRCLE_ATKER] && --(ssc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){ - status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER); - clif->bladestop(bl, sce->val2, 0); - } - } - break; - case SC_BLOOD_SUCKER: - if( sce->val2 ){ + break; + case SC_ADORAMUS: + status_change_end(bl, SC_BLIND, INVALID_TIMER); + break; + case SC__SHADOWFORM: + { + struct map_session_data *s_sd = map->id2sd(sce->val2); + if( !s_sd ) + break; + s_sd->shadowform_id = 0; + } + break; + case SC_SITDOWN_FORCE: + if( sd && pc_issit(sd) ) { + pc->setstand(sd); + clif->standing(bl); + } + break; + case SC_NEUTRALBARRIER_MASTER: + case SC_STEALTHFIELD_MASTER: + if( sce->val2 ) { + struct skill_unit_group* group = skill->id2group(sce->val2); + sce->val2 = 0; + if( group ) /* might have been cleared before status ended, e.g. land protector */ + skill->del_unitgroup(group,ALC_MARK); + } + break; + case SC_BANDING: + if(sce->val4) { + struct skill_unit_group *group = skill->id2group(sce->val4); + sce->val4 = 0; + if( group ) /* might have been cleared before status ended, e.g. land protector */ + skill->del_unitgroup(group,ALC_MARK); + } + break; + case SC_CURSEDCIRCLE_ATKER: + if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target. + map->foreachinrange(status->change_timer_sub, bl, battle_config.area_size,BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, timer->gettick()); + break; + case SC_RAISINGDRAGON: + if( sd && sce->val2 && !pc_isdead(sd) ) { + int i; + i = min(sd->spiritball,5); + pc->delspiritball(sd, sd->spiritball, 0); + status_change_end(bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER); + while( i > 0 ) { + pc->addspiritball(sd, skill->get_time(MO_CALLSPIRITS, pc->checkskill(sd,MO_CALLSPIRITS)), 5); + --i; + } + } + break; + case SC_CURSEDCIRCLE_TARGET: + { struct block_list *src = map->id2bl(sce->val2); - if(src) { - struct status_change *ssc = status->get_sc(src); - ssc->bs_counter--; + struct status_change *ssc = status->get_sc(src); + if( ssc && ssc->data[SC_CURSEDCIRCLE_ATKER] && --(ssc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){ + status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER); + clif->bladestop(bl, sce->val2, 0); } } - break; - case SC_KYOUGAKU: - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_ACTIVE_MONSTER_TRANSFORM); - break; - case SC_CLAIRVOYANCE: - calc_flag = SCB_ALL;/* required for overlapping */ - break; - case SC_FULL_THROTTLE: - sc_start(bl,SC_REBOUND,100,sce->val1,skill->get_time2(ALL_FULL_THROTTLE,sce->val1)); - break; - case SC_MONSTER_TRANSFORM: - if( sce->val2 ) - status_change_end(bl, (sc_type)sce->val2, INVALID_TIMER); - break; - case SC_ITEMSCRIPT: - if( sd ) { - switch( sce->val1 ) { - //case ITEMID_PHREEONI_CARD: - //case ITEMID_GHOSTRING_CARD: - case ITEMID_TAO_GUNKA_CARD: - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_TAOGUNKA); - break; - case ITEMID_MISTRESS_CARD: - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_MISTRESS); - break; - case ITEMID_ORC_HERO_CARD: - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_ORCHERO); - break; - case ITEMID_ORC_LOAD_CARD: - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_ORCLORD); - break; + break; + case SC_BLOOD_SUCKER: + if( sce->val2 ){ + struct block_list *src = map->id2bl(sce->val2); + if(src) { + struct status_change *ssc = status->get_sc(src); + ssc->bs_counter--; + } } - } - break; + break; + case SC_KYOUGAKU: + clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_ACTIVE_MONSTER_TRANSFORM); + break; + case SC_CLAIRVOYANCE: + calc_flag = SCB_ALL;/* required for overlapping */ + break; + case SC_FULL_THROTTLE: + sc_start(bl,SC_REBOUND,100,sce->val1,skill->get_time2(ALL_FULL_THROTTLE,sce->val1)); + break; + case SC_MONSTER_TRANSFORM: + if( sce->val2 ) + status_change_end(bl, (sc_type)sce->val2, INVALID_TIMER); + break; + case SC_ITEMSCRIPT: + if( sd ) { + switch( sce->val1 ) { + //case ITEMID_PHREEONI_CARD: + //case ITEMID_GHOSTRING_CARD: + case ITEMID_TAO_GUNKA_CARD: + clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_TAOGUNKA); + break; + case ITEMID_MISTRESS_CARD: + clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_MISTRESS); + break; + case ITEMID_ORC_HERO_CARD: + clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_ORCHERO); + break; + case ITEMID_ORC_LOAD_CARD: + clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_MVPCARD_ORCLORD); + break; + } + } + break; } opt_flag = 1; @@ -9947,6 +9954,12 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const opt_flag = 0; } +#ifdef ANTI_MAYAP_CHEAT + if( invisible && !(sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE)) ) { + clif->fixpos(bl); + } +#endif + if (calc_flag&SCB_DYE) { //Restore DYE color if (vd && !vd->cloth_color && sce->val4) clif->changelook(bl,LOOK_CLOTHES_COLOR,sce->val4); @@ -10059,767 +10072,767 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) { } while(0) switch(type) { - case SC_MAXIMIZEPOWER: - case SC_CLOAKING: - if(!status->charge(bl, 0, 1)) - break; //Not enough SP to continue. - sc_timer_next(sce->val2+tick, status->change_timer, bl->id, data); - return 0; - - case SC_CHASEWALK: - if(!status->charge(bl, 0, sce->val4)) - break; //Not enough SP to continue. + case SC_MAXIMIZEPOWER: + case SC_CLOAKING: + if(!status->charge(bl, 0, 1)) + break; //Not enough SP to continue. + sc_timer_next(sce->val2+tick, status->change_timer, bl->id, data); + return 0; - if (!sc->data[SC_CHASEWALK2]) { - sc_start(bl, SC_CHASEWALK2,100,1<<(sce->val1-1), - (sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration - * skill->get_time2(status->sc2skill(type),sce->val1)); - } - sc_timer_next(sce->val2+tick, status->change_timer, bl->id, data); - return 0; - break; + case SC_CHASEWALK: + if(!status->charge(bl, 0, sce->val4)) + break; //Not enough SP to continue. - case SC_SKA: - if(--(sce->val2)>0) { - sce->val3 = rnd()%100; //Random defense. - sc_timer_next(1000+tick, status->change_timer,bl->id, data); + if (!sc->data[SC_CHASEWALK2]) { + sc_start(bl, SC_CHASEWALK2,100,1<<(sce->val1-1), + (sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration + * skill->get_time2(status->sc2skill(type),sce->val1)); + } + sc_timer_next(sce->val2+tick, status->change_timer, bl->id, data); return 0; - } - break; + break; - case SC_HIDING: - if(--(sce->val2)>0) { - if(sce->val2 % sce->val4 == 0 && !status->charge(bl, 0, 1)) - break; //Fail if it's time to substract SP and there isn't. + case SC_SKA: + if(--(sce->val2)>0) { + sce->val3 = rnd()%100; //Random defense. + sc_timer_next(1000+tick, status->change_timer,bl->id, data); + return 0; + } + break; - sc_timer_next(1000+tick, status->change_timer,bl->id, data); - return 0; - } - break; + case SC_HIDING: + if(--(sce->val2)>0) { + if(sce->val2 % sce->val4 == 0 && !status->charge(bl, 0, 1)) + break; //Fail if it's time to substract SP and there isn't. - case SC_SIGHT: - case SC_RUWACH: - case SC_WZ_SIGHTBLASTER: - if(type == SC_WZ_SIGHTBLASTER) - map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick); - else - map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick); + sc_timer_next(1000+tick, status->change_timer,bl->id, data); + return 0; + } + break; - if( --(sce->val2)>0 ){ - sce->val4 += 250; // use for Shadow Form 2 seconds checking. - sc_timer_next(250+tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_SIGHT: + case SC_RUWACH: + case SC_WZ_SIGHTBLASTER: + if(type == SC_WZ_SIGHTBLASTER) + map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick); + else + map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick); - case SC_PROVOKE: - if(sce->val2) { //Auto-provoke (it is ended in status->heal) - sc_timer_next(1000*60+tick, status->change_timer, bl->id, data ); - return 0; - } - break; + if( --(sce->val2)>0 ){ + sce->val4 += 250; // use for Shadow Form 2 seconds checking. + sc_timer_next(250+tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_STONE: - if(sc->opt1 == OPT1_STONEWAIT && sce->val3) { - sce->val4 = 0; - unit->stop_walking(bl,1); - unit->stop_attack(bl); - sc->opt1 = OPT1_STONE; - clif->changeoption(bl); - sc_timer_next(1000+tick, status->change_timer, bl->id, data ); - status_calc_bl(bl, status->ChangeFlagTable[type]); - return 0; - } - if(--(sce->val3) > 0) { - if(++(sce->val4)%5 == 0 && st->hp > st->max_hp/4) - status_percent_damage(NULL, bl, 1, 0, false); - sc_timer_next(1000+tick, status->change_timer, bl->id, data ); - return 0; - } - break; + case SC_PROVOKE: + if(sce->val2) { //Auto-provoke (it is ended in status->heal) + sc_timer_next(1000*60+tick, status->change_timer, bl->id, data ); + return 0; + } + break; - case SC_POISON: - if(st->hp <= max(st->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left. + case SC_STONE: + if(sc->opt1 == OPT1_STONEWAIT && sce->val3) { + sce->val4 = 0; + unit->stop_walking(bl,1); + unit->stop_attack(bl); + sc->opt1 = OPT1_STONE; + clif->changeoption(bl); + sc_timer_next(1000+tick, status->change_timer, bl->id, data ); + status_calc_bl(bl, status->ChangeFlagTable[type]); + return 0; + } + if(--(sce->val3) > 0) { + if(++(sce->val4)%5 == 0 && st->hp > st->max_hp/4) + status_percent_damage(NULL, bl, 1, 0, false); + sc_timer_next(1000+tick, status->change_timer, bl->id, data ); + return 0; + } break; - case SC_DPOISON: - if (--(sce->val3) > 0) { - if (!sc->data[SC_SLOWPOISON]) { - if( sce->val2 && bl->type == BL_MOB ) { - struct block_list* src = map->id2bl(sce->val2); - if( src ) - mob->log_damage((TBL_MOB*)bl,src,sce->val4); + + case SC_POISON: + if(st->hp <= max(st->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left. + break; + case SC_DPOISON: + if (--(sce->val3) > 0) { + if (!sc->data[SC_SLOWPOISON]) { + if( sce->val2 && bl->type == BL_MOB ) { + struct block_list* src = map->id2bl(sce->val2); + if( src ) + mob->log_damage((TBL_MOB*)bl,src,sce->val4); + } + map->freeblock_lock(); + status_zap(bl, sce->val4, 0); + if (sc->data[type]) { // Check if the status still last ( can be dead since then ). + sc_timer_next(1000 + tick, status->change_timer, bl->id, data ); + } + map->freeblock_unlock(); + } + return 0; + } + break; + + case SC_TENSIONRELAX: + if(st->max_hp > st->hp && --(sce->val3) > 0) { + sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); + return 0; + } + break; + + case SC_KNOWLEDGE: + if (!sd) break; + if(bl->m == sd->feel_map[0].m || + bl->m == sd->feel_map[1].m || + bl->m == sd->feel_map[2].m) + { //Timeout will be handled by pc->setpos + sce->timer = INVALID_TIMER; + return 0; + } + break; + + case SC_BLOODING: + if (--(sce->val4) >= 0) { + int hp = rnd()%600 + 200; + struct block_list* src = map->id2bl(sce->val2); + if( src && bl && bl->type == BL_MOB ) { + mob->log_damage((TBL_MOB*)bl,src,sd||hp<st->hp?hp:st->hp-1); } map->freeblock_lock(); - status_zap(bl, sce->val4, 0); - if (sc->data[type]) { // Check if the status still last ( can be dead since then ). - sc_timer_next(1000 + tick, status->change_timer, bl->id, data ); + status_fix_damage(src, bl, sd||hp<st->hp?hp:st->hp-1, 1); + if( sc->data[type] ) { + if( st->hp == 1 ) { + map->freeblock_unlock(); + break; + } + sc_timer_next(10000 + tick, status->change_timer, bl->id, data); } map->freeblock_unlock(); + return 0; } - return 0; - } - break; + break; - case SC_TENSIONRELAX: - if(st->max_hp > st->hp && --(sce->val3) > 0) { - sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_S_LIFEPOTION: + case SC_L_LIFEPOTION: + if( sd && --(sce->val4) >= 0 ) { + // val1 < 0 = per max% | val1 > 0 = exact amount + int hp = 0; + if( st->hp < st->max_hp ) + hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ; + status->heal(bl, hp, 0, 2); + sc_timer_next((sce->val2 * 1000) + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_KNOWLEDGE: - if (!sd) break; - if(bl->m == sd->feel_map[0].m || - bl->m == sd->feel_map[1].m || - bl->m == sd->feel_map[2].m) - { //Timeout will be handled by pc->setpos - sce->timer = INVALID_TIMER; - return 0; - } - break; + case SC_CASH_BOSS_ALARM: + if( sd && --(sce->val4) >= 0 ) { + struct mob_data *boss_md = map->id2boss(sce->val1); + if( boss_md && sd->bl.m == boss_md->bl.m ) { + clif->bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap + if (boss_md->bl.prev != NULL) { + sc_timer_next(5000 + tick, status->change_timer, bl->id, data); + return 0; + } + } + } + break; - case SC_BLOODING: - if (--(sce->val4) >= 0) { - int hp = rnd()%600 + 200; - struct block_list* src = map->id2bl(sce->val2); - if( src && bl && bl->type == BL_MOB ) { - mob->log_damage((TBL_MOB*)bl,src,sd||hp<st->hp?hp:st->hp-1); - } - map->freeblock_lock(); - status_fix_damage(src, bl, sd||hp<st->hp?hp:st->hp-1, 1); - if( sc->data[type] ) { - if( st->hp == 1 ) { - map->freeblock_unlock(); + case SC_DANCING: //SP consumption by time of dancing skills + { + int s = 0; + int sp = 1; + if (--sce->val3 <= 0) + break; + switch(sce->val1&0xFFFF){ + case BD_RICHMANKIM: + case BD_DRUMBATTLEFIELD: + case BD_RINGNIBELUNGEN: + case BD_SIEGFRIED: + case BA_DISSONANCE: + case BA_ASSASSINCROSS: + case DC_UGLYDANCE: + s=3; + break; + case BD_LULLABY: + case BD_ETERNALCHAOS: + case BD_ROKISWEIL: + case DC_FORTUNEKISS: + s=4; + break; + case CG_HERMODE: + case BD_INTOABYSS: + case BA_WHISTLE: + case DC_HUMMING: + case BA_POEMBRAGI: + case DC_SERVICEFORYOU: + s=5; + break; + case BA_APPLEIDUN: + #ifdef RENEWAL + s=5; + #else + s=6; + #endif + break; + case CG_MOONLIT: + //Moonlit's cost is 4sp*skill_lv [Skotlex] + sp= 4*(sce->val1>>16); + //Upkeep is also every 10 secs. + case DC_DONTFORGETME: + s=10; break; } - sc_timer_next(10000 + tick, status->change_timer, bl->id, data); + if( s != 0 && sce->val3 % s == 0 ) { + if (sc->data[SC_LONGING]) + sp*= 3; + if (!status->charge(bl, 0, sp)) + break; + } + sc_timer_next(1000+tick, status->change_timer, bl->id, data); + return 0; } - map->freeblock_unlock(); - return 0; - } - break; - - case SC_S_LIFEPOTION: - case SC_L_LIFEPOTION: - if( sd && --(sce->val4) >= 0 ) { - // val1 < 0 = per max% | val1 > 0 = exact amount - int hp = 0; - if( st->hp < st->max_hp ) - hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ; - status->heal(bl, hp, 0, 2); - sc_timer_next((sce->val2 * 1000) + tick, status->change_timer, bl->id, data); - return 0; - } - break; + break; + case SC_BERSERK: + // 5% every 10 seconds [DracoRPG] + if( --( sce->val3 ) > 0 && status->charge(bl, sce->val2, 0) && st->hp > 100 ) { + sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_CASH_BOSS_ALARM: - if( sd && --(sce->val4) >= 0 ) { - struct mob_data *boss_md = map->id2boss(sce->val1); - if( boss_md && sd->bl.m == boss_md->bl.m ) { - clif->bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap - if (boss_md->bl.prev != NULL) { - sc_timer_next(5000 + tick, status->change_timer, bl->id, data); + case SC_NOCHAT: + if(sd) { + sd->status.manner++; + clif->changestatus(sd,SP_MANNER,sd->status.manner); + clif->updatestatus(sd,SP_MANNER); + if (sd->status.manner < 0) { + //Every 60 seconds your manner goes up by 1 until it gets back to 0. + sc_timer_next(60000+tick, status->change_timer, bl->id, data); return 0; } } - } - break; + break; - case SC_DANCING: //SP consumption by time of dancing skills - { - int s = 0; - int sp = 1; - if (--sce->val3 <= 0) - break; - switch(sce->val1&0xFFFF){ - case BD_RICHMANKIM: - case BD_DRUMBATTLEFIELD: - case BD_RINGNIBELUNGEN: - case BD_SIEGFRIED: - case BA_DISSONANCE: - case BA_ASSASSINCROSS: - case DC_UGLYDANCE: - s=3; - break; - case BD_LULLABY: - case BD_ETERNALCHAOS: - case BD_ROKISWEIL: - case DC_FORTUNEKISS: - s=4; - break; - case CG_HERMODE: - case BD_INTOABYSS: - case BA_WHISTLE: - case DC_HUMMING: - case BA_POEMBRAGI: - case DC_SERVICEFORYOU: - s=5; - break; - case BA_APPLEIDUN: -#ifdef RENEWAL - s=5; -#else - s=6; -#endif - break; - case CG_MOONLIT: - //Moonlit's cost is 4sp*skill_lv [Skotlex] - sp= 4*(sce->val1>>16); - //Upkeep is also every 10 secs. - case DC_DONTFORGETME: - s=10; - break; + case SC_SPLASHER: + // custom Venom Splasher countdown timer + //if (sce->val4 % 1000 == 0) { + // char counter[10]; + // snprintf (counter, 10, "%d", sce->val4/1000); + // clif->message(bl, counter); + //} + if((sce->val4 -= 500) > 0) { + sc_timer_next(500 + tick, status->change_timer, bl->id, data); + return 0; } - if( s != 0 && sce->val3 % s == 0 ) { - if (sc->data[SC_LONGING]) - sp*= 3; - if (!status->charge(bl, 0, sp)) - break; + break; + + case SC_MARIONETTE_MASTER: + case SC_MARIONETTE: + { + struct block_list *pbl = map->id2bl(sce->val1); + if( pbl && check_distance_bl(bl, pbl, 7) ) { + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } } - sc_timer_next(1000+tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_BERSERK: - // 5% every 10 seconds [DracoRPG] - if( --( sce->val3 ) > 0 && status->charge(bl, sce->val2, 0) && st->hp > 100 ) { - sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); - return 0; - } - break; + break; - case SC_NOCHAT: - if(sd) { - sd->status.manner++; - clif->changestatus(sd,SP_MANNER,sd->status.manner); - clif->updatestatus(sd,SP_MANNER); - if (sd->status.manner < 0) { - //Every 60 seconds your manner goes up by 1 until it gets back to 0. - sc_timer_next(60000+tick, status->change_timer, bl->id, data); + case SC_GOSPEL: + if(sce->val4 == BCT_SELF && --(sce->val2) > 0) { + int hp, sp; + hp = (sce->val1 > 5) ? 45 : 30; + sp = (sce->val1 > 5) ? 35 : 20; + if(!status->charge(bl, hp, sp)) + break; + sc_timer_next(10000+tick, status->change_timer, bl->id, data); return 0; } - } - break; - - case SC_SPLASHER: - // custom Venom Splasher countdown timer - //if (sce->val4 % 1000 == 0) { - // char counter[10]; - // snprintf (counter, 10, "%d", sce->val4/1000); - // clif->message(bl, counter); - //} - if((sce->val4 -= 500) > 0) { - sc_timer_next(500 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + break; - case SC_MARIONETTE_MASTER: - case SC_MARIONETTE: - { - struct block_list *pbl = map->id2bl(sce->val1); - if( pbl && check_distance_bl(bl, pbl, 7) ) { - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + case SC_JAILED: + if(sce->val1 == INT_MAX || --(sce->val1) > 0) { + sc_timer_next(60000+tick, status->change_timer, bl->id,data); return 0; } - } - break; - - case SC_GOSPEL: - if(sce->val4 == BCT_SELF && --(sce->val2) > 0) { - int hp, sp; - hp = (sce->val1 > 5) ? 45 : 30; - sp = (sce->val1 > 5) ? 35 : 20; - if(!status->charge(bl, hp, sp)) - break; - sc_timer_next(10000+tick, status->change_timer, bl->id, data); - return 0; - } - break; - - case SC_JAILED: - if(sce->val1 == INT_MAX || --(sce->val1) > 0) { - sc_timer_next(60000+tick, status->change_timer, bl->id,data); - return 0; - } - break; - - case SC_BLIND: - if(sc->data[SC_FOGWALL]) { - //Blind lasts forever while you are standing on the fog. - sc_timer_next(5000+tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_ABUNDANCE: - if(--(sce->val4) > 0) { - status->heal(bl,0,60,0); - sc_timer_next(10000+tick, status->change_timer, bl->id, data); - } - break; + break; - case SC_PYREXIA: - if( --(sce->val4) > 0 ) { - map->freeblock_lock(); - clif->damage(bl,bl,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0); - status_fix_damage(NULL,bl,100,0); - if( sc->data[type] ) { - sc_timer_next(3000+tick,status->change_timer,bl->id,data); + case SC_BLIND: + if(sc->data[SC_FOGWALL]) { + //Blind lasts forever while you are standing on the fog. + sc_timer_next(5000+tick, status->change_timer, bl->id, data); + return 0; } - map->freeblock_unlock(); - return 0; - } - break; - - case SC_LEECHESEND: - if( --(sce->val4) > 0 ) { - int damage = st->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) - damage += st->vit * (sce->val1 - 3); - unit->skillcastcancel(bl,2); - map->freeblock_lock(); - status->damage(bl, bl, damage, 0, clif->damage(bl,bl,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1); - if( sc->data[type] ) { - sc_timer_next(1000 + tick, status->change_timer, bl->id, data ); - } - map->freeblock_unlock(); - return 0; - } - break; + break; + case SC_ABUNDANCE: + if(--(sce->val4) > 0) { + status->heal(bl,0,60,0); + sc_timer_next(10000+tick, status->change_timer, bl->id, data); + } + break; - case SC_MAGICMUSHROOM: - if( --(sce->val4) > 0 ) { - bool flag = 0; - int damage = st->max_hp * 3 / 100; - if( st->hp <= damage ) - damage = st->hp - 1; // Cannot Kill + case SC_PYREXIA: + if( --(sce->val4) > 0 ) { + map->freeblock_lock(); + clif->damage(bl,bl,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0); + status_fix_damage(NULL,bl,100,0); + if( sc->data[type] ) { + sc_timer_next(3000+tick,status->change_timer,bl->id,data); + } + map->freeblock_unlock(); + return 0; + } + break; - if( damage > 0 ) { // 3% Damage each 4 seconds + case SC_LEECHESEND: + if( --(sce->val4) > 0 ) { + int damage = st->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) + damage += st->vit * (sce->val1 - 3); + unit->skillcastcancel(bl,2); map->freeblock_lock(); - status_zap(bl,damage,0); - flag = !sc->data[type]; // Killed? Should not + status->damage(bl, bl, damage, 0, clif->damage(bl,bl,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1); + if( sc->data[type] ) { + sc_timer_next(1000 + tick, status->change_timer, bl->id, data ); + } map->freeblock_unlock(); + return 0; } + break; - if( !flag ) { // Random Skill Cast - if (sd && !pc_issit(sd)) { //can't cast if sit - int mushroom_skill_id = 0, i; - unit->stop_attack(bl); - unit->skillcastcancel(bl,0); - do { - i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB; - mushroom_skill_id = skill->magicmushroom_db[i].skill_id; - } - while( mushroom_skill_id == 0 ); + case SC_MAGICMUSHROOM: + if( --(sce->val4) > 0 ) { + bool flag = 0; + int damage = st->max_hp * 3 / 100; + if( st->hp <= damage ) + damage = st->hp - 1; // Cannot Kill + + if( damage > 0 ) { // 3% Damage each 4 seconds + map->freeblock_lock(); + status_zap(bl,damage,0); + flag = !sc->data[type]; // Killed? Should not + map->freeblock_unlock(); + } - switch( skill->get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage - case CAST_GROUND: - skill->castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0); - break; - case CAST_NODAMAGE: - skill->castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0); - break; - case CAST_DAMAGE: - skill->castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0); - break; + if( !flag ) { // Random Skill Cast + if (sd && !pc_issit(sd)) { //can't cast if sit + int mushroom_skill_id = 0, i; + unit->stop_attack(bl); + unit->skillcastcancel(bl,0); + do { + i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB; + mushroom_skill_id = skill->magicmushroom_db[i].skill_id; + } + while( mushroom_skill_id == 0 ); + + switch( skill->get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage + case CAST_GROUND: + skill->castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0); + break; + case CAST_NODAMAGE: + skill->castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0); + break; + case CAST_DAMAGE: + skill->castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0); + break; + } } + + clif->emotion(bl,E_HEH); + sc_timer_next(4000+tick,status->change_timer,bl->id,data); } + return 0; + } + break; - clif->emotion(bl,E_HEH); - sc_timer_next(4000+tick,status->change_timer,bl->id,data); + case SC_TOXIN: + if( --(sce->val4) > 0 ) { + //Damage is every 10 seconds including 3%sp drain. + map->freeblock_lock(); + clif->damage(bl,bl,status_get_amotion(bl),1,1,0,0,0); + status->damage(NULL, bl, 1, st->max_sp * 3 / 100, 0, 0); //cancel dmg only if cancelable + if( sc->data[type] ) { + sc_timer_next(10000 + tick, status->change_timer, bl->id, data ); + } + map->freeblock_unlock(); + return 0; } - return 0; - } - break; + break; - case SC_TOXIN: - if( --(sce->val4) > 0 ) { - //Damage is every 10 seconds including 3%sp drain. - map->freeblock_lock(); - clif->damage(bl,bl,status_get_amotion(bl),1,1,0,0,0); - status->damage(NULL, bl, 1, st->max_sp * 3 / 100, 0, 0); //cancel dmg only if cancelable - if( sc->data[type] ) { - sc_timer_next(10000 + tick, status->change_timer, bl->id, data ); + case SC_OBLIVIONCURSE: + if( --(sce->val4) > 0 ) { + clif->emotion(bl,E_WHAT); + sc_timer_next(3000 + tick, status->change_timer, bl->id, data ); + return 0; } - map->freeblock_unlock(); - return 0; - } - break; + break; - case SC_OBLIVIONCURSE: - if( --(sce->val4) > 0 ) { - clif->emotion(bl,E_WHAT); - sc_timer_next(3000 + tick, status->change_timer, bl->id, data ); - return 0; - } - break; + case SC_WEAPONBLOCKING: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl,0,3) ) + break; + sc_timer_next(3000+tick,status->change_timer,bl->id,data); + return 0; + } + break; - case SC_WEAPONBLOCKING: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl,0,3) ) + case SC_CLOAKINGEXCEED: + if(!status->charge(bl,0,10-sce->val1)) break; - sc_timer_next(3000+tick,status->change_timer,bl->id,data); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); return 0; - } - break; - case SC_CLOAKINGEXCEED: - if(!status->charge(bl,0,10-sce->val1)) + case SC_RENOVATIO: + if( --(sce->val4) > 0 ) { + int heal = st->max_hp * 3 / 100; + if( sc && sc->data[SC_AKAITSUKI] && heal ) + heal = ~heal + 1; + status->heal(bl, heal, 0, 2); + sc_timer_next(5000 + tick, status->change_timer, bl->id, data); + return 0; + } break; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - case SC_RENOVATIO: - if( --(sce->val4) > 0 ) { - int heal = st->max_hp * 3 / 100; - if( sc && sc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - status->heal(bl, heal, 0, 2); - sc_timer_next(5000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_BURNING: + if( --(sce->val4) > 0 ) { + struct block_list *src = map->id2bl(sce->val3); + int damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP) - case SC_BURNING: - if( --(sce->val4) > 0 ) { - struct block_list *src = map->id2bl(sce->val3); - int damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP) + map->freeblock_lock(); + clif->damage(bl,bl,0,0,damage,1,9,0); //damage is like endure effect with no walk delay + status->damage(src, bl, damage, 0, 0, 1); - map->freeblock_lock(); - clif->damage(bl,bl,0,0,damage,1,9,0); //damage is like endure effect with no walk delay - status->damage(src, bl, damage, 0, 0, 1); + if( sc->data[type]){ // Target still lives. [LimitLine] + sc_timer_next(3000 + tick, status->change_timer, bl->id, data); + } + map->freeblock_unlock(); + return 0; + } + break; - if( sc->data[type]){ // Target still lives. [LimitLine] - sc_timer_next(3000 + tick, status->change_timer, bl->id, data); + case SC_FEAR: + if( --(sce->val4) > 0 ) { + if( sce->val2 > 0 ) + sce->val2--; + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; } - map->freeblock_unlock(); - return 0; - } - break; + break; - case SC_FEAR: - if( --(sce->val4) > 0 ) { - if( sce->val2 > 0 ) - sce->val2--; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_SUMMON1: + case SC_SUMMON2: + case SC_SUMMON3: + case SC_SUMMON4: + case SC_SUMMON5: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl, 0, 1) ) + break; + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_SUMMON1: - case SC_SUMMON2: - case SC_SUMMON3: - case SC_SUMMON4: - case SC_SUMMON5: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl, 0, 1) ) + case SC_READING_SB: + if( !status->charge(bl, 0, sce->val2) ) { + int i; + for(i = SC_SPELLBOOK1; i <= SC_SPELLBOOK7; i++) // Also remove stored spell as well. + status_change_end(bl, (sc_type)i, INVALID_TIMER); break; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + } + sc_timer_next(5000 + tick, status->change_timer, bl->id, data); return 0; - } - break; - case SC_READING_SB: - if( !status->charge(bl, 0, sce->val2) ) { - int i; - for(i = SC_SPELLBOOK1; i <= SC_SPELLBOOK7; i++) // Also remove stored spell as well. - status_change_end(bl, (sc_type)i, INVALID_TIMER); + case SC_ELECTRICSHOCKER: + if( --(sce->val4) > 0 ) { + status->charge(bl, 0, st->max_sp / 100 * sce->val1 ); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } break; - } - sc_timer_next(5000 + tick, status->change_timer, bl->id, data); - return 0; - - case SC_ELECTRICSHOCKER: - if( --(sce->val4) > 0 ) { - status->charge(bl, 0, st->max_sp / 100 * sce->val1 ); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - - case SC_CAMOUFLAGE: - if(--(sce->val4) > 0) { - status->charge(bl,0,7 - sce->val1); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC__REPRODUCE: - if(!status->charge(bl, 0, 1)) + case SC_CAMOUFLAGE: + if(--(sce->val4) > 0) { + status->charge(bl,0,7 - sce->val1); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } break; - sc_timer_next(1000+tick, status->change_timer, bl->id, data); - return 0; - case SC__SHADOWFORM: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl, 0, sce->val1 - (sce->val1 - 1)) ) + case SC__REPRODUCE: + if(!status->charge(bl, 0, 1)) break; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + sc_timer_next(1000+tick, status->change_timer, bl->id, data); return 0; - } - break; - case SC__INVISIBILITY: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl, 0, (st->sp * 6 - sce->val1) / 100) )// 6% - skill_lv. - break; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC__SHADOWFORM: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl, 0, sce->val1 - (sce->val1 - 1)) ) + break; + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_STRIKING: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl,0, sce->val1 ) ) - break; - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_VACUUM_EXTREME: - if( --(sce->val4) > 0 ) { - sc_timer_next(100 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_BLOOD_SUCKER: - if( --(sce->val4) > 0 ) { - struct block_list *src = map->id2bl(sce->val2); - int damage; - if( !src || (src && (status->isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) ) - break; - map->freeblock_lock(); - damage = sce->val3; - status->damage(src, bl, damage, 0, clif->damage(bl,bl,st->amotion,st->dmotion+200,damage,1,0,0), 1); - unit->skillcastcancel(bl,1); - if ( sc->data[type] ) { + case SC__INVISIBILITY: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl, 0, (st->sp * 6 - sce->val1) / 100) )// 6% - skill_lv. + break; sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; } - map->freeblock_unlock(); - status->heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level - return 0; - } - break; + break; - case SC_SIREN: - if( --(sce->val4) > 0 ) { - clif->emotion(bl,E_LV); - sc_timer_next(2000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_STRIKING: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl,0, sce->val1 ) ) + break; + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_VACUUM_EXTREME: + if( --(sce->val4) > 0 ) { + sc_timer_next(100 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_BLOOD_SUCKER: + if( --(sce->val4) > 0 ) { + struct block_list *src = map->id2bl(sce->val2); + int damage; + if( !src || (src && (status->isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) ) + break; + map->freeblock_lock(); + damage = sce->val3; + status->damage(src, bl, damage, 0, clif->damage(bl,bl,st->amotion,st->dmotion+200,damage,1,0,0), 1); + unit->skillcastcancel(bl,1); + if ( sc->data[type] ) { + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + } + map->freeblock_unlock(); + status->heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level + return 0; + } + break; - case SC_DEEP_SLEEP: - if( --(sce->val4) > 0 ) { - // Recovers 1% HP/SP every 2 seconds. - status->heal(bl, st->max_hp / 100, st->max_sp / 100, 2); - sc_timer_next(2000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_SIREN: + if( --(sce->val4) > 0 ) { + clif->emotion(bl,E_LV); + sc_timer_next(2000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_SIRCLEOFNATURE: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl,0,sce->val2) ) - break; - status->heal(bl, sce->val3, 0, 1); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_DEEP_SLEEP: + if( --(sce->val4) > 0 ) { + // Recovers 1% HP/SP every 2 seconds. + status->heal(bl, st->max_hp / 100, st->max_sp / 100, 2); + sc_timer_next(2000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_SONG_OF_MANA: - if( --(sce->val4) > 0 ) { - status->heal(bl,0,sce->val3,3); - sc_timer_next(3000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + case SC_SIRCLEOFNATURE: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl,0,sce->val2) ) + break; + status->heal(bl, sce->val3, 0, 1); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_SONG_OF_MANA: + if( --(sce->val4) > 0 ) { + status->heal(bl,0,sce->val3,3); + sc_timer_next(3000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_SATURDAY_NIGHT_FEVER: - // 1% HP/SP drain every val4 seconds [Jobbie] - if( --(sce->val3) > 0 ) { - int hp = st->hp / 100; - int sp = st->sp / 100; - if( !status->charge(bl, hp, sp) ) - break; - sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_COLD: - if( --(sce->val4) > 0 ) { - // Drains 2% of HP and 1% of SP every seconds. - if( bl->type != BL_MOB) // doesn't work on mobs - status->charge(bl, st->max_hp * 2 / 100, st->max_sp / 100); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - - case SC_FORCEOFVANGUARD: - if( !status->charge(bl, 0, (24 - 4 * sce->val1)) ) + case SC_SATURDAY_NIGHT_FEVER: + // 1% HP/SP drain every val4 seconds [Jobbie] + if( --(sce->val3) > 0 ) { + int hp = st->hp / 100; + int sp = st->sp / 100; + if( !status->charge(bl, hp, sp) ) + break; + sc_timer_next(sce->val4+tick, status->change_timer, bl->id, data); + return 0; + } break; - sc_timer_next(10000 + tick, status->change_timer, bl->id, data); - return 0; - case SC_BANDING: - if( status->charge(bl, 0, 7 - sce->val1) ) { - if( sd ) pc->banding(sd, sce->val1); - sc_timer_next(5000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - - case SC_LG_REFLECTDAMAGE: - if( --(sce->val4) > 0 ) { - if( !status->charge(bl,0,sce->val3) ) + case SC_COLD: + if( --(sce->val4) > 0 ) { + // Drains 2% of HP and 1% of SP every seconds. + if( bl->type != BL_MOB) // doesn't work on mobs + status->charge(bl, st->max_hp * 2 / 100, st->max_sp / 100); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + + case SC_FORCEOFVANGUARD: + if( !status->charge(bl, 0, (24 - 4 * sce->val1)) ) break; sc_timer_next(10000 + tick, status->change_timer, bl->id, data); return 0; - } - break; - case SC_OVERHEAT_LIMITPOINT: - if( --(sce->val1) > 0 ) { // Cooling - sc_timer_next(30000 + tick, status->change_timer, bl->id, data); - } - break; + case SC_BANDING: + if( status->charge(bl, 0, 7 - sce->val1) ) { + if( sd ) pc->banding(sd, sce->val1); + sc_timer_next(5000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; - case SC_OVERHEAT: - { - int damage = st->max_hp / 100; // Suggestion 1% each second - if( damage >= st->hp ) damage = st->hp - 1; // Do not kill, just keep you with 1 hp minimum - map->freeblock_lock(); - status_fix_damage(NULL,bl,damage,clif->damage(bl,bl,0,0,damage,0,0,0)); - if( sc->data[type] ) { - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + case SC_LG_REFLECTDAMAGE: + if( --(sce->val4) > 0 ) { + if( !status->charge(bl,0,sce->val3) ) + break; + sc_timer_next(10000 + tick, status->change_timer, bl->id, data); + return 0; } - map->freeblock_unlock(); - } - break; + break; - case SC_MAGNETICFIELD: - { - if( --(sce->val3) <= 0 ) - break; // Time out - if( sce->val2 == bl->id ) { - if( !status->charge(bl,0,14 + (3 * sce->val1)) ) - break; // No more SP status should end, and in the next second will end for the other affected players - } else { - struct block_list *src = map->id2bl(sce->val2); - struct status_change *ssc; - if( !src || (ssc = status->get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] ) - break; // Source no more under Magnetic Field + case SC_OVERHEAT_LIMITPOINT: + if( --(sce->val1) > 0 ) { // Cooling + sc_timer_next(30000 + tick, status->change_timer, bl->id, data); } - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - } - break; + break; + + case SC_OVERHEAT: + { + int damage = st->max_hp / 100; // Suggestion 1% each second + if( damage >= st->hp ) damage = st->hp - 1; // Do not kill, just keep you with 1 hp minimum + map->freeblock_lock(); + status_fix_damage(NULL,bl,damage,clif->damage(bl,bl,0,0,damage,0,0,0)); + if( sc->data[type] ) { + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + } + map->freeblock_unlock(); + } + break; - case SC_INSPIRATION: - if(--(sce->val4) > 0) { - int hp = st->max_hp * (7-sce->val1) / 100; - int sp = st->max_sp * (9-sce->val1) / 100; + case SC_MAGNETICFIELD: + { + if( --(sce->val3) <= 0 ) + break; // Time out + if( sce->val2 == bl->id ) { + if( !status->charge(bl,0,14 + (3 * sce->val1)) ) + break; // No more SP status should end, and in the next second will end for the other affected players + } else { + struct block_list *src = map->id2bl(sce->val2); + struct status_change *ssc; + if( !src || (ssc = status->get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] ) + break; // Source no more under Magnetic Field + } + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + } + break; - if( !status->charge(bl,hp,sp) ) break; + case SC_INSPIRATION: + if(--(sce->val4) > 0) { + int hp = st->max_hp * (7-sce->val1) / 100; + int sp = st->max_sp * (9-sce->val1) / 100; - sc_timer_next(1000+tick,status->change_timer,bl->id, data); - return 0; - } - break; + if( !status->charge(bl,hp,sp) ) break; - case SC_RAISINGDRAGON: - // 1% every 5 seconds [Jobbie] - if( --(sce->val3)>0 && status->charge(bl, sce->val2, 0) ) { - if( !sc->data[type] ) return 0; - sc_timer_next(5000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; + sc_timer_next(1000+tick,status->change_timer,bl->id, data); + return 0; + } + break; - case SC_CIRCLE_OF_FIRE: - case SC_FIRE_CLOAK: - case SC_WATER_DROP: - case SC_WATER_SCREEN: - case SC_WIND_CURTAIN: - case SC_WIND_STEP: - case SC_STONE_SHIELD: - case SC_SOLID_SKIN: - if( !status->charge(bl,0,sce->val2) ){ - struct block_list *s_bl = battle->get_master(bl); - if( s_bl ) - status_change_end(s_bl,type+1,INVALID_TIMER); - status_change_end(bl,type,INVALID_TIMER); + case SC_RAISINGDRAGON: + // 1% every 5 seconds [Jobbie] + if( --(sce->val3)>0 && status->charge(bl, sce->val2, 0) ) { + if( !sc->data[type] ) return 0; + sc_timer_next(5000 + tick, status->change_timer, bl->id, data); + return 0; + } break; - } - sc_timer_next(2000 + tick, status->change_timer, bl->id, data); - return 0; - case SC_STOMACHACHE: - if( --(sce->val4) > 0 ) { - status->charge(bl,0,sce->val2); // Reduce 8 every 10 seconds. - if( sd && !pc_issit(sd) ) { // Force to sit every 10 seconds. - pc_stop_walking(sd,1|4); - pc_stop_attack(sd); - pc_setsit(sd); - clif->sitting(bl); + case SC_CIRCLE_OF_FIRE: + case SC_FIRE_CLOAK: + case SC_WATER_DROP: + case SC_WATER_SCREEN: + case SC_WIND_CURTAIN: + case SC_WIND_STEP: + case SC_STONE_SHIELD: + case SC_SOLID_SKIN: + if( !status->charge(bl,0,sce->val2) ){ + struct block_list *s_bl = battle->get_master(bl); + if( s_bl ) + status_change_end(s_bl,type+1,INVALID_TIMER); + status_change_end(bl,type,INVALID_TIMER); + break; } - sc_timer_next(10000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_LEADERSHIP: - case SC_GLORYWOUNDS: - case SC_SOULCOLD: - case SC_HAWKEYES: - /* they only end by status_change_end */ - sc_timer_next(600000 + tick, status->change_timer, bl->id, data); - return 0; - case SC_MEIKYOUSISUI: - if( --(sce->val4) > 0 ) { - status->heal(bl, st->max_hp * (sce->val1+1) / 100, st->max_sp * sce->val1 / 100, 0); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_IZAYOI: - case SC_KAGEMUSYA: - if( --(sce->val2) > 0 ) { - if(!status->charge(bl, 0, 1)) break; - sc_timer_next(1000+tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_ANGRIFFS_MODUS: - if(--(sce->val4) > 0) { //drain hp/sp - if( !status->charge(bl,100,20) ) break; - sc_timer_next(1000+tick,status->change_timer,bl->id, data); - return 0; - } - break; - case SC_FULL_THROTTLE: - if( --(sce->val4) > 0 ) { - status_percent_damage(bl, bl, sce->val2, 0, false); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); - return 0; - } - break; - case SC_KINGS_GRACE: - if( --(sce->val4) > 0 ) { - status_percent_heal(bl, sce->val2, 0); - sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + sc_timer_next(2000 + tick, status->change_timer, bl->id, data); return 0; - } - break; - case SC_FRIGG_SONG: - if( --(sce->val4) > 0 ) { - status->heal(bl, sce->val3, 0, 0); - sc_timer_next(10000 + tick, status->change_timer, bl->id, data); + + case SC_STOMACHACHE: + if( --(sce->val4) > 0 ) { + status->charge(bl,0,sce->val2); // Reduce 8 every 10 seconds. + if( sd && !pc_issit(sd) ) { // Force to sit every 10 seconds. + pc_stop_walking(sd,1|4); + pc_stop_attack(sd); + pc_setsit(sd); + clif->sitting(bl); + } + sc_timer_next(10000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_LEADERSHIP: + case SC_GLORYWOUNDS: + case SC_SOULCOLD: + case SC_HAWKEYES: + /* they only end by status_change_end */ + sc_timer_next(600000 + tick, status->change_timer, bl->id, data); return 0; - } - break; + case SC_MEIKYOUSISUI: + if( --(sce->val4) > 0 ) { + status->heal(bl, st->max_hp * (sce->val1+1) / 100, st->max_sp * sce->val1 / 100, 0); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_IZAYOI: + case SC_KAGEMUSYA: + if( --(sce->val2) > 0 ) { + if(!status->charge(bl, 0, 1)) break; + sc_timer_next(1000+tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_ANGRIFFS_MODUS: + if(--(sce->val4) > 0) { //drain hp/sp + if( !status->charge(bl,100,20) ) break; + sc_timer_next(1000+tick,status->change_timer,bl->id, data); + return 0; + } + break; + case SC_FULL_THROTTLE: + if( --(sce->val4) > 0 ) { + status_percent_damage(bl, bl, sce->val2, 0, false); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_KINGS_GRACE: + if( --(sce->val4) > 0 ) { + status_percent_heal(bl, sce->val2, 0); + sc_timer_next(1000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; + case SC_FRIGG_SONG: + if( --(sce->val4) > 0 ) { + status->heal(bl, sce->val3, 0, 0); + sc_timer_next(10000 + tick, status->change_timer, bl->id, data); + return 0; + } + break; } // default for all non-handled control paths is to end the status @@ -10844,57 +10857,57 @@ int status_change_timer_sub(struct block_list* bl, va_list ap) { tsc = status->get_sc(bl); switch( type ) { - case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */ - if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking - rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % - status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); - case SC_CONCENTRATION: - status_change_end(bl, SC_HIDING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); - status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); - break; - case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */ - if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || - tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] || - tsc->data[SC__INVISIBILITY])) { - status_change_end(bl, SC_HIDING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKING, INVALID_TIMER); - status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); - if(battle->check_target( src, bl, BCT_ENEMY ) > 0) - skill->attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); - } - if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking - rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % - status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); - break; - case SC_WZ_SIGHTBLASTER: - if (battle->check_target( src, bl, BCT_ENEMY ) > 0 - && status->check_skilluse(src, bl, WZ_SIGHTBLASTER, 2) - ) { - if (sce && !(bl->type&BL_SKILL) //The hit is not counted if it's against a trap - && skill->attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0) - ){ - sce->val2 = 0; //This signals it to end. + case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */ + if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking + rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % + status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); + case SC_CONCENTRATION: + status_change_end(bl, SC_HIDING, INVALID_TIMER); + status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); + status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); + break; + case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */ + if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || + tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] || + tsc->data[SC__INVISIBILITY])) { + status_change_end(bl, SC_HIDING, INVALID_TIMER); + status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); + status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); + if(battle->check_target( src, bl, BCT_ENEMY ) > 0) + skill->attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); } - } - break; - case SC_RG_CCONFINE_M: - //Lock char has released the hold on everyone... - if (tsc && tsc->data[SC_RG_CCONFINE_S] && tsc->data[SC_RG_CCONFINE_S]->val2 == src->id) { - tsc->data[SC_RG_CCONFINE_S]->val2 = 0; - status_change_end(bl, SC_RG_CCONFINE_S, INVALID_TIMER); - } - break; - case SC_CURSEDCIRCLE_TARGET: - if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) { - clif->bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0); - status_change_end(bl, type, INVALID_TIMER); - } - break; + if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking + rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % + status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); + break; + case SC_WZ_SIGHTBLASTER: + if (battle->check_target( src, bl, BCT_ENEMY ) > 0 + && status->check_skilluse(src, bl, WZ_SIGHTBLASTER, 2) + ) { + if (sce && !(bl->type&BL_SKILL) //The hit is not counted if it's against a trap + && skill->attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0) + ){ + sce->val2 = 0; //This signals it to end. + } + } + break; + case SC_RG_CCONFINE_M: + //Lock char has released the hold on everyone... + if (tsc && tsc->data[SC_RG_CCONFINE_S] && tsc->data[SC_RG_CCONFINE_S]->val2 == src->id) { + tsc->data[SC_RG_CCONFINE_S]->val2 = 0; + status_change_end(bl, SC_RG_CCONFINE_S, INVALID_TIMER); + } + break; + case SC_CURSEDCIRCLE_TARGET: + if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) { + clif->bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0); + status_change_end(bl, type, INVALID_TIMER); + } + break; } return 0; } diff --git a/src/map/unit.c b/src/map/unit.c index af52e6dfb..7b37be266 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1345,6 +1345,19 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui casttime = 0; } + if( sc ) { + /** + * why the if else chain: these 3 status do not stack, so its efficient that way. + **/ + if( sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_id != AS_CLOAKING ) { + status_change_end(src, SC_CLOAKING, INVALID_TIMER); + if (!src->prev) return 0; //Warped away! + } else if( sc->data[SC_CLOAKINGEXCEED] && !(sc->data[SC_CLOAKINGEXCEED]->val4&4) && skill_id != GC_CLOAKINGEXCEED ) { + status_change_end(src,SC_CLOAKINGEXCEED, INVALID_TIMER); + if (!src->prev) return 0; + } + } + if(!ud->state.running) //need TK_RUN or WUGDASH handler to be done before that, see bugreport:6026 unit->stop_walking(src,1);// eventhough this is not how official works but this will do the trick. bugreport:6829 @@ -1401,20 +1414,6 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui ud->skill_id = skill_id; ud->skill_lv = skill_lv; - if( sc ) { - /** - * why the if else chain: these 3 status do not stack, so its efficient that way. - **/ - if( sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_id != AS_CLOAKING ) { - status_change_end(src, SC_CLOAKING, INVALID_TIMER); - if (!src->prev) return 0; //Warped away! - } else if( sc->data[SC_CLOAKINGEXCEED] && !(sc->data[SC_CLOAKINGEXCEED]->val4&4) && skill_id != GC_CLOAKINGEXCEED ) { - status_change_end(src,SC_CLOAKINGEXCEED, INVALID_TIMER); - if (!src->prev) return 0; - } - } - - if( casttime > 0 ) { ud->skilltimer = timer->add( tick+casttime, skill->castend_id, src->id, 0 ); if( sd && (pc->checkskill(sd,SA_FREECAST) > 0 || skill_id == LG_EXEEDBREAK) ) |