diff options
-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) ) |