From cad5cab976290283bf47738aa8a383891e33b82a Mon Sep 17 00:00:00 2001 From: Playtester <3785983+Playtester@users.noreply.github.com> Date: Wed, 1 May 2024 08:58:51 +0200 Subject: [PATCH] Coma Rework, Exp Calc Type, Fiber Lock Range (#8287) - Coma item bonus is no longer an additional effect / status change, but instead replaces the attack damage with setting HP/SP to 1 * That means Coma attacks will never kill someone, even if the original attack would be lethal * When doing a Coma attack, the attack displays the original damage and this is also what counts towards the mob damage log - Coma is checked for every single event that applies damage, including reflect damage, and for Provoke / Self Provoke * Some Renewal-only effects couldn't be tested and were left unchanged (e.g. damage from White Imprison) - Coma now sets SP to 1 instead of 0 - bComaRace, bWeaponComaRace and bWeaponComaEle won't work on bosses, GVG and battlefield objects - bComaClass and bWeaponComaClass no longer work on GVG and battlefield objects, but can work on bosses if you specify Class_Boss or Class_All - Edge is now defined to only work on normal monster - The default exp_calc_type 0 no longer counts the first attacker twice and instead works like on official servers - Added a new exp_calc_type 2 that counts the first attacker twice as previously - The damage logged will now include overkill damage on exp_calc_type 0 and 2, but not count Coma damage (it will count the damage you would have originally dealt instead) - Fiber Lock now has a range of 9 instead of 7 - Fixes #8279 --- conf/battle/exp.conf | 3 +- db/pre-re/skill_db.yml | 2 +- db/re/item_db_equip.yml | 2 +- db/re/skill_db.yml | 2 +- src/map/battle.cpp | 76 +++++++++++++++++++++++++++++++---------- src/map/battle.hpp | 6 +++- src/map/mob.cpp | 17 ++++----- src/map/skill.cpp | 41 ++++++++-------------- src/map/status.cpp | 22 ++++++++++-- 9 files changed, 110 insertions(+), 61 deletions(-) diff --git a/conf/battle/exp.conf b/conf/battle/exp.conf index 692d61d2c57..86c70f1c87c 100644 --- a/conf/battle/exp.conf +++ b/conf/battle/exp.conf @@ -33,8 +33,7 @@ max_exp_gain_rate: 0 // Method of calculating earned experience when defeating a monster: // 0 = uses damage given / total damage as damage ratio // 1 = uses damage given / max_hp as damage ratio -// NOTE: Using type 1 disables the bonus where the first attacker gets -// his share of the exp doubled when multiple people attack the mob. +// 2 = 0 + first attacker counts twice exp_calc_type: 0 // Experience increase per attacker. That is, every additional attacker to the diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index dd1913a72c8..33cb72990db 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -11062,7 +11062,7 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Range: 7 + Range: 9 Hit: Single HitCount: 1 ActiveInstance: 3 diff --git a/db/re/item_db_equip.yml b/db/re/item_db_equip.yml index 0535a66a179..2b3c1443dc5 100644 --- a/db/re/item_db_equip.yml +++ b/db/re/item_db_equip.yml @@ -948,7 +948,7 @@ Body: Refineable: true Script: | bonus2 bAddEff,Eff_Curse,30; - bonus2 bComaClass,Class_All,10; + bonus2 bComaClass,Class_Normal,10; - Id: 1133 AegisName: Fire_Brand Name: Fireblend diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 6e8bda5fa07..6136c4ce415 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -11312,7 +11312,7 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Range: 7 + Range: 9 Hit: Single HitCount: 1 ActiveInstance: 3 diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 297dcf8a413..45fb432a7c3 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -34,6 +34,8 @@ #include "pc_groups.hpp" #include "pet.hpp" +using namespace rathena; + struct Battle_Config battle_config; static struct eri *delay_damage_ers; //For battle delay damage structures. @@ -254,33 +256,42 @@ struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int return bl_list[rnd()%c]; } -/*========================================== [Playtester] +/** * Deals damage without delay, applies additional effects and triggers monster events * This function is called from battle_delay_damage or battle_delay_damage_sub +* All other instances of battle damage should also go through this function (i.e. anything that displays a damage number) +* Consider calling this function or battle_fix_damage instead status_fix_damage directly * @param src: Source of damage * @param target: Target of damage * @param damage: Damage to be dealt * @param delay: Damage delay * @param skill_lv: Level of skill used -* @param skill_id: ID o skill used +* @param skill_id: ID of skill used * @param dmg_lv: State of the attack (miss, etc.) * @param attack_type: Type of the attack (BF_NORMAL|BF_SKILL|BF_SHORT|BF_LONG|BF_WEAPON|BF_MAGIC|BF_MISC) -* @param additional_effects: Whether additional effect should be applied +* @param additional_effects: Whether additional effects should be applied (otherwise it's just damage+coma) * @param isspdamage: If the damage is done to SP * @param tick: Current tick -*------------------------------------------*/ -void battle_damage(struct block_list *src, struct block_list *target, int64 damage, t_tick delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, t_tick tick, bool isspdamage) { +* @return HP+SP+AP (0 if HP/SP/AP remained unchanged) +*/ +int battle_damage(struct block_list *src, struct block_list *target, int64 damage, t_tick delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, t_tick tick, bool isspdamage) { + int dmg_change; + map_session_data* sd = nullptr; + if (src) + sd = BL_CAST(BL_PC, src); map_freeblock_lock(); if (isspdamage) - status_fix_spdamage(src, target, damage, delay, skill_id); + dmg_change = status_fix_spdamage(src, target, damage, delay, skill_id); + else if (sd && battle_check_coma(*sd, *target, (e_battle_flag)attack_type)) + dmg_change = status_damage(src, target, damage, 0, delay, 16, skill_id); // Coma attack else - status_fix_damage(src, target, damage, delay, skill_id); // We have to separate here between reflect damage and others [icescope] + dmg_change = status_fix_damage(src, target, damage, delay, skill_id); if (attack_type && !status_isdead(target) && additional_effects) skill_additional_effect(src, target, skill_id, skill_lv, attack_type, dmg_lv, tick); - if (dmg_lv > ATK_BLOCK && attack_type) + if (dmg_lv > ATK_BLOCK && attack_type && additional_effects) skill_counter_additional_effect(src, target, skill_id, skill_lv, attack_type, tick); // This is the last place where we have access to the actual damage type, so any monster events depending on type must be placed here - if (target->type == BL_MOB) { + if (target->type == BL_MOB && additional_effects) { mob_data *md = BL_CAST(BL_MOB, target); if (md != nullptr) { @@ -298,6 +309,7 @@ void battle_damage(struct block_list *src, struct block_list *target, int64 dama } } map_freeblock_unlock(); + return dmg_change; } /// Damage Delayed Structure @@ -331,9 +343,7 @@ TIMER_FUNC(battle_delay_damage_sub){ //Deal damage battle_damage(src, target, dat->damage, dat->delay, dat->skill_lv, dat->skill_id, dat->dmg_lv, dat->attack_type, dat->additional_effects, tick, dat->isspdamage); } else if( !src && dat->skill_id == CR_REFLECTSHIELD ) { // it was monster reflected damage, and the monster died, we pass the damage to the character as expected - map_freeblock_lock(); - status_fix_damage(target, target, dat->damage, dat->delay, dat->skill_id); - map_freeblock_unlock(); + battle_fix_damage(target, target, dat->damage, dat->delay, dat->skill_id); } } @@ -6956,7 +6966,7 @@ static void battle_calc_weapon_final_atk_modifiers(struct Damage* wd, struct blo { ATK_RATER(wd->damage, 50) clif_skill_nodamage(target,target,ST_REJECTSWORD, tsc->getSCE(SC_REJECTSWORD)->val1,1); - status_fix_damage(target,src,wd->damage,clif_damage(target,src,gettick(),0,0,wd->damage,0,DMG_NORMAL,0,false),ST_REJECTSWORD); + battle_fix_damage(target,src,wd->damage,clif_damage(target,src,gettick(),0,0,wd->damage,0,DMG_NORMAL,0,false),ST_REJECTSWORD); if (status_isdead(target)) return; if( --(tsc->getSCE(SC_REJECTSWORD)->val3) <= 0 ) @@ -6974,7 +6984,7 @@ static void battle_calc_weapon_final_atk_modifiers(struct Damage* wd, struct blo clif_skill_damage(target, src, gettick(), status_get_amotion(src), 0, rdamage, 1, SR_CRESCENTELBOW_AUTOSPELL, tsc->getSCE(SC_CRESCENTELBOW)->val1, DMG_SINGLE); // This is how official does clif_damage(src, target, gettick(), status_get_amotion(src)+1000, 0, rdamage/10, 1, DMG_NORMAL, 0, false); - status_damage(target, src, rdamage, 0, 0, 0, 0); + battle_fix_damage(target, src, rdamage, 0, SR_CRESCENTELBOW); status_damage(src, target, rdamage/10, 0, 0, 1, 0); status_change_end(target, SC_CRESCENTELBOW); } @@ -9515,6 +9525,36 @@ int64 battle_calc_return_damage(struct block_list* tbl, struct block_list *src, return cap_value(rdamage, 1, status_get_max_hp(tbl)); } +/** Check for Coma damage + * @param sd: Source player + * @param bl: Target + * @param attack_type: Attack type + * @return True if Coma applies, false if Coma does not apply + */ +bool battle_check_coma(map_session_data& sd, struct block_list& target, e_battle_flag attack_type) +{ + struct status_data* tstatus = status_get_status_data(&target); + mob_data* dstmd = BL_CAST(BL_MOB, &target); + + // Coma + if (sd.special_state.bonus_coma && (!dstmd || (!util::vector_exists(status_get_race2(&dstmd->bl), RC2_GVG) && status_get_class(&dstmd->bl) != CLASS_BATTLEFIELD))) { + int rate = 0; + rate += sd.indexed_bonus.coma_class[tstatus->class_] + sd.indexed_bonus.coma_class[CLASS_ALL]; + if(!status_bl_has_mode(&target, MD_STATUSIMMUNE)) + rate += sd.indexed_bonus.coma_race[tstatus->race] + sd.indexed_bonus.coma_race[RC_ALL]; + if (attack_type&BF_WEAPON) { + rate += sd.indexed_bonus.weapon_coma_class[tstatus->class_] + sd.indexed_bonus.weapon_coma_class[CLASS_ALL]; + if (!status_bl_has_mode(&target, MD_STATUSIMMUNE)) { + rate += sd.indexed_bonus.weapon_coma_ele[tstatus->def_ele] + sd.indexed_bonus.weapon_coma_ele[ELE_ALL]; + rate += sd.indexed_bonus.weapon_coma_race[tstatus->race] + sd.indexed_bonus.weapon_coma_race[RC_ALL]; + } + } + if (rate > 0 && rnd_chance(rate, 10000)) + return true; + } + return false; +} + /** * Calculate Vellum damage on a target * @param sd: Player with vanish item @@ -9649,7 +9689,7 @@ int battle_damage_area(struct block_list *bl, va_list ap) { if( amotion ) battle_delay_damage(tick, amotion,src,bl,0,CR_REFLECTSHIELD,0,damage,ATK_DEF,0,true,false); else - status_fix_damage(src,bl,damage,0,LG_REFLECTDAMAGE); + battle_fix_damage(src,bl,damage,0,LG_REFLECTDAMAGE); clif_damage(bl,bl,tick,amotion,dmotion,damage,1,DMG_ENDURE,0,false); skill_additional_effect(src, bl, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick); map_freeblock_unlock(); @@ -10016,7 +10056,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t devotion_damage -= devotion_damage * d_sc->getSCE(SC_REBOUND_S)->val2 / 100; clif_damage(d_bl, d_bl, gettick(), wd.amotion, wd.dmotion, devotion_damage, 1, DMG_NORMAL, 0, false); - status_fix_damage(NULL, d_bl, devotion_damage, 0, CR_DEVOTION); + battle_fix_damage(src, d_bl, devotion_damage, 0, CR_DEVOTION); } } else @@ -10035,7 +10075,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t if (e_bl && !status_isdead(e_bl)) { clif_damage(e_bl, e_bl, tick, 0, 0, damage, wd.div_, DMG_NORMAL, 0, false); - status_fix_damage(NULL, e_bl, damage, 0, EL_WATER_SCREEN); + battle_fix_damage(src, e_bl, damage, 0, EL_WATER_SCREEN); } } } @@ -11103,7 +11143,7 @@ static const struct _battle_data { { "skill_steal_random_options", &battle_config.skill_steal_random_options, 0, 0, 1, }, { "motd_type", &battle_config.motd_type, 0, 0, 1, }, { "finding_ore_rate", &battle_config.finding_ore_rate, 100, 0, INT_MAX, }, - { "exp_calc_type", &battle_config.exp_calc_type, 0, 0, 1, }, + { "exp_calc_type", &battle_config.exp_calc_type, 0, 0, 2, }, { "exp_bonus_attacker", &battle_config.exp_bonus_attacker, 25, 0, INT_MAX, }, { "exp_bonus_max_attacker", &battle_config.exp_bonus_max_attacker, 12, 2, INT_MAX, }, { "min_skill_delay_limit", &battle_config.min_skill_delay_limit, 100, 10, INT_MAX, }, diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 89921c8220f..aa12e979150 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -122,8 +122,11 @@ int64 battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int64 int64 battle_calc_bg_damage(struct block_list *src,struct block_list *bl,int64 damage,uint16 skill_id,int flag); int64 battle_calc_pk_damage(block_list &src, block_list &bl, int64 damage, uint16 skill_id, int flag); -void battle_damage(struct block_list *src, struct block_list *target, int64 damage, t_tick delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, t_tick tick, bool spdamage); +int battle_damage(struct block_list *src, struct block_list *target, int64 damage, t_tick delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, t_tick tick, bool spdamage); int battle_delay_damage (t_tick tick, int amotion, struct block_list *src, struct block_list *target, int attack_type, uint16 skill_id, uint16 skill_lv, int64 damage, enum damage_lv dmg_lv, t_tick ddelay, bool additional_effects, bool spdamage); +static int battle_fix_damage(struct block_list* src, struct block_list* target, int64 damage, t_tick walkdelay, uint16 skill_id) { + return battle_damage(src, target, damage, walkdelay, 0, skill_id, ATK_DEF, BF_MISC, false, gettick(), false); +} int battle_calc_chorusbonus(map_session_data *sd); @@ -140,6 +143,7 @@ uint16 battle_getcurrentskill(struct block_list *bl); int battle_check_undead(int race,int element); int battle_check_target(struct block_list *src, struct block_list *target,int flag); bool battle_check_range(struct block_list *src,struct block_list *bl,int range); +bool battle_check_coma(map_session_data& sd, struct block_list& target, e_battle_flag attack_type); void battle_consume_ammo(map_session_data* sd, int skill, int lv); diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 248c22fcbd2..1e968037a34 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -2631,8 +2631,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // determines if the monster was killed by mercenary damage only merckillonly = (bool)((dmgbltypes & BL_MER) && !(dmgbltypes & ~BL_MER)); - if(!battle_config.exp_calc_type && count > 1) { //Apply first-attacker 200% exp share bonus - //TODO: Determine if this should go before calculating the MVP player instead of after. + if(battle_config.exp_calc_type == 2 && count > 1) { //Apply first-attacker 200% exp share bonus if (UINT_MAX - md->dmglog[0].dmg > md->tdmg) { md->tdmg += md->dmglog[0].dmg; md->dmglog[0].dmg *= 2; @@ -2675,14 +2674,16 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) if (!tmpsd[i]) continue; - if (!battle_config.exp_calc_type && md->tdmg) - //jAthena's exp formula based on total damage. - per = (double)md->dmglog[i].dmg/(double)md->tdmg; + if (battle_config.exp_calc_type == 1 || md->tdmg == 0) { + // eAthena's exp formula based on max hp + per = (double)md->dmglog[i].dmg / (double)status->max_hp; + } else { - //eAthena's exp formula based on max hp. - per = (double)md->dmglog[i].dmg/(double)status->max_hp; - if (per > 2) per = 2; // prevents unlimited exp gain + // Aegis's exp formula based on total damage + per = (double)md->dmglog[i].dmg / (double)md->tdmg; } + // To prevent exploits + if (per > 1) per = 1; //Exclude rebirth tap from this calculation count -= md->state.rebirth; diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 782c7251fcf..b3b9af545f1 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -2222,23 +2222,6 @@ int skill_additional_effect( struct block_list* src, struct block_list *bl, uint src = sd?&sd->bl:src; } - // Coma - if (sd && sd->special_state.bonus_coma && (!md || util::vector_exists(status_get_race2(&md->bl), RC2_GVG) || status_get_class(&md->bl) != CLASS_BATTLEFIELD)) { - int rate = 0; - //! TODO: Filter the skills that shouldn't inflict coma bonus, to avoid some non-damage skills inflict coma. [Cydh] - if (!skill_id || !skill_get_nk(skill_id, NK_NODAMAGE)) { - rate += sd->indexed_bonus.coma_class[tstatus->class_] + sd->indexed_bonus.coma_class[CLASS_ALL]; - rate += sd->indexed_bonus.coma_race[tstatus->race] + sd->indexed_bonus.coma_race[RC_ALL]; - } - if (attack_type&BF_WEAPON) { - rate += sd->indexed_bonus.weapon_coma_ele[tstatus->def_ele] + sd->indexed_bonus.weapon_coma_ele[ELE_ALL]; - rate += sd->indexed_bonus.weapon_coma_race[tstatus->race] + sd->indexed_bonus.weapon_coma_race[RC_ALL]; - rate += sd->indexed_bonus.weapon_coma_class[tstatus->class_] + sd->indexed_bonus.weapon_coma_class[CLASS_ALL]; - } - if (rate > 0) - status_change_start(src,bl, SC_COMA, rate, 0, 0, src->id, 0, 0, SCSTART_NONE); - } - if( attack_type&BF_WEAPON ) { // Breaking Equipment if( sd && battle_config.equip_self_break_rate ) @@ -3749,7 +3732,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * nbl = battle_getenemyarea(bl,bl->x,bl->y,2,BL_CHAR,bl->id); if( nbl ){ // Only one target is chosen. damage = damage / 2; // Deflect half of the damage to a target nearby - clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, status_fix_damage(bl,nbl,damage,0,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, DMG_SINGLE); + clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, battle_fix_damage(bl,nbl,damage,0,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, DMG_SINGLE); } } @@ -3997,12 +3980,13 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * // Instant damage if( !dmg.amotion ) { + //Deal damage before knockback to allow stuff like firewall+storm gust combo. if( (!tsc || (!tsc->getSCE(SC_DEVOTION) && skill_id != CR_REFLECTSHIELD && !tsc->getSCE(SC_WATER_SCREEN_OPTION)) #ifndef RENEWAL || skill_id == HW_GRAVITATION #endif || skill_id == NPC_EVILLAND) && !shadow_flag ) - status_fix_damage(src,bl,damage,dmg.dmotion,skill_id); //Deal damage before knockback to allow stuff like firewall+storm gust combo. + battle_damage(src, bl, damage, dmg.dmotion, skill_lv, skill_id, dmg.dmg_lv, dmg.flag, false, tick, false); if( !status_isdead(bl) && additional_effects ) skill_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,dmg.dmg_lv,tick); if( damage > 0 ) //Counter status effects [Skotlex] @@ -4064,7 +4048,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * if (!rmdamage) { clif_damage(d_bl, d_bl, gettick(), 0, 0, devotion_damage, 0, DMG_NORMAL, 0, false); - status_fix_damage(NULL, d_bl, devotion_damage, 0, 0); + battle_fix_damage(src, d_bl, devotion_damage, 0, 0); } else { bool isDevotRdamage = false; @@ -4074,12 +4058,12 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * // This check is only for magical skill. // For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage clif_damage(bl, (!isDevotRdamage) ? bl : d_bl, gettick(), 0, 0, devotion_damage, 0, DMG_NORMAL, 0, false); - status_fix_damage(bl, (!isDevotRdamage) ? bl : d_bl, devotion_damage, 0, 0); + battle_fix_damage(bl, (!isDevotRdamage) ? bl : d_bl, devotion_damage, 0, 0); } } else { status_change_end(bl, SC_DEVOTION); if (!dmg.amotion) - status_fix_damage(src, bl, damage, dmg.dmotion, 0); + battle_fix_damage(src, bl, damage, dmg.dmotion, 0); } } if (tsc->getSCE(SC_WATER_SCREEN_OPTION)) { @@ -4089,10 +4073,10 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * if (e_bl) { if (!rmdamage) { clif_skill_damage(e_bl, e_bl, gettick(), 0, 0, damage, dmg.div_, skill_id, -1, skill_get_hit(skill_id)); - status_fix_damage(NULL, e_bl, damage, 0, 0); + battle_fix_damage(src, e_bl, damage, 0, 0); } else { clif_skill_damage(bl, bl, gettick(), 0, 0, damage, dmg.div_, skill_id, -1, skill_get_hit(skill_id)); - status_fix_damage(bl, bl, damage, 0, 0); + battle_fix_damage(bl, bl, damage, 0, 0); } } } @@ -5021,7 +5005,7 @@ static int skill_tarotcard(struct block_list* src, struct block_list *target, ui } case 4: // THE CHARIOT - 1000 damage, random armor destroyed { - status_fix_damage(src, target, 1000, 0, skill_id); + battle_fix_damage(src, target, 1000, 0, skill_id); clif_damage(src, target, tick, 0, 0, 1000, 0, DMG_NORMAL, 0, false); if (!status_isdead(target)) { @@ -5075,7 +5059,7 @@ static int skill_tarotcard(struct block_list* src, struct block_list *target, ui } case 11: // THE DEVIL - 6666 damage, atk and matk halved, cursed { - status_fix_damage(src, target, 6666, 0, skill_id); + battle_fix_damage(src, target, 6666, 0, skill_id); clif_damage(src, target, tick, 0, 0, 6666, 0, DMG_NORMAL, 0, false); sc_start(src, target, SC_INCATKRATE, 100, -50, skill_get_time2(skill_id, skill_lv)); sc_start(src, target, SC_INCMATKRATE, 100, -50, skill_get_time2(skill_id, skill_lv)); @@ -5084,7 +5068,7 @@ static int skill_tarotcard(struct block_list* src, struct block_list *target, ui } case 12: // THE TOWER - 4444 damage { - status_fix_damage(src, target, 4444, 0, skill_id); + battle_fix_damage(src, target, 4444, 0, skill_id); clif_damage(src, target, tick, 0, 0, 4444, 0, DMG_NORMAL, 0, false); break; } @@ -8371,6 +8355,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui dstmd->state.provoke_flag = src->id; mob_target(dstmd, src, skill_get_range2(src, skill_id, skill_lv, true)); } + // Provoke can cause Coma even though it's a nodamage skill + if (sd && battle_check_coma(*sd, *bl, BF_MISC)) + status_change_start(src, bl, SC_COMA, 10000, skill_lv, 0, src->id, 0, 0, SCSTART_NONE); break; case ML_DEVOTION: diff --git a/src/map/status.cpp b/src/map/status.cpp index 7d6f8d6a3e6..2baf9715fe2 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -1408,6 +1408,7 @@ int64 status_charge(struct block_list* bl, int64 hp, int64 sp) * flag&2: Fail if there is not enough to subtract * flag&4: Mob does not give EXP/Loot if killed * flag&8: Used to damage SP of a dead character + * flag&16: Coma damage - Log it as normal damage, but actually set HP/SP to 1 * @return hp+sp+ap * Note: HP/SP/AP are integer values, not percentages. Values should be * calculated either within function call or before @@ -1527,10 +1528,25 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in unit_skillcastcancel(target, 2); } + // We need to log the real damage on exp_calc_type 1 + if (battle_config.exp_calc_type == 1) { + dhp = hp; + // Coma real damage + if (flag&16) + dhp = status->hp - 1; + } + status->hp-= hp; status->sp-= sp; status->ap-= ap; + // Coma + if (flag&16) { + status->hp = 1; + status->sp = 1; + if (!sp) sp = 1; // To make sure the status bar is updated + } + if (sc && hp && status->hp) { if (sc->getSCE(SC_AUTOBERSERK) && (!sc->getSCE(SC_PROVOKE) || !sc->getSCE(SC_PROVOKE)->val4) && @@ -1544,9 +1560,11 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in status_change_end(target, SC_SATURDAYNIGHTFEVER); } + // Need to pass original HP damage for the mob damage log + dhp = cap_value(dhp, INT_MIN, INT_MAX); switch (target->type) { case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp,ap); break; - case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break; + case BL_MOB: mob_damage((TBL_MOB*)target, src, (int)dhp); break; case BL_HOM: hom_damage((TBL_HOM*)target); break; case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break; case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break; @@ -11226,7 +11244,7 @@ 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 - status_zap(bl, status->hp-1, val2?0:status->sp); + status_zap(bl, status->hp-1, val2?0:status->sp-1); return 1; break; case SC_CLOSECONFINE2: