diff --git a/db/pre-re/skill_cast_db.txt b/db/pre-re/skill_cast_db.txt index d3b58f3fd1..253cc58cc8 100644 --- a/db/pre-re/skill_cast_db.txt +++ b/db/pre-re/skill_cast_db.txt @@ -859,7 +859,7 @@ //-- GS_FULLBUSTER 519,0,1200:1400:1600:1800:2000:2200:2400:2600:2800:3000,0,0,10000,0 //-- GS_GROUNDDRIFT (Upkeep2 times are duration of: Stun(lv1), Blind(lv2), Poison(lv3) and Freeze(lv4)) -521,2000,0,0,3000:6000:9000:12000:15000:18000:21000:24000:27000:30000,5000:30000:30000:12000,0 +521,2000,0,0,3000:6000:9000:12000:15000:18000:21000:24000:27000:30000,5000:30000:60000:12000,0 //========================================== diff --git a/doc/status_change.txt b/doc/status_change.txt index 5867c915f9..01fe1f873f 100644 --- a/doc/status_change.txt +++ b/doc/status_change.txt @@ -25,9 +25,9 @@ SC_STONE () desc: DEF -50%; if HP>25% lose 1% HP/5 sec; MDEF +25%; change element to Earth Lv 1; ignore Steal & Lex Aeterna; can't move/attack/pick item/use item/use skill/sit/logout val1: - val2: Caster's object ID (for mob_log_damage) - val3: Tick - val4: Petrifying tick + val2: Caster's object ID + val3: Incubation time + val4: Remaining tick SC_FREEZE () desc: DEF -50%; FLEE = 0; MDEF +25%; ignore Steal, Lex Aeterna, Storm Gust, Falling Ice Pillar; change element to Water Lv 1; can't move/attack/pick item/use item/sit/logout @@ -44,9 +44,9 @@ SC_SLEEP () SC_POISON () desc: DEF -25%; if HP>25% lose 1.5% + 2 HP/sec; SP Regeneration is disabled val1: Skill Level - val2: Caster's object ID (for mob_log_damage) - val3: Tick - val4: HP Damage + val2: Caster's object ID + val3: + val4: Remaining tick SC_CURSE () desc: ATK-25%; LUK = 0; Movement speed -300 @@ -69,14 +69,14 @@ SC_BLEEDING (SI_BLEEDING) val1: Skill Level val2: Caster's object ID (for mob_log_damage) val3: - val4: Tick + val4: Remaining tick SC_DPOISON () desc: DEF -25%; if HP>25% lose 10/15% HP/sec val1: Skill Level val2: Caster's object ID (for mob_log_damage) - val3: Tick - val4: HP Damage + val3: + val4: Remaining tick SC_PROVOKE (SI_PROVOKE) desc: Decrease DEF by (5+(5*Skill Lv))%; Increase ATK by (2+(3*Skill lv))% @@ -1283,7 +1283,7 @@ SC_BURNING (SI_BURNT) val1: Skill Level val2: 1000 val3: Caster's object ID (for mob_log_damage) - val4: Tick + val4: Remaining tick SC_FREEZING () desc: @@ -1519,9 +1519,9 @@ SC_ROLLINGCUTTER () SC_TOXIN (SI_TOXIN) desc: Inflict damage, which causes the affected entity to flinch every 10 seconds; This will interrupt the skill casting, even if protected against it val1: GC_WEAPONRESEARCH Skill Level - val2: Caster's object ID (for mob_log_damage) + val2: Caster's object ID val3: - val4: Tick + val4: Remaining tick SC_PARALYSE (SI_PARALYSE) desc: Decrease both ASPD and Flee Rate by 10% and halve Movement Speed, which does not stack with Decrease AGI, Quagmire, Marsh Of Abyss or Freezing status @@ -1540,9 +1540,9 @@ SC_VENOMBLEED (SI_VENOMBLEED) SC_MAGICMUSHROOM (SI_MAGICMUSHROOM) desc: Force the affected entity to use /heh emote, to randomly use skills and drain 3% of Max HP every 4 seconds val1: GC_WEAPONRESEARCH Skill Level - val2: Caster's object ID (for mob_log_damage) + val2: Caster's object ID val3: - val4: Tick + val4: Remaining tick SC_DEATHHURT (SI_DEATHHURT) desc: Drop the healing effectiveness by 20%; This effect stacks with Critical Wounds @@ -1556,7 +1556,7 @@ SC_PYREXIA (SI_PYREXIA) val1: GC_WEAPONRESEARCH Skill Level val2: val3: - val4: Tick + val4: Remaining tick SC_OBLIVIONCURSE (SI_OBLIVIONCURSE) desc: Force the affected entity to use /? emote, block SP Recovery and cause Oblivion status; There is a chance (100% - (Target INT * 0.8)%) of being inflicted, with a minimum of 5% @@ -1568,9 +1568,9 @@ SC_OBLIVIONCURSE (SI_OBLIVIONCURSE) SC_LEECHESEND (SI_LEECHESEND) desc: Drain (Target VIT * (SkillLv - 3)) + (Target HP / 100) HP each second val1: GC_WEAPONRESEARCH Skill Level - val2: Caster's object ID (for mob_log_damage) + val2: Caster's object ID val3: - val4: Tick + val4: Remaining tick SC_REFLECTDAMAGE () desc: diff --git a/src/map/map.c b/src/map/map.c index 986b22469d..fecf13480a 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -795,7 +795,7 @@ int map_foreachinshootarea(int(*func)(struct block_list*, va_list), int16 m, int bl_list[bl_list_count++] = bl; if (bl_list_count >= BL_LIST_MAX) - ShowWarning("map_foreachinarea: block count too many!\n"); + ShowWarning("map_foreachinshootarea: block count too many!\n"); map_freeblock_lock(); @@ -1423,7 +1423,7 @@ int map_foreachindir(int(*func)(struct block_list*, va_list), int16 m, int16 x0, } if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinpath: block count too many!\n"); + ShowWarning("map_foreachindir: block count too many!\n"); map_freeblock_lock(); diff --git a/src/map/mob.c b/src/map/mob.c index c8b42d14c2..a7984ccbda 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -40,7 +40,7 @@ #define MOB_LAZYMOVEPERC(md) (md->state.spotted?1000:0) #define MOB_MAX_DELAY (24*3600*1000) #define MAX_MINCHASE 30 //Max minimum chase value to use for mobs. -#define RUDE_ATTACKED_COUNT 2 //After how many rude-attacks should the skill be used? +#define RUDE_ATTACKED_COUNT 1 //After how many rude-attacks should the skill be used? #define MAX_MOB_CHAT 50 //Max Skill's messages // On official servers, monsters will only seek targets that are closer to walk to than their @@ -1565,7 +1565,8 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) ) ) ) { // Rude attacked - if (md->state.attacked_count++ >= RUDE_ATTACKED_COUNT + if (abl->id != md->bl.id //Self damage does not cause rude attack + && md->state.attacked_count++ >= RUDE_ATTACKED_COUNT && !mobskill_use(md, tick, MSC_RUDEATTACKED) && can_move && !tbl && unit_escape(&md->bl, abl, rnd()%10 +1)) { //Escaped. @@ -2041,8 +2042,6 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) return; //Do nothing for absorbed damage. if( !damage && !(src->type&DEFAULT_ENEMY_TYPE(md)) ) return; //Do not log non-damaging effects from non-enemies. - if( src->id == md->bl.id ) - return; //Do not log self-damage. switch( src->type ) { @@ -2116,6 +2115,12 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) md->attacked_id = src->id; } + //Self damage increases tap bonus + if (!char_id && src->id == md->bl.id) { + char_id = src->id; + flag = MDLF_SELF; + } + if( char_id ) { //Log damage... int i,minpos; @@ -2154,28 +2159,24 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) //Call when a mob has received damage. void mob_damage(struct mob_data *md, struct block_list *src, int damage) { - if (damage > 0) { //Store total damage... + if (src && damage > 0) { //Store total damage... if (UINT_MAX - (unsigned int)damage > md->tdmg) - md->tdmg+=damage; + md->tdmg += damage; else if (md->tdmg == UINT_MAX) damage = 0; //Stop recording damage once the cap has been reached. else { //Cap damage log... damage = (int)(UINT_MAX - md->tdmg); md->tdmg = UINT_MAX; } - if (md->state.aggressive) //No longer aggressive, change to retaliate AI. + if ((src != &md->bl) && md->state.aggressive) //No longer aggressive, change to retaliate AI. md->state.aggressive = 0; //Log damage - if (src) - mob_log_damage(md, src, damage); + mob_log_damage(md, src, damage); md->dmgtick = gettick(); } if (battle_config.show_mob_info&3) - clif_charnameack (0, &md->bl); - - if (!src) - return; + clif_charnameack(0, &md->bl); #if PACKETVER >= 20120404 if( battle_config.monster_hp_bars_info){ @@ -2188,6 +2189,9 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) } #endif + if (!src) + return; + if( md->special_state.ai == AI_SPHERE ) {//LOne WOlf explained that ANYONE can trigger the marine countdown skill. [Skotlex] md->state.alchemist = 1; mobskill_use(md, gettick(), MSC_ALCHEMIST); @@ -2241,18 +2245,23 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // filter out entries not eligible for exp distribution memset(tmpsd,0,sizeof(tmpsd)); for(i = 0, count = 0, mvp_damage = 0; i < DAMAGELOG_SIZE && md->dmglog[i].id; i++) { - struct map_session_data* tsd = map_charid2sd(md->dmglog[i].id); - - if(tsd == NULL) + struct map_session_data* tsd = NULL; + if (md->dmglog[i].flag == MDLF_SELF) { + //Self damage counts as exp tap + count++; + continue; + } + tsd = map_charid2sd(md->dmglog[i].id); + if (tsd == NULL) continue; // skip empty entries - if(tsd->bl.m != m) + if (tsd->bl.m != m) continue; // skip players not on this map count++; //Only logged into same map chars are counted for the total. if (pc_isdead(tsd)) continue; // skip dead players - if(md->dmglog[i].flag == MDLF_HOMUN && !hom_is_active(tsd->hd)) + if (md->dmglog[i].flag == MDLF_HOMUN && !hom_is_active(tsd->hd)) continue; // skip homunc's share if inactive - if( md->dmglog[i].flag == MDLF_PET && (!tsd->status.pet_id || !tsd->pd) ) + if (md->dmglog[i].flag == MDLF_PET && (!tsd->status.pet_id || !tsd->pd)) continue; // skip pet's share if inactive if(md->dmglog[i].dmg > mvp_damage) { diff --git a/src/map/mob.h b/src/map/mob.h index 9d91678fda..05481e7e80 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -61,6 +61,7 @@ enum MobDamageLogFlag MDLF_NORMAL = 0, MDLF_HOMUN, MDLF_PET, + MDLF_SELF }; enum size { diff --git a/src/map/pc.c b/src/map/pc.c index 1b028d7f9a..eb6fb56b25 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -7312,7 +7312,7 @@ void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int h if (hp) clif_updatestatus(sd,SP_HP); else return; - if( !src || src == &sd->bl ) + if (!src) return; if( pc_issit(sd) ) { diff --git a/src/map/skill.c b/src/map/skill.c index 878caecf54..8c6e9b16c9 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -7053,7 +7053,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; } if (sc_start4(src,bl,type,(skill_lv*4+20)+brate, - skill_lv, src->id, 0, skill_get_time(skill_id, skill_lv), + skill_lv, src->id, skill_get_time(skill_id, skill_lv), 0, skill_get_time2(skill_id,skill_lv))) clif_skill_nodamage(src,bl,skill_id,skill_lv,1); else if(sd) { @@ -13412,7 +13412,7 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, uns case UNT_VENOMDUST: if(tsc && !tsc->data[type]) - status_change_start(ss, bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),SCSTART_NONE); + status_change_start(ss,bl,type,10000,sg->skill_lv,sg->src_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),SCSTART_NONE); break; case UNT_LANDMINE: diff --git a/src/map/status.c b/src/map/status.c index 647ef27aa9..48e0809577 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -7304,6 +7304,34 @@ void status_change_init(struct block_list *bl) memset(sc, 0, sizeof (struct status_change)); } +/*========================================== [Playtester] +* Returns the interval for status changes that iterate multiple times +* through the timer (e.g. those that deal damage in regular intervals) +* @param type: Status change (SC_*) +*------------------------------------------*/ +int status_get_sc_interval(enum sc_type type) +{ + switch (type) { + case SC_POISON: + case SC_DPOISON: + case SC_LEECHESEND: + return 1000; + case SC_BURNING: + case SC_PYREXIA: + return 3000; + case SC_MAGICMUSHROOM: + return 4000; + case SC_STONE: + return 5000; + case SC_BLEEDING: + case SC_TOXIN: + return 10000; + default: + break; + } + return 0; +} + /** * Applies SC defense to a given status change * This function also determines whether or not the status change will be applied @@ -7606,13 +7634,9 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ if (!(rnd()%10000 < rate)) return 0; - // Even if a status change doesn't have a duration, it should still trigger - if (tick < 1) - return 1; - // Duration cannot be reduced if (flag&SCSTART_NOTICKDEF) - return tick; + return max(tick, 1); tick -= tick*tick_def/10000; tick -= tick_def2; @@ -7620,7 +7644,6 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ // Minimum durations switch (type) { case SC_ANKLE: - case SC_BURNING: case SC_MARSHOFABYSS: case SC_DEEPSLEEP: tick = max(tick, 5000); // Minimum duration 5s @@ -7628,6 +7651,7 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ case SC_FREEZING: tick = max(tick, 6000); // Minimum duration 6s break; + case SC_BURNING: case SC_STASIS: tick = max(tick, 10000); // Minimum duration 10s break; @@ -8935,10 +8959,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty break; case SC_STONE: - val4 = max(val4, 100); // Incubation time - val3 = (tick-val4)/100; // Petrified timer iterations - if(val3 < 1) val3 = 1; - tick = val4; + val3 = max(val3, 100); // Incubation time + val4 = max(tick-val3, 100); // Petrify time + tick = val3; calc_flag = 0; // Actual status changes take effect on petrified state. break; @@ -8948,34 +8971,30 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty int diff = status->max_hp*(bl->type==BL_PC?10:15)/100; if (status->hp - diff < status->max_hp>>2) diff = status->hp - (status->max_hp>>2); - if( val2 && bl->type == BL_MOB ) { - struct block_list* src2 = map_id2bl(val2); - if( src2 ) - mob_log_damage((TBL_MOB*)bl,src2,diff); - } status_zap(bl, diff, 0); } - case SC_POISON: // Fall through - val3 = tick/1000; // Damage iterations - if(val3 < 1) val3 = 1; - tick_time = 1000; // [GodLesZ] tick time - // val4: HP damage - if (bl->type == BL_PC) - val4 = (type == SC_DPOISON) ? 2 + status->max_hp/50 : 2 + status->max_hp*3/200; - else - val4 = (type == SC_DPOISON) ? 2 + status->max_hp/100 : 2 + status->max_hp/200; + case SC_POISON: + case SC_BLEEDING: + case SC_BURNING: + case SC_TOXIN: + case SC_MAGICMUSHROOM: + case SC_LEECHESEND: + tick_time = status_get_sc_interval(type); + val4 = tick-tick_time; // Remaining time + break; + + case SC_PYREXIA: + //Causes blind for duration of pyrexia, unreducable and unavoidable, but can be healed with e.g. green potion + status_change_start(src,bl,SC_BLIND,10000,val1,0,0,0,tick,SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_NORATEDEF); + tick_time = status_get_sc_interval(type); + val4 = tick-tick_time; // Remaining time break; case SC_CONFUSION: if (!val4) clif_emotion(bl,E_WHAT); break; - case SC_BLEEDING: - val4 = tick/10000; - if (!val4) val4 = 1; - tick_time = 10000; // [GodLesZ] tick time - break; case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: if( val1 == 0 ) return 0; @@ -9219,11 +9238,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty } case SC_COMA: // Coma. Sends a char to 1HP. If val2, do not zap sp - if( val3 && bl->type == BL_MOB ) { - struct block_list* src2 = map_id2bl(val3); - if( src2 ) - mob_log_damage((TBL_MOB*)bl,src2,status->hp - 1); - } status_zap(bl, status->hp-1, val2?0:status->sp); return 1; break; @@ -9561,10 +9575,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty val4 = tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; - case SC_BURNING: - val4 = tick / 2000; // Total Ticks to Burn!! - tick_time = 2000; // [GodLesZ] tick time - break; /* Rune Knight */ case SC_DEATHBOUND: @@ -9598,23 +9608,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty val4 = tick / 5000; tick_time = 5000; // [GodLesZ] tick time break; - case SC_TOXIN: - val4 = tick / 10000; - tick_time = 10000; // [GodLesZ] tick time - break; - case SC_MAGICMUSHROOM: - val4 = tick / 4000; - tick_time = 4000; // [GodLesZ] tick time - break; - case SC_PYREXIA: - status_change_start(src,bl,SC_BLIND,10000,val1,0,0,0,30000,SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_NORATEDEF); // Blind status that last for 30 seconds - val4 = tick / 3000; - tick_time = 3000; // [GodLesZ] tick time - break; - case SC_LEECHESEND: - val4 = tick / 1000; - tick_time = 1000; // [GodLesZ] tick time - break; case SC_OBLIVIONCURSE: val4 = tick / 3000; tick_time = 3000; // [GodLesZ] tick time @@ -10331,6 +10324,21 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); clif_changelook(bl,LOOK_BODY2,0); break; + case SC_STONE: + if (val3 > 0) + break; //Incubation time still active + //Fall through + case SC_POISON: + case SC_DPOISON: + case SC_BLEEDING: + case SC_BURNING: + case SC_TOXIN: + case SC_MAGICMUSHROOM: + case SC_PYREXIA: + case SC_LEECHESEND: + tick_time = tick; + tick = tick_time + max(val4,0); + break; } // Values that must be set regardless of flag&4 e.g. val_flag [Ind] @@ -10500,7 +10508,12 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty opt_flag = 1; switch(type) { // OPT1 - case SC_STONE: sc->opt1 = OPT1_STONEWAIT; break; + case SC_STONE: + if (val3 > 0) + sc->opt1 = OPT1_STONEWAIT; + else + sc->opt1 = OPT1_STONE; + break; case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break; case SC_STUN: sc->opt1 = OPT1_STUN; break; case SC_DEEPSLEEP: opt_flag = 0; @@ -11018,7 +11031,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const // 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. + sce->val4 = -1; // Petrify time case SC_FREEZE: case SC_STUN: case SC_SLEEP: @@ -11743,6 +11756,8 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) struct status_data *status; struct status_change *sc; struct status_change_entry *sce; + int interval = status_get_sc_interval(type); + bool dounlock = false; bl = map_id2bl(id); if(!bl) { @@ -11837,8 +11852,8 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) break; case SC_STONE: - if(sc->opt1 == OPT1_STONEWAIT && sce->val3) { - sce->val4 = 0; + if (sc->opt1 == OPT1_STONEWAIT && sce->val4) { + sce->val3 = 0; //Incubation time used up unit_stop_attack(bl); if (sc->data[SC_DANCING]) { unit_stop_walking(bl, 1); @@ -11847,45 +11862,117 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) status_change_end(bl, SC_AETERNA, INVALID_TIMER); sc->opt1 = OPT1_STONE; clif_changeoption(bl); - sc_timer_next(100+tick,status_change_timer, bl->id, data ); + sc_timer_next(min(sce->val4, interval) + tick, status_change_timer, bl->id, data); + sce->val4 -= interval; //Remaining time status_calc_bl(bl, StatusChangeFlagTable[type]); return 0; } - if (++(sce->val4)%50 == 0 && status->hp > status->max_hp/4) { - if (sce->val2 && bl->type == BL_MOB) { - struct block_list *src = map_id2bl(sce->val2); - - if (src) - mob_log_damage((TBL_MOB*)bl, src, apply_rate(status->hp, 1)); - } + if (sce->val4 >= 0 && !(sce->val3) && status->hp > status->max_hp / 4) { status_percent_damage(NULL, bl, 1, 0, false); } - if(--(sce->val3) > 0) { - sc_timer_next(100+tick,status_change_timer, bl->id, data ); - return 0; - } break; case SC_POISON: 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); - } + if (sce->val4 >= 0 && !sc->data[SC_SLOWPOISON]) { + int64 damage = 0; + if (sd) + damage = (type == SC_DPOISON) ? 2 + status->max_hp / 50 : 2 + status->max_hp * 3 / 200; + else + damage = (type == SC_DPOISON) ? 2 + status->max_hp / 100 : 2 + status->max_hp / 200; + if (status->hp > max(status->max_hp / 4, damage)) // Stop damaging after 25% HP left. + status_zap(bl, damage, 0); + } + break; + + case SC_BLEEDING: + if (sce->val4 >= 0) { + int64 damage = rnd() % 600 + 200; + if (!sd && damage >= status->hp) + damage = status->hp - 1; // No deadly damage for monsters + map_freeblock_lock(); + dounlock = true; + status_zap(bl, damage, 0); + } + break; + + case SC_BURNING: + if (sce->val4 >= 0) { + int64 damage = 1000 + (3 * status->max_hp) / 100; // Deals fixed (1000 + 3%*MaxHP) + map_freeblock_lock(); + dounlock = true; + status_fix_damage(bl, bl, damage, clif_damage(bl, bl, tick, 0, 1, damage, 1, DMG_NORMAL, 0)); + } + break; + + case SC_TOXIN: + if (sce->val4 >= 0) { // Damage is every 10 seconds including 3%sp drain. + map_freeblock_lock(); + dounlock = true; + status_damage(bl, bl, 1, status->max_sp * 3 / 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 1, 1, DMG_NORMAL, 0), 0); + } + break; + + case SC_MAGICMUSHROOM: + if (sce->val4 >= 0) { + bool flag = 0; + int64 damage = status->max_hp * 3 / 100; + if (status->hp <= damage) + damage = status->hp - 1; // Cannot Kill + + if (damage > 0) { // 3% Damage each 4 seconds map_freeblock_lock(); - if(status->hp >= max(status->max_hp>>2, sce->val4)) // Stop damaging after 25% HP left. - 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_zap(bl, damage, 0); + flag = !sc->data[type]; // Killed? Should not map_freeblock_unlock(); } - return 0; - } + if (!flag) { // Random Skill Cast + if (skill_magicmushroom_count && sd && !pc_issit(sd)) { // Can't cast if sit + int mushroom_skill_id = 0, checked = 0, checked_max = MAX_SKILL_MAGICMUSHROOM_DB * 3; + unit_stop_attack(bl); + unit_skillcastcancel(bl, 1); + do { + int i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB; + mushroom_skill_id = skill_magicmushroom_db[i].skill_id; + } while (checked++ < checked_max && mushroom_skill_id == 0); + + if (!skill_get_index(mushroom_skill_id)) + break; + + 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); + } + } + break; + + case SC_PYREXIA: + if (sce->val4 >= 0) { + map_freeblock_lock(); + dounlock = true; + status_fix_damage(bl, bl, 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 100, 1, DMG_NORMAL, 0)); + } + break; + + case SC_LEECHESEND: + if (sce->val4 >= 0) { + int64 damage = status->vit * (sce->val1 - 3) + status->max_hp / 100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) + map_freeblock_lock(); + dounlock = true; + status_fix_damage(bl, bl, damage, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, damage, 1, DMG_NORMAL, 0)); + unit_skillcastcancel(bl, 2); + } break; case SC_TENSIONRELAX: @@ -11906,26 +11993,6 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) } break; - case SC_BLEEDING: - 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 < status->hp ? hp : status->hp - 1 ); - map_freeblock_lock(); - status_fix_damage(src, bl, sd||hphp?hp:status->hp-1, 1); - if( sc->data[type] ) { - if( status->hp == 1 ) { - map_freeblock_unlock(); - break; - } - sc_timer_next(10000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - return 0; - } - break; - case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: if( sd && --(sce->val4) >= 0 ) { @@ -12083,114 +12150,6 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) } break; - case SC_PYREXIA: - if( --(sce->val4) >= 0 ) { - map_freeblock_lock(); - clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,DMG_NORMAL,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; - - case SC_LEECHESEND: - if( --(sce->val4) >= 0 ) { - int damage = status->vit * (sce->val1 - 3) + status->max_hp / 100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) - - if (sce->val2 && bl->type == BL_MOB) { - struct block_list *src2 = map_id2bl(sce->val2); - - if (src2) - mob_log_damage((TBL_MOB*)bl, src2, damage); - } - map_freeblock_lock(); - status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,DMG_NORMAL,0), 0); - unit_skillcastcancel(bl, 2); - if (sc->data[type]) { - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - return 0; - } - break; - - case SC_MAGICMUSHROOM: - if( --(sce->val4) >= 0 ) { - bool flag = 0; - int damage = status->max_hp * 3 / 100; - if( status->hp <= damage ) - damage = status->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(); - } - - if (sce->val2 && bl->type == BL_MOB) { - struct block_list *src2 = map_id2bl(sce->val2); - - if (src2) - mob_log_damage((TBL_MOB*)bl, src2, damage); - } - - if( !flag ) { // Random Skill Cast - if (skill_magicmushroom_count && sd && !pc_issit(sd)) { // Can't cast if sit - int mushroom_skill_id = 0, checked = 0, checked_max = MAX_SKILL_MAGICMUSHROOM_DB * 3; - unit_stop_attack(bl); - unit_skillcastcancel(bl,1); - do { - int i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB; - mushroom_skill_id = skill_magicmushroom_db[i].skill_id; - } - while( checked++ < checked_max && mushroom_skill_id == 0 ); - - if (!skill_get_index(mushroom_skill_id)) - break; - - 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; - - case SC_TOXIN: - if( --(sce->val4) >= 0 ) { // Damage is every 10 seconds including 3%sp drain. - if (sce->val2 && bl->type == BL_MOB) { - struct block_list *src2 = map_id2bl(sce->val2); - - if (src2) - mob_log_damage((TBL_MOB*)bl, src2, 1); - } - map_freeblock_lock(); - clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,DMG_NORMAL,0); - status_damage(NULL, bl, 1, status->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; - } - break; - case SC_OBLIVIONCURSE: if( --(sce->val4) >= 0 ) { clif_emotion(bl,E_WHAT); @@ -12225,24 +12184,6 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) } 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) - - if (src && bl->type == BL_MOB) - mob_log_damage((TBL_MOB*)bl, src, damage); - map_freeblock_lock(); - clif_damage(bl,bl,tick,0,0,damage,1,DMG_MULTI_HIT_ENDURE,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(2000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - return 0; - } - break; - case SC_FEAR: if( --(sce->val4) >= 0 ) { if( sce->val2 > 0 ) @@ -12657,8 +12598,19 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) break; } - // Default for all non-handled control paths is to end the status + // If status has an interval and there is at least 100ms remaining time, wait for next interval + if(interval > 0 && sc->data[type] && sce->val4 >= 100) { + sc_timer_next(min(sce->val4,interval)+tick, status_change_timer, bl->id, data); + sce->val4 -= interval; + if (dounlock) + map_freeblock_unlock(); + return 0; + } + if (dounlock) + map_freeblock_unlock(); + + // Default for all non-handled control paths is to end the status return status_change_end( bl,type,tid ); #undef sc_timer_next } @@ -12965,7 +12917,7 @@ int status_change_spread(struct block_list *src, struct block_list *bl, bool typ { int i, flag = 0; struct status_change *sc = status_get_sc(src); - const struct TimerData *timer; + const struct TimerData *timer = NULL; unsigned int tick; struct status_change_data data; @@ -12981,6 +12933,11 @@ int status_change_spread(struct block_list *src, struct block_list *bl, bool typ for( i = SC_COMMON_MIN; i < SC_MAX; i++ ) { if( !sc->data[i] || i == SC_COMMON_MAX ) continue; + if (sc->data[i]->timer != INVALID_TIMER) { + timer = get_timer(sc->data[i]->timer); + if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick, tick) < 0) + continue; + } switch( i ) { // Debuffs that can be spread. @@ -13009,45 +12966,30 @@ int status_change_spread(struct block_list *src, struct block_list *bl, bool typ //case SC_BITE: case SC_FREEZING: case SC_VENOMBLEED: - if( sc->data[i]->timer != INVALID_TIMER ) { - timer = get_timer(sc->data[i]->timer); - if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) - continue; - data.tick = DIFF_TICK(timer->tick,tick); - } else + if (sc->data[i]->timer != INVALID_TIMER) + data.tick = DIFF_TICK(timer->tick, tick); + else data.tick = INVALID_TIMER; break; // Special cases - case SC_POISON: - case SC_DPOISON: - data.tick = sc->data[i]->val3 * 1000; - break; + case SC_TOXIN: + case SC_MAGICMUSHROOM: + case SC_PYREXIA: case SC_LEECHESEND: if (type) continue; + case SC_POISON: + case SC_DPOISON: + case SC_BLEEDING: + case SC_BURNING: + if (sc->data[i]->timer != INVALID_TIMER) + data.tick = DIFF_TICK(timer->tick, tick) + sc->data[i]->val4; + else + data.tick = INVALID_TIMER; + break; case SC_FEAR: data.tick = sc->data[i]->val4 * 1000; break; - case SC_BURNING: - data.tick = sc->data[i]->val4 * 2000; - break; - case SC_PYREXIA: - if (type) - continue; - //case SC_OBLIVIONCURSE: // Players are not affected by Oblivion Curse. - data.tick = sc->data[i]->val4 * 3000; - break; - case SC_MAGICMUSHROOM: - if (type) - continue; - data.tick = sc->data[i]->val4 * 4000; - break; - case SC_TOXIN: - if (type) - continue; - case SC_BLEEDING: - data.tick = sc->data[i]->val4 * 10000; - break; default: continue; } diff --git a/src/map/status.h b/src/map/status.h index bb24b23807..d3885fce5b 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -2150,6 +2150,7 @@ struct status_change *status_get_sc(struct block_list *bl); int status_isdead(struct block_list *bl); int status_isimmune(struct block_list *bl); +int status_get_sc_interval(enum sc_type type); int status_get_sc_def(struct block_list *src,struct block_list *bl, enum sc_type type, int rate, int tick, unsigned char flag); //Short version, receives rate in 1->100 range, and does not uses a flag setting. #define sc_start(src, bl, type, rate, val1, tick) status_change_start(src,bl,type,100*(rate),val1,0,0,0,tick,SCSTART_NONE)