// $Id: battle.c,v 1.10 2004/09/29 21:08:17 Akitasha Exp $ #include #include #include #include #include "battle.hpp" #include "../common/timer.hpp" #include "../common/nullpo.hpp" #include "clif.hpp" #include "guild.hpp" #include "itemdb.hpp" #include "map.hpp" #include "mob.hpp" #include "pc.hpp" #include "skill.hpp" #include "../common/socket.hpp" #include "../common/mt_rand.hpp" #ifdef MEMWATCH #include "memwatch.hpp" #endif int attr_fix_table[4][10][10]; struct Battle_Config battle_config; /*========================================== * 二点間の距離を返す * 戻りは整数で0以上 *------------------------------------------ */ static int distance (int x0, int y0, int x1, int y1) { int dx, dy; dx = abs (x0 - x1); dy = abs (y0 - y1); return dx > dy ? dx : dy; } /*========================================== * 自分をロックしている対象の数を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_counttargeted (struct block_list *bl, struct block_list *src, int target_lv) { nullpo_retr (0, bl); if (bl->type == BL_PC) return pc_counttargeted ((struct map_session_data *) bl, src, target_lv); else if (bl->type == BL_MOB) return mob_counttargeted ((struct mob_data *) bl, src, target_lv); return 0; } /*========================================== * 対象のClassを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_class (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->mob_class; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->status.pc_class; else return 0; } /*========================================== * 対象の方向を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_dir (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->dir; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->dir; else return 0; } /*========================================== * 対象のレベルを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_lv (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->stats[MOB_LV]; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->status.base_level; else return 0; } /*========================================== * 対象の射程を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_range (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return mob_db[((struct mob_data *) bl)->mob_class].range; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->attackrange; else return 0; } /*========================================== * 対象のHPを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_hp (struct block_list *bl) { nullpo_retr (1, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->hp; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->status.hp; else return 1; } /*========================================== * 対象のMHPを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_max_hp (struct block_list *bl) { nullpo_retr (1, bl); if (bl->type == BL_PC && ((struct map_session_data *) bl)) return ((struct map_session_data *) bl)->status.max_hp; else { struct status_change *sc_data = battle_get_sc_data (bl); int max_hp = 1; if (bl->type == BL_MOB && ((struct mob_data *) bl)) { max_hp = ((struct mob_data *) bl)->stats[MOB_MAX_HP]; if (mob_db[((struct mob_data *) bl)->mob_class].mexp > 0) { if (battle_config.mvp_hp_rate != 100) max_hp = (max_hp * battle_config.mvp_hp_rate) / 100; } else { if (battle_config.monster_hp_rate != 100) max_hp = (max_hp * battle_config.monster_hp_rate) / 100; } } if (sc_data) { if (sc_data[SC_APPLEIDUN].timer != -1) max_hp += ((5 + sc_data[SC_APPLEIDUN].val1 * 2 + ((sc_data[SC_APPLEIDUN].val2 + 1) >> 1) + sc_data[SC_APPLEIDUN].val3 / 10) * max_hp) / 100; } if (max_hp < 1) max_hp = 1; return max_hp; } return 1; } /*========================================== * 対象のStrを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_str (struct block_list *bl) { int str = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && ((struct mob_data *) bl)) str = ((struct mob_data *) bl)->stats[MOB_STR]; else if (bl->type == BL_PC && ((struct map_session_data *) bl)) return ((struct map_session_data *) bl)->paramc[0]; if (sc_data) { if (sc_data[SC_LOUD].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) str += 4; if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) { // ブレッシング int race = battle_get_race (bl); if (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) str >>= 1; // 悪 魔/不死 else str += sc_data[SC_BLESSING].val1; // その他 } if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト str += 5; } if (str < 0) str = 0; return str; } /*========================================== * 対象のAgiを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_agi (struct block_list *bl) { int agi = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) agi = ((struct mob_data *) bl)->stats[MOB_AGI]; else if (bl->type == BL_PC && (struct map_session_data *) bl) agi = ((struct map_session_data *) bl)->paramc[1]; if (sc_data) { if (sc_data[SC_INCREASEAGI].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1 && bl->type != BL_PC) // 速度増加(PCはpc.cで) agi += 2 + sc_data[SC_INCREASEAGI].val1; if (sc_data[SC_CONCENTRATE].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) agi += agi * (2 + sc_data[SC_CONCENTRATE].val1) / 100; if (sc_data[SC_DECREASEAGI].timer != -1) // 速度減少 agi -= 2 + sc_data[SC_DECREASEAGI].val1; if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア agi >>= 1; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト agi += 5; } if (agi < 0) agi = 0; return agi; } /*========================================== * 対象のVitを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_vit (struct block_list *bl) { int vit = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) vit = ((struct mob_data *) bl)->stats[MOB_VIT]; else if (bl->type == BL_PC && (struct map_session_data *) bl) vit = ((struct map_session_data *) bl)->paramc[2]; if (sc_data) { if (sc_data[SC_STRIPARMOR].timer != -1 && bl->type != BL_PC) vit = vit * 60 / 100; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト vit += 5; } if (vit < 0) vit = 0; return vit; } /*========================================== * 対象のIntを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_int (struct block_list *bl) { int int_ = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) int_ = ((struct mob_data *) bl)->stats[MOB_INT]; else if (bl->type == BL_PC && (struct map_session_data *) bl) int_ = ((struct map_session_data *) bl)->paramc[3]; if (sc_data) { if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) { // ブレッシング int race = battle_get_race (bl); if (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) int_ >>= 1; // 悪 魔/不死 else int_ += sc_data[SC_BLESSING].val1; // その他 } if (sc_data[SC_STRIPHELM].timer != -1 && bl->type != BL_PC) int_ = int_ * 60 / 100; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト int_ += 5; } if (int_ < 0) int_ = 0; return int_; } /*========================================== * 対象のDexを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_dex (struct block_list *bl) { int dex = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) dex = ((struct mob_data *) bl)->stats[MOB_DEX]; else if (bl->type == BL_PC && (struct map_session_data *) bl) dex = ((struct map_session_data *) bl)->paramc[4]; if (sc_data) { if (sc_data[SC_CONCENTRATE].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) dex += dex * (2 + sc_data[SC_CONCENTRATE].val1) / 100; if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) { // ブレッシング int race = battle_get_race (bl); if (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) dex >>= 1; // 悪 魔/不死 else dex += sc_data[SC_BLESSING].val1; // その他 } if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア dex >>= 1; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト dex += 5; } if (dex < 0) dex = 0; return dex; } /*========================================== * 対象のLukを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_luk (struct block_list *bl) { int luk = 0; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) luk = ((struct mob_data *) bl)->stats[MOB_LUK]; else if (bl->type == BL_PC && (struct map_session_data *) bl) luk = ((struct map_session_data *) bl)->paramc[5]; if (sc_data) { if (sc_data[SC_GLORIA].timer != -1 && bl->type != BL_PC) // グロリア(PCはpc.cで) luk += 30; if (sc_data[SC_CURSE].timer != -1) // 呪い luk = 0; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト luk += 5; } if (luk < 0) luk = 0; return luk; } /*========================================== * 対象のFleeを返す(汎用) * 戻りは整数で1以上 *------------------------------------------ */ int battle_get_flee (struct block_list *bl) { int flee = 1; struct status_change *sc_data; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) flee = ((struct map_session_data *) bl)->flee; else flee = battle_get_agi (bl) + battle_get_lv (bl); if (sc_data) { if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) flee += flee * (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 + (sc_data[SC_WHISTLE].val3 >> 16)) / 100; if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) flee -= flee * 25 / 100; if (sc_data[SC_WINDWALK].timer != -1 && bl->type != BL_PC) // ウィンドウォーク flee += flee * (sc_data[SC_WINDWALK].val2) / 100; if (sc_data[SC_SPIDERWEB].timer != -1 && bl->type != BL_PC) //スパイダーウェブ flee -= flee * 50 / 100; if (battle_is_unarmed (bl)) flee += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 flee += skill_power_bl (bl, TMW_SPEED) >> 3; } if (flee < 1) flee = 1; return flee; } /*========================================== * 対象のHitを返す(汎用) * 戻りは整数で1以上 *------------------------------------------ */ int battle_get_hit (struct block_list *bl) { int hit = 1; struct status_change *sc_data; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) hit = ((struct map_session_data *) bl)->hit; else hit = battle_get_dex (bl) + battle_get_lv (bl); if (sc_data) { if (sc_data[SC_HUMMING].timer != -1 && bl->type != BL_PC) // hit += hit * (sc_data[SC_HUMMING].val1 * 2 + sc_data[SC_HUMMING].val2 + sc_data[SC_HUMMING].val3) / 100; if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) // 呪い hit -= hit * 25 / 100; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト hit += 3 * (sc_data[SC_TRUESIGHT].val1); if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション hit += (hit * (10 * (sc_data[SC_CONCENTRATION].val1))) / 100; if (battle_is_unarmed (bl)) hit += (skill_power_bl (bl, TMW_BRAWLING) >> 4); // +12 for 200 } if (hit < 1) hit = 1; return hit; } /*========================================== * 対象の完全回避を返す(汎用) * 戻りは整数で1以上 *------------------------------------------ */ int battle_get_flee2 (struct block_list *bl) { int flee2 = 1; struct status_change *sc_data; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) { flee2 = battle_get_luk (bl) + 10; flee2 += ((struct map_session_data *) bl)->flee2 - (((struct map_session_data *) bl)->paramc[5] + 10); } else flee2 = battle_get_luk (bl) + 1; if (sc_data) { if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) flee2 += (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 + (sc_data[SC_WHISTLE].val3 & 0xffff)) * 10; if (battle_is_unarmed (bl)) flee2 += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 flee2 += skill_power_bl (bl, TMW_SPEED) >> 3; } if (flee2 < 1) flee2 = 1; return flee2; } /*========================================== * 対象のクリティカルを返す(汎用) * 戻りは整数で1以上 *------------------------------------------ */ static int battle_get_critical (struct block_list *bl) { int critical = 1; struct status_change *sc_data; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) { critical = battle_get_luk (bl) * 2 + 10; critical += ((struct map_session_data *) bl)->critical - ((((struct map_session_data *) bl)->paramc[5] * 3) + 10); } else critical = battle_get_luk (bl) * 3 + 1; if (sc_data) { if (sc_data[SC_FORTUNE].timer != -1 && bl->type != BL_PC) critical += (10 + sc_data[SC_FORTUNE].val1 + sc_data[SC_FORTUNE].val2 + sc_data[SC_FORTUNE].val3) * 10; if (sc_data[SC_EXPLOSIONSPIRITS].timer != -1 && bl->type != BL_PC) critical += sc_data[SC_EXPLOSIONSPIRITS].val2; if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) //トゥルーサイト critical += critical * sc_data[SC_TRUESIGHT].val1 / 100; } if (critical < 1) critical = 1; return critical; } /*========================================== * base_atkの取得 * 戻りは整数で1以上 *------------------------------------------ */ int battle_get_baseatk (struct block_list *bl) { struct status_change *sc_data; int batk = 1; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) batk = ((struct map_session_data *) bl)->base_atk; //設定されているbase_atk else { //それ以外なら int str, dstr; str = battle_get_str (bl); //STR dstr = str / 10; batk = dstr * dstr + str; //base_atkを計算する } if (sc_data) { //状態異常あり if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) //PCでプロボック(SM_PROVOKE)状態 batk = batk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; //base_atk増加 if (sc_data[SC_CURSE].timer != -1) //呪われていたら batk -= batk * 25 / 100; //base_atkが25%減少 if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション batk += batk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; } if (batk < 1) batk = 1; //base_atkは最低でも1 return batk; } /*========================================== * 対象のAtkを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_atk (struct block_list *bl) { struct status_change *sc_data; int atk = 0; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) atk = ((struct map_session_data *) bl)->watk; else if (bl->type == BL_MOB && (struct mob_data *) bl) atk = ((struct mob_data *) bl)->stats[MOB_ATK1]; if (sc_data) { if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) atk = atk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; if (sc_data[SC_CURSE].timer != -1) atk -= atk * 25 / 100; if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション atk += atk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; } if (atk < 0) atk = 0; return atk; } /*========================================== * 対象の左手Atkを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ static int battle_get_atk_ (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) { int atk = ((struct map_session_data *) bl)->watk_; if (((struct map_session_data *) bl)->sc_data[SC_CURSE].timer != -1) atk -= atk * 25 / 100; return atk; } else return 0; } /*========================================== * 対象のAtk2を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_atk2 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->watk2; else { struct status_change *sc_data = battle_get_sc_data (bl); int atk2 = 0; if (bl->type == BL_MOB && (struct mob_data *) bl) atk2 = ((struct mob_data *) bl)->stats[MOB_ATK2]; if (sc_data) { if (sc_data[SC_IMPOSITIO].timer != -1) atk2 += sc_data[SC_IMPOSITIO].val1 * 5; if (sc_data[SC_PROVOKE].timer != -1) atk2 = atk2 * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; if (sc_data[SC_CURSE].timer != -1) atk2 -= atk2 * 25 / 100; if (sc_data[SC_DRUMBATTLE].timer != -1) atk2 += sc_data[SC_DRUMBATTLE].val2; if (sc_data[SC_NIBELUNGEN].timer != -1 && (battle_get_element (bl) / 10) >= 8) atk2 += sc_data[SC_NIBELUNGEN].val2; if (sc_data[SC_STRIPWEAPON].timer != -1) atk2 = atk2 * 90 / 100; if (sc_data[SC_CONCENTRATION].timer != -1) //コンセントレーション atk2 += atk2 * (5 * sc_data[SC_CONCENTRATION].val1) / 100; } if (atk2 < 0) atk2 = 0; return atk2; } return 0; } /*========================================== * 対象の左手Atk2を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ static int battle_get_atk_2 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC) return ((struct map_session_data *) bl)->watk_2; else return 0; } /*========================================== * 対象のMAtk1を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ static int battle_get_matk1 (struct block_list *bl) { struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB) { int matk, int_ = battle_get_int (bl); matk = int_ + (int_ / 5) * (int_ / 5); if (sc_data) if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; return matk; } else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->matk1; else return 0; } /*========================================== * 対象のMAtk2を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ static int battle_get_matk2 (struct block_list *bl) { struct status_change *sc_data = battle_get_sc_data (bl); nullpo_retr (0, bl); if (bl->type == BL_MOB) { int matk, int_ = battle_get_int (bl); matk = int_ + (int_ / 7) * (int_ / 7); if (sc_data) if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; return matk; } else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->matk2; else return 0; } /*========================================== * 対象のDefを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_def (struct block_list *bl) { struct status_change *sc_data; int def = 0, skilltimer = -1, skillid = 0; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) { def = ((struct map_session_data *) bl)->def; skilltimer = ((struct map_session_data *) bl)->skilltimer; skillid = ((struct map_session_data *) bl)->skillid; } else if (bl->type == BL_MOB && (struct mob_data *) bl) { def = ((struct mob_data *) bl)->stats[MOB_DEF]; skilltimer = ((struct mob_data *) bl)->skilltimer; skillid = ((struct mob_data *) bl)->skillid; } if (def < 1000000) { if (sc_data) { //キーピング時はDEF100 if (sc_data[SC_KEEPING].timer != -1) def = 100; //プロボック時は減算 if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) def = (def * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; //戦太鼓の響き時は加算 if (sc_data[SC_DRUMBATTLE].timer != -1 && bl->type != BL_PC) def += sc_data[SC_DRUMBATTLE].val3; //毒にかかっている時は減算 if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) def = def * 75 / 100; //ストリップシールド時は減算 if (sc_data[SC_STRIPSHIELD].timer != -1 && bl->type != BL_PC) def = def * 85 / 100; //シグナムクルシス時は減算 if (sc_data[SC_SIGNUMCRUCIS].timer != -1 && bl->type != BL_PC) def = def * (100 - sc_data[SC_SIGNUMCRUCIS].val2) / 100; //永遠の混沌時はDEF0になる if (sc_data[SC_ETERNALCHAOS].timer != -1 && bl->type != BL_PC) def = 0; //凍結、石化時は右シフト if (sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0)) def >>= 1; //コンセントレーション時は減算 if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) def = (def * (100 - 5 * sc_data[SC_CONCENTRATION].val1)) / 100; } //詠唱中は詠唱時減算率に基づいて減算 if (skilltimer != -1) { int def_rate = skill_get_castdef (skillid); if (def_rate != 0) def = (def * (100 - def_rate)) / 100; } } if (def < 0) def = 0; return def; } /*========================================== * 対象のMDefを返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_mdef (struct block_list *bl) { struct status_change *sc_data; int mdef = 0; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC && (struct map_session_data *) bl) mdef = ((struct map_session_data *) bl)->mdef; else if (bl->type == BL_MOB && (struct mob_data *) bl) mdef = ((struct mob_data *) bl)->stats[MOB_MDEF]; if (mdef < 1000000) { if (sc_data) { //バリアー状態時はMDEF100 if (mdef < 90 && sc_data[SC_MBARRIER].timer != -1) { mdef += sc_data[SC_MBARRIER].val1; if (mdef > 90) mdef = 90; } if (sc_data[SC_BARRIER].timer != -1) mdef = 100; //凍結、石化時は1.25倍 if (sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0)) mdef = mdef * 125 / 100; if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) mdef -= (mdef * 6 * sc_data[SC_MINDBREAKER].val1) / 100; } } if (mdef < 0) mdef = 0; return mdef; } /*========================================== * 対象のDef2を返す(汎用) * 戻りは整数で1以上 *------------------------------------------ */ int battle_get_def2 (struct block_list *bl) { struct status_change *sc_data; int def2 = 1; nullpo_retr (1, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_PC) def2 = ((struct map_session_data *) bl)->def2; else if (bl->type == BL_MOB) def2 = ((struct mob_data *) bl)->stats[MOB_VIT]; if (sc_data) { if (sc_data[SC_ANGELUS].timer != -1 && bl->type != BL_PC) def2 = def2 * (110 + 5 * sc_data[SC_ANGELUS].val1) / 100; if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) def2 = (def2 * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) def2 = def2 * 75 / 100; //コンセントレーション時は減算 if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) def2 = def2 * (100 - 5 * sc_data[SC_CONCENTRATION].val1) / 100; } if (def2 < 1) def2 = 1; return def2; } /*========================================== * 対象のMDef2を返す(汎用) * 戻りは整数で0以上 *------------------------------------------ */ int battle_get_mdef2 (struct block_list *bl) { int mdef2 = 0; struct status_change *sc_data = battle_get_sc_data (bl); nullpo_retr (0, bl); if (bl->type == BL_MOB) mdef2 = ((struct mob_data *) bl)->stats[MOB_INT] + (((struct mob_data *) bl)->stats[MOB_VIT] >> 1); else if (bl->type == BL_PC) mdef2 = ((struct map_session_data *) bl)->mdef2 + (((struct map_session_data *) bl)->paramc[2] >> 1); if (sc_data) { if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) mdef2 -= (mdef2 * 6 * sc_data[SC_MINDBREAKER].val1) / 100; } if (mdef2 < 0) mdef2 = 0; return mdef2; } /*========================================== * 対象のSpeed(移動速度)を返す(汎用) * 戻りは整数で1以上 * Speedは小さいほうが移動速度が速い *------------------------------------------ */ int battle_get_speed (struct block_list *bl) { nullpo_retr (1000, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->speed; else { struct status_change *sc_data = battle_get_sc_data (bl); int speed = 1000; if (bl->type == BL_MOB && (struct mob_data *) bl) speed = ((struct mob_data *) bl)->stats[MOB_SPEED]; if (sc_data) { //速度増加時は25%減算 if (sc_data[SC_INCREASEAGI].timer != -1 && sc_data[SC_DONTFORGETME].timer == -1) speed -= speed * 25 / 100; //速度減少時は25%加算 if (sc_data[SC_DECREASEAGI].timer != -1) speed = speed * 125 / 100; //クァグマイア時は50%加算 if (sc_data[SC_QUAGMIRE].timer != -1) speed = speed * 3 / 2; //私を忘れないで…時は加算 if (sc_data[SC_DONTFORGETME].timer != -1) speed = speed * (100 + sc_data[SC_DONTFORGETME].val1 * 2 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3 & 0xffff)) / 100; //金剛時は25%加算 if (sc_data[SC_STEELBODY].timer != -1) speed = speed * 125 / 100; //ディフェンダー時は加算 if (sc_data[SC_DEFENDER].timer != -1) speed = (speed * (155 - sc_data[SC_DEFENDER].val1 * 5)) / 100; //踊り状態は4倍遅い if (sc_data[SC_DANCING].timer != -1) speed *= 4; //呪い時は450加算 if (sc_data[SC_CURSE].timer != -1) speed = speed + 450; //ウィンドウォーク時はLv*2%減算 if (sc_data[SC_WINDWALK].timer != -1) speed -= (speed * (sc_data[SC_WINDWALK].val1 * 2)) / 100; } if (speed < 1) speed = 1; return speed; } return 1000; } /*========================================== * 対象のaDelay(攻撃時ディレイ)を返す(汎用) * aDelayは小さいほうが攻撃速度が速い *------------------------------------------ */ int battle_get_adelay (struct block_list *bl) { nullpo_retr (4000, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return (((struct map_session_data *) bl)->aspd << 1); else { struct status_change *sc_data = battle_get_sc_data (bl); int adelay = 4000, aspd_rate = 100, i; if (bl->type == BL_MOB && (struct mob_data *) bl) adelay = ((struct mob_data *) bl)->stats[MOB_ADELAY]; if (sc_data) { //ツーハンドクイッケン使用時でクァグマイアでも私を忘れないで…でもない時は3割減算 if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ aspd_rate -= 30; //アドレナリンラッシュ使用時でツーハンドクイッケンでもクァグマイアでも私を忘れないで…でもない時は if (sc_data[SC_ADRENALINE].timer != -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) { // アドレナリンラッシュ //使用者とパーティメンバーで格差が出る設定でなければ3割減算 if (sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penaly) aspd_rate -= 30; //そうでなければ2.5割減算 else aspd_rate -= 25; } //スピアクィッケン時は減算 if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; //夕日のアサシンクロス時は減算 if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_SPEARSQUICKEN].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) aspd_rate -= 5 + sc_data[SC_ASSNCROS].val1 + sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; //私を忘れないで…時は加算 if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで aspd_rate += sc_data[SC_DONTFORGETME].val1 * 3 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3 >> 16); //金剛時25%加算 if (sc_data[SC_STEELBODY].timer != -1) // 金剛 aspd_rate += 25; //増速ポーション使用時は減算 if (sc_data[i = SC_SPEEDPOTION2].timer != -1 || sc_data[i = SC_SPEEDPOTION1].timer != -1 || sc_data[i = SC_SPEEDPOTION0].timer != -1) aspd_rate -= sc_data[i].val1; // Fate's `haste' spell works the same as the above if (sc_data[SC_HASTE].timer != -1) aspd_rate -= sc_data[SC_HASTE].val1; //ディフェンダー時は加算 if (sc_data[SC_DEFENDER].timer != -1) adelay += (1100 - sc_data[SC_DEFENDER].val1 * 100); } if (aspd_rate != 100) adelay = adelay * aspd_rate / 100; if (adelay < battle_config.monster_max_aspd << 1) adelay = battle_config.monster_max_aspd << 1; return adelay; } return 4000; } int battle_get_amotion (struct block_list *bl) { nullpo_retr (2000, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->amotion; else { struct status_change *sc_data = battle_get_sc_data (bl); int amotion = 2000, aspd_rate = 100, i; if (bl->type == BL_MOB && (struct mob_data *) bl) amotion = mob_db[((struct mob_data *) bl)->mob_class].amotion; if (sc_data) { if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ aspd_rate -= 30; if (sc_data[SC_ADRENALINE].timer != -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) { // アドレナリンラッシュ if (sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penaly) aspd_rate -= 30; else aspd_rate -= 25; } if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_SPEARSQUICKEN].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) aspd_rate -= 5 + sc_data[SC_ASSNCROS].val1 + sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで aspd_rate += sc_data[SC_DONTFORGETME].val1 * 3 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3 >> 16); if (sc_data[SC_STEELBODY].timer != -1) // 金剛 aspd_rate += 25; if (sc_data[i = SC_SPEEDPOTION2].timer != -1 || sc_data[i = SC_SPEEDPOTION1].timer != -1 || sc_data[i = SC_SPEEDPOTION0].timer != -1) aspd_rate -= sc_data[i].val1; if (sc_data[SC_HASTE].timer != -1) aspd_rate -= sc_data[SC_HASTE].val1; if (sc_data[SC_DEFENDER].timer != -1) amotion += (550 - sc_data[SC_DEFENDER].val1 * 50); } if (aspd_rate != 100) amotion = amotion * aspd_rate / 100; if (amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd; return amotion; } return 2000; } int battle_get_dmotion (struct block_list *bl) { int ret; struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) { ret = mob_db[((struct mob_data *) bl)->mob_class].dmotion; if (battle_config.monster_damage_delay_rate != 100) ret = ret * battle_config.monster_damage_delay_rate / 400; } else if (bl->type == BL_PC && (struct map_session_data *) bl) { ret = ((struct map_session_data *) bl)->dmotion; if (battle_config.pc_damage_delay_rate != 100) ret = ret * battle_config.pc_damage_delay_rate / 400; } else return 2000; if ((sc_data && sc_data[SC_ENDURE].timer != -1) || (bl->type == BL_PC && ((struct map_session_data *) bl)->special_state.infinite_endure)) ret = 0; return ret; } int battle_get_element (struct block_list *bl) { int ret = 20; struct status_change *sc_data; nullpo_retr (ret, bl); sc_data = battle_get_sc_data (bl); if (bl->type == BL_MOB && (struct mob_data *) bl) // 10の位=Lv*2、1の位=属性 ret = ((struct mob_data *) bl)->def_ele; else if (bl->type == BL_PC && (struct map_session_data *) bl) ret = 20 + ((struct map_session_data *) bl)->def_ele; // 防御属性Lv1 if (sc_data) { if (sc_data[SC_BENEDICTIO].timer != -1) // 聖体降福 ret = 26; if (sc_data[SC_FREEZE].timer != -1) // 凍結 ret = 21; if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) ret = 22; } return ret; } int battle_get_attack_element (struct block_list *bl) { int ret = 0; struct status_change *sc_data = battle_get_sc_data (bl); nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) ret = 0; else if (bl->type == BL_PC && (struct map_session_data *) bl) ret = ((struct map_session_data *) bl)->atk_ele; if (sc_data) { if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン ret = 1; if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン ret = 2; if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー ret = 3; if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー ret = 4; if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン ret = 5; if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ ret = 6; } return ret; } int battle_get_attack_element2 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) { int ret = ((struct map_session_data *) bl)->atk_ele_; struct status_change *sc_data = ((struct map_session_data *) bl)->sc_data; if (sc_data) { if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン ret = 1; if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン ret = 2; if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー ret = 3; if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー ret = 4; if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン ret = 5; if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ ret = 6; } return ret; } return 0; } int battle_get_party_id (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->status.party_id; else if (bl->type == BL_MOB && (struct mob_data *) bl) { struct mob_data *md = (struct mob_data *) bl; if (md->master_id > 0) return -md->master_id; return -md->bl.id; } else if (bl->type == BL_SKILL && (struct skill_unit *) bl) return ((struct skill_unit *) bl)->group->party_id; else return 0; } int battle_get_guild_id (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->status.guild_id; else if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->mob_class; else if (bl->type == BL_SKILL && (struct skill_unit *) bl) return ((struct skill_unit *) bl)->group->guild_id; else return 0; } int battle_get_race (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return mob_db[((struct mob_data *) bl)->mob_class].race; else if (bl->type == BL_PC && (struct map_session_data *) bl) return 7; else return 0; } int battle_get_size (struct block_list *bl) { nullpo_retr (1, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return mob_db[((struct mob_data *) bl)->mob_class].size; else if (bl->type == BL_PC && (struct map_session_data *) bl) return 1; else return 1; } int battle_get_mode (struct block_list *bl) { nullpo_retr (0x01, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return mob_db[((struct mob_data *) bl)->mob_class].mode; else return 0x01; // とりあえず動くということで1 } int battle_get_mexp (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) { const struct mob_data *mob = (struct mob_data *) bl; const int retval = (mob_db[mob->mob_class].mexp * (int) (mob->stats[MOB_XP_BONUS])) >> MOB_XP_BONUS_SHIFT; fprintf (stderr, "Modifier of %x: -> %d\n", mob->stats[MOB_XP_BONUS], retval); return retval; } else return 0; } int battle_get_stat (int stat_id /* SP_VIT or similar */ , struct block_list *bl) { switch (stat_id) { case SP_STR: return battle_get_str (bl); case SP_AGI: return battle_get_agi (bl); case SP_DEX: return battle_get_dex (bl); case SP_VIT: return battle_get_vit (bl); case SP_INT: return battle_get_int (bl); case SP_LUK: return battle_get_luk (bl); default: return 0; } } // StatusChange系の所得 struct status_change *battle_get_sc_data (struct block_list *bl) { nullpo_retr (NULL, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return ((struct mob_data *) bl)->sc_data; else if (bl->type == BL_PC && (struct map_session_data *) bl) return ((struct map_session_data *) bl)->sc_data; return NULL; } short *battle_get_sc_count (struct block_list *bl) { nullpo_retr (NULL, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return &((struct mob_data *) bl)->sc_count; else if (bl->type == BL_PC && (struct map_session_data *) bl) return &((struct map_session_data *) bl)->sc_count; return NULL; } short *battle_get_opt1 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return &((struct mob_data *) bl)->opt1; else if (bl->type == BL_PC && (struct map_session_data *) bl) return &((struct map_session_data *) bl)->opt1; else if (bl->type == BL_NPC && (struct npc_data *) bl) return &((struct npc_data *) bl)->opt1; return 0; } short *battle_get_opt2 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return &((struct mob_data *) bl)->opt2; else if (bl->type == BL_PC && (struct map_session_data *) bl) return &((struct map_session_data *) bl)->opt2; else if (bl->type == BL_NPC && (struct npc_data *) bl) return &((struct npc_data *) bl)->opt2; return 0; } short *battle_get_opt3 (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return &((struct mob_data *) bl)->opt3; else if (bl->type == BL_PC && (struct map_session_data *) bl) return &((struct map_session_data *) bl)->opt3; else if (bl->type == BL_NPC && (struct npc_data *) bl) return &((struct npc_data *) bl)->opt3; return 0; } short *battle_get_option (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB && (struct mob_data *) bl) return &((struct mob_data *) bl)->option; else if (bl->type == BL_PC && (struct map_session_data *) bl) return &((struct map_session_data *) bl)->status.option; else if (bl->type == BL_NPC && (struct npc_data *) bl) return &((struct npc_data *) bl)->option; return 0; } //------------------------------------------------------------------- // ダメージの遅延 struct battle_delay_damage_ { struct block_list *src, *target; int damage; int flag; }; static void battle_delay_damage_sub (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { struct battle_delay_damage_ *dat = (struct battle_delay_damage_ *) data; if (dat && map_id2bl (id) == dat->src && dat->target->prev != NULL) battle_damage (dat->src, dat->target, dat->damage, dat->flag); free (dat); } int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int damage, int flag) { struct battle_delay_damage_ *dat; CREATE (dat, struct battle_delay_damage_, 1); nullpo_retr (0, src); nullpo_retr (0, target); dat->src = src; dat->target = target; dat->damage = damage; dat->flag = flag; add_timer (tick, battle_delay_damage_sub, src->id, (int) dat); return 0; } // 実際にHPを操作 int battle_damage (struct block_list *bl, struct block_list *target, int damage, int flag) { struct map_session_data *sd = NULL; struct status_change *sc_data = battle_get_sc_data (target); short *sc_count; int i; nullpo_retr (0, target); //blはNULLで呼ばれることがあるので他でチェック if (damage == 0) return 0; if (target->prev == NULL) return 0; if (bl) { if (bl->prev == NULL) return 0; if (bl->type == BL_PC) sd = (struct map_session_data *) bl; } if (damage < 0) return battle_heal (bl, target, -damage, 0, flag); if (!flag && (sc_count = battle_get_sc_count (target)) != NULL && *sc_count > 0) { // 凍結、石化、睡眠を消去 if (sc_data[SC_FREEZE].timer != -1) skill_status_change_end (target, SC_FREEZE, -1); if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) skill_status_change_end (target, SC_STONE, -1); if (sc_data[SC_SLEEP].timer != -1) skill_status_change_end (target, SC_SLEEP, -1); } if (target->type == BL_MOB) { // MOB struct mob_data *md = (struct mob_data *) target; if (md && md->skilltimer != -1 && md->state.skillcastcancel) // 詠唱妨害 skill_castcancel (target, 0); return mob_damage (bl, md, damage, 0); } else if (target->type == BL_PC) { // PC struct map_session_data *tsd = (struct map_session_data *) target; if (tsd && tsd->sc_data && tsd->sc_data[SC_DEVOTION].val1) { // ディボーションをかけられている struct map_session_data *md = map_id2sd (tsd->sc_data[SC_DEVOTION].val1); if (md && skill_devotion3 (&md->bl, target->id)) { skill_devotion (md, target->id); } else if (md && bl) for (i = 0; i < 5; i++) if (md->dev.val1[i] == target->id) { clif_damage (bl, &md->bl, gettick (), 0, 0, damage, 0, 0, 0); pc_damage (&md->bl, md, damage); return 0; } } if (tsd && tsd->skilltimer != -1) { // 詠唱妨害 // フェンカードや妨害されないスキルかの検査 if ((!tsd->special_state.no_castcancel || map[bl->m].flag.gvg) && tsd->state.skillcastcancel && !tsd->special_state.no_castcancel2) skill_castcancel (target, 0); } return pc_damage (bl, tsd, damage); } else if (target->type == BL_SKILL) return skill_unit_ondamaged ((struct skill_unit *) target, bl, damage, gettick ()); return 0; } int battle_heal (struct block_list *bl, struct block_list *target, int hp, int sp, int flag) { nullpo_retr (0, target); //blはNULLで呼ばれることがあるので他でチェック if (target->type == BL_PC && pc_isdead ((struct map_session_data *) target)) return 0; if (hp == 0 && sp == 0) return 0; if (hp < 0) return battle_damage (bl, target, -hp, flag); if (target->type == BL_MOB) return mob_heal ((struct mob_data *) target, hp); else if (target->type == BL_PC) return pc_heal ((struct map_session_data *) target, hp, sp); return 0; } // 攻撃停止 int battle_stopattack (struct block_list *bl) { nullpo_retr (0, bl); if (bl->type == BL_MOB) return mob_stopattack ((struct mob_data *) bl); else if (bl->type == BL_PC) return pc_stopattack ((struct map_session_data *) bl); return 0; } // 移動停止 int battle_stopwalking (struct block_list *bl, int type) { nullpo_retr (0, bl); if (bl->type == BL_MOB) return mob_stop_walking ((struct mob_data *) bl, type); else if (bl->type == BL_PC) return pc_stop_walking ((struct map_session_data *) bl, type); return 0; } /*========================================== * ダメージの属性修正 *------------------------------------------ */ int battle_attr_fix (int damage, int atk_elem, int def_elem) { int def_type = def_elem % 10, def_lv = def_elem / 10 / 2; if (atk_elem < 0 || atk_elem > 9 || def_type < 0 || def_type > 9 || def_lv < 1 || def_lv > 4) { // 属 性値がおかしいのでとりあえずそのまま返す if (battle_config.error_log) printf ("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n", atk_elem, def_type, def_lv); return damage; } return damage * attr_fix_table[def_lv - 1][atk_elem][def_type] / 100; } /*========================================== * ダメージ最終計算 *------------------------------------------ */ int battle_calc_damage (struct block_list *src, struct block_list *bl, int damage, int div_, int skill_num, int skill_lv, int flag) { struct map_session_data *sd = NULL; struct mob_data *md = NULL; struct status_change *sc_data, *sc; short *sc_count; int class_; nullpo_retr (0, bl); class_ = battle_get_class (bl); if (bl->type == BL_MOB) md = (struct mob_data *) bl; else sd = (struct map_session_data *) bl; sc_data = battle_get_sc_data (bl); sc_count = battle_get_sc_count (bl); if (sc_count != NULL && *sc_count > 0) { if (sc_data[SC_SAFETYWALL].timer != -1 && damage > 0 && flag & BF_WEAPON && flag & BF_SHORT && skill_num != NPC_GUIDEDATTACK) { // セーフティウォール struct skill_unit *unit = (struct skill_unit *) sc_data[SC_SAFETYWALL].val2; if (unit && unit->alive && (--unit->group->val2) <= 0) skill_delunit (unit); skill_unit_move (bl, gettick (), 1); // 重ね掛けチェック damage = 0; } if (sc_data[SC_PNEUMA].timer != -1 && damage > 0 && flag & BF_WEAPON && flag & BF_LONG && skill_num != NPC_GUIDEDATTACK) { // ニューマ damage = 0; } if (sc_data[SC_ROKISWEIL].timer != -1 && damage > 0 && flag & BF_MAGIC) { // ニューマ damage = 0; } if (sc_data[SC_AETERNA].timer != -1 && damage > 0) { // レックスエーテルナ damage <<= 1; skill_status_change_end (bl, SC_AETERNA, -1); } //属性場のダメージ増加 if (sc_data[SC_VOLCANO].timer != -1) { // ボルケーノ if (flag & BF_SKILL && skill_get_pl (skill_num) == 3) damage += damage * sc_data[SC_VOLCANO].val4 / 100; else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 3)) damage += damage * sc_data[SC_VOLCANO].val4 / 100; } if (sc_data[SC_VIOLENTGALE].timer != -1) { // バイオレントゲイル if (flag & BF_SKILL && skill_get_pl (skill_num) == 4) damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 4)) damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; } if (sc_data[SC_DELUGE].timer != -1) { // デリュージ if (flag & BF_SKILL && skill_get_pl (skill_num) == 1) damage += damage * sc_data[SC_DELUGE].val4 / 100; else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 1)) damage += damage * sc_data[SC_DELUGE].val4 / 100; } if (sc_data[SC_ENERGYCOAT].timer != -1 && damage > 0 && flag & BF_WEAPON) { // エナジーコート if (sd) { if (sd->status.sp > 0) { int per = sd->status.sp * 5 / (sd->status.max_sp + 1); sd->status.sp -= sd->status.sp * (per * 5 + 10) / 1000; if (sd->status.sp < 0) sd->status.sp = 0; damage -= damage * ((per + 1) * 6) / 100; clif_updatestatus (sd, SP_SP); } if (sd->status.sp <= 0) skill_status_change_end (bl, SC_ENERGYCOAT, -1); } else damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100; } if (sc_data[SC_KYRIE].timer != -1 && damage > 0) { // キリエエレイソン sc = &sc_data[SC_KYRIE]; sc->val2 -= damage; if (flag & BF_WEAPON) { if (sc->val2 >= 0) damage = 0; else damage = -sc->val2; } if ((--sc->val3) <= 0 || (sc->val2 <= 0) || skill_num == AL_HOLYLIGHT) skill_status_change_end (bl, SC_KYRIE, -1); } if (sc_data[SC_BASILICA].timer != -1 && damage > 0) { // ニューマ damage = 0; } if (sc_data[SC_LANDPROTECTOR].timer != -1 && damage > 0 && flag & BF_MAGIC) { // ニューマ damage = 0; } if (sc_data[SC_AUTOGUARD].timer != -1 && damage > 0 && flag & BF_WEAPON) { if (MRAND (100) < sc_data[SC_AUTOGUARD].val2) { damage = 0; clif_skill_nodamage (bl, bl, CR_AUTOGUARD, sc_data[SC_AUTOGUARD].val1, 1); if (sd) sd->canmove_tick = gettick () + 300; else if (md) md->canmove_tick = gettick () + 300; } } // -- moonsoul (chance to block attacks with new Lord Knight skill parrying) // if (sc_data[SC_PARRYING].timer != -1 && damage > 0 && flag & BF_WEAPON) { if (MRAND (100) < sc_data[SC_PARRYING].val2) { damage = 0; clif_skill_nodamage (bl, bl, LK_PARRYING, sc_data[SC_PARRYING].val1, 1); } } // リジェクトソード if (sc_data[SC_REJECTSWORD].timer != -1 && damage > 0 && flag & BF_WEAPON && ((src->type == BL_PC && ((struct map_session_data *) src)->status.weapon == (1 || 2 || 3)) || src->type == BL_MOB)) { if (MRAND (100) < (10 + 5 * sc_data[SC_REJECTSWORD].val1)) { //反射確率は10+5*Lv damage = damage * 50 / 100; battle_damage (bl, src, damage, 0); //ダメージを与えたのは良いんだが、ここからどうして表示するんだかわかんねぇ //エフェクトもこれでいいのかわかんねぇ clif_skill_nodamage (bl, bl, ST_REJECTSWORD, sc_data[SC_REJECTSWORD].val1, 1); if ((--sc_data[SC_REJECTSWORD].val2) <= 0) skill_status_change_end (bl, SC_REJECTSWORD, -1); } } } if (class_ == 1288 || class_ == 1287 || class_ == 1286 || class_ == 1285) { // if(class == 1288) { if (class_ == 1288 && flag & BF_SKILL) damage = 0; if (src->type == BL_PC) { struct guild *g = guild_search (((struct map_session_data *) src)-> status.guild_id); struct guild_castle *gc = guild_mapname2gc (map[bl->m].name); if (!((struct map_session_data *) src)->status.guild_id) damage = 0; if (gc && agit_flag == 0 && class_ != 1288) // guardians cannot be damaged during non-woe [Valaris] damage = 0; // end woe check [Valaris] if (g == NULL) damage = 0; //ギルド未加入ならダメージ無し else if ((gc != NULL) && guild_isallied (g, gc)) damage = 0; //自占領ギルドのエンペならダメージ無し else if (g && guild_checkskill (g, GD_APPROVAL) <= 0) damage = 0; //正規ギルド承認がないとダメージ無し else if (battle_config.guild_max_castles != 0 && guild_checkcastles (g) >= battle_config.guild_max_castles) damage = 0; // [MouseJstr] } else damage = 0; } if (map[bl->m].flag.gvg && damage > 0) { //GvG if (flag & BF_WEAPON) { if (flag & BF_SHORT) damage = damage * battle_config.gvg_short_damage_rate / 100; if (flag & BF_LONG) damage = damage * battle_config.gvg_long_damage_rate / 100; } if (flag & BF_MAGIC) damage = damage * battle_config.gvg_magic_damage_rate / 100; if (flag & BF_MISC) damage = damage * battle_config.gvg_misc_damage_rate / 100; if (damage < 1) damage = 1; } if (battle_config.skill_min_damage || flag & BF_MISC) { if (div_ < 255) { if (damage > 0 && damage < div_) damage = div_; } else if (damage > 0 && damage < 3) damage = 3; } if (md != NULL && md->hp > 0 && damage > 0) // 反撃などのMOBスキル判定 mobskill_event (md, flag); return damage; } /*========================================== * 修練ダメージ *------------------------------------------ */ static int battle_addmastery (struct map_session_data *sd, struct block_list *target, int dmg, int type) { int damage, skill; int race = battle_get_race (target); int weapon; damage = 0; nullpo_retr (0, sd); // デーモンベイン(+3 〜 +30) vs 不死 or 悪魔 (死人は含めない?) if ((skill = pc_checkskill (sd, AL_DEMONBANE)) > 0 && (battle_check_undead (race, battle_get_elem_type (target)) || race == 6)) damage += (skill * 3); // ビーストベイン(+4 〜 +40) vs 動物 or 昆虫 if ((skill = pc_checkskill (sd, HT_BEASTBANE)) > 0 && (race == 2 || race == 4)) damage += (skill * 4); if (type == 0) weapon = sd->weapontype1; else weapon = sd->weapontype2; switch (weapon) { case 0x01: // 短剣 (Updated By AppleGirl) case 0x02: // 1HS { // 剣修練(+4 〜 +40) 片手剣 短剣含む if ((skill = pc_checkskill (sd, SM_SWORD)) > 0) { damage += (skill * 4); } break; } case 0x03: // 2HS { // 両手剣修練(+4 〜 +40) 両手剣 if ((skill = pc_checkskill (sd, SM_TWOHAND)) > 0) { damage += (skill * 4); } break; } case 0x04: // 1HL { // 槍修練(+4 〜 +40,+5 〜 +50) 槍 if ((skill = pc_checkskill (sd, KN_SPEARMASTERY)) > 0) { if (!pc_isriding (sd)) damage += (skill * 4); // ペコに乗ってない else damage += (skill * 5); // ペコに乗ってる } break; } case 0x05: // 2HL { // 槍修練(+4 〜 +40,+5 〜 +50) 槍 if ((skill = pc_checkskill (sd, KN_SPEARMASTERY)) > 0) { if (!pc_isriding (sd)) damage += (skill * 4); // ペコに乗ってない else damage += (skill * 5); // ペコに乗ってる } break; } case 0x06: // 片手斧 { if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) { damage += (skill * 3); } break; } case 0x07: // Axe by Tato { if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) { damage += (skill * 3); } break; } case 0x08: // メイス { // メイス修練(+3 〜 +30) メイス if ((skill = pc_checkskill (sd, PR_MACEMASTERY)) > 0) { damage += (skill * 3); } break; } case 0x09: // なし? break; case 0x0a: // 杖 break; case 0x0b: // 弓 break; case 0x00: // 素手 case 0x0c: // Knuckles { // 鉄拳(+3 〜 +30) 素手,ナックル if ((skill = pc_checkskill (sd, MO_IRONHAND)) > 0) { damage += (skill * 3); } break; } case 0x0d: // Musical Instrument { // 楽器の練習(+3 〜 +30) 楽器 if ((skill = pc_checkskill (sd, BA_MUSICALLESSON)) > 0) { damage += (skill * 3); } break; } case 0x0e: // Dance Mastery { // Dance Lesson Skill Effect(+3 damage for every lvl = +30) 鞭 if ((skill = pc_checkskill (sd, DC_DANCINGLESSON)) > 0) { damage += (skill * 3); } break; } case 0x0f: // Book { // Advance Book Skill Effect(+3 damage for every lvl = +30) { if ((skill = pc_checkskill (sd, SA_ADVANCEDBOOK)) > 0) { damage += (skill * 3); } break; } case 0x10: // Katars { // カタール修練(+3 〜 +30) カタール if ((skill = pc_checkskill (sd, AS_KATAR)) > 0) { //ソニックブロー時は別処理(1撃に付き1/8適応) damage += (skill * 3); } break; } } damage = dmg + damage; return (damage); } static struct Damage battle_calc_mob_weapon_attack (struct block_list *src, struct block_list *target, int skill_num, int skill_lv, int wflag) { struct map_session_data *tsd = NULL; struct mob_data *md = (struct mob_data *) src, *tmd = NULL; int hitrate, flee, cri = 0, atkmin, atkmax; int luk, target_count = 1; int def1 = battle_get_def (target); int def2 = battle_get_def2 (target); int t_vit = battle_get_vit (target); struct Damage wd; int damage, damage2 = 0, type, div_, blewcount = skill_get_blewcount (skill_num, skill_lv); int flag, skill, ac_flag = 0, dmg_lv = 0; int t_mode = 0, t_race = 0, t_size = 1, s_race = 0, s_ele = 0; struct status_change *sc_data, *t_sc_data; short *sc_count; short *option, *opt1, *opt2; //return前の処理があるので情報出力部のみ変更 if (src == NULL || target == NULL || md == NULL) { nullpo_info (NLP_MARK); memset (&wd, 0, sizeof (wd)); return wd; } s_race = battle_get_race (src); s_ele = battle_get_attack_element (src); sc_data = battle_get_sc_data (src); sc_count = battle_get_sc_count (src); option = battle_get_option (src); opt1 = battle_get_opt1 (src); opt2 = battle_get_opt2 (src); // ターゲット if (target->type == BL_PC) tsd = (struct map_session_data *) target; else if (target->type == BL_MOB) tmd = (struct mob_data *) target; t_race = battle_get_race (target); t_size = battle_get_size (target); t_mode = battle_get_mode (target); t_sc_data = battle_get_sc_data (target); if ((skill_num == 0 || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) || (target->type == BL_MOB && battle_config.monster_auto_counter_type & 2)) && skill_lv >= 0) { if (skill_num != CR_GRANDCROSS && t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1) { int dir = map_calc_dir (src, target->x, target->y), t_dir = battle_get_dir (target); int dist = distance (src->x, src->y, target->x, target->y); if (dist <= 0 || map_check_dir (dir, t_dir)) { memset (&wd, 0, sizeof (wd)); t_sc_data[SC_AUTOCOUNTER].val3 = 0; t_sc_data[SC_AUTOCOUNTER].val4 = 1; if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) { int range = battle_get_range (target); if ((target->type == BL_PC && ((struct map_session_data *) target)-> status.weapon != 11 && dist <= range + 1) || (target->type == BL_MOB && range <= 3 && dist <= range + 1)) t_sc_data[SC_AUTOCOUNTER].val3 = src->id; } return wd; } else ac_flag = 1; } } flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 // 回避率計算、回避判定は後で flee = battle_get_flee (target); if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) target_count += battle_counttargeted (target, src, battle_config.agi_penaly_count_lv); if (battle_config.agi_penaly_type > 0) { if (target_count >= battle_config.agi_penaly_count) { if (battle_config.agi_penaly_type == 1) flee = (flee * (100 - (target_count - (battle_config.agi_penaly_count - 1)) * battle_config.agi_penaly_num)) / 100; else if (battle_config.agi_penaly_type == 2) flee -= (target_count - (battle_config.agi_penaly_count - 1)) * battle_config.agi_penaly_num; if (flee < 1) flee = 1; } } hitrate = battle_get_hit (src) - flee + 80; type = 0; // normal div_ = 1; // single attack luk = battle_get_luk (src); if (battle_config.enemy_str) damage = battle_get_baseatk (src); else damage = 0; if (skill_num == HW_MAGICCRASHER) { /* マジッククラッシャーはMATKで殴る */ atkmin = battle_get_matk1 (src); atkmax = battle_get_matk2 (src); } else { atkmin = battle_get_atk (src); atkmax = battle_get_atk2 (src); } if (mob_db[md->mob_class].range > 3) flag = (flag & ~BF_RANGEMASK) | BF_LONG; if (atkmin > atkmax) atkmin = atkmax; if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) { // マキシマイズパワー atkmin = atkmax; } cri = battle_get_critical (src); cri -= battle_get_luk (target) * 3; if (battle_config.enemy_critical_rate != 100) { cri = cri * battle_config.enemy_critical_rate / 100; if (cri < 1) cri = 1; } if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に cri <<= 1; if (ac_flag) cri = 1000; if (skill_num == KN_AUTOCOUNTER) { if (!(battle_config.monster_auto_counter_type & 1)) cri = 1000; else cri <<= 1; } if (tsd && tsd->critical_def) cri = cri * (100 - tsd->critical_def) / 100; if ((skill_num == 0 || skill_num == KN_AUTOCOUNTER) && skill_lv >= 0 && battle_config.enemy_critical && (MRAND (1000)) < cri) // 判定(スキルの場合は無視) // 敵の判定 { damage += atkmax; type = 0x0a; } else { int vitbonusmax; if (atkmax > atkmin) damage += atkmin + MRAND ((atkmax - atkmin + 1)); else damage += atkmin; // スキル修正1(攻撃力倍化系) // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 // バッシュ,マグナムブレイク, // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, // メマーナイト,カートレボリューション // ダブルストレイフィング,アローシャワー,チャージアロー, // ソニックブロー if (sc_data) { //状態異常中のダメージ追加 if (sc_data[SC_OVERTHRUST].timer != -1) // オーバートラスト damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; if (sc_data[SC_TRUESIGHT].timer != -1) // トゥルーサイト damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; if (sc_data[SC_BERSERK].timer != -1) // バーサーク damage += damage * 50 / 100; } if (skill_num > 0) { int i; if ((i = skill_get_pl (skill_num)) > 0) s_ele = i; flag = (flag & ~BF_SKILLMASK) | BF_SKILL; switch (skill_num) { case SM_BASH: // バッシュ damage = damage * (100 + 30 * skill_lv) / 100; hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; break; case SM_MAGNUM: // マグナムブレイク damage = damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; break; case MC_MAMMONITE: // メマーナイト damage = damage * (100 + 50 * skill_lv) / 100; break; case AC_DOUBLE: // ダブルストレイフィング damage = damage * (180 + 20 * skill_lv) / 100; div_ = 2; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case AC_SHOWER: // アローシャワー damage = damage * (75 + 5 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case AC_CHARGEARROW: // チャージアロー damage = damage * 150 / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case KN_PIERCE: // ピアース damage = damage * (100 + 10 * skill_lv) / 100; hitrate = hitrate * (100 + 5 * skill_lv) / 100; div_ = t_size + 1; damage *= div_; break; case KN_SPEARSTAB: // スピアスタブ damage = damage * (100 + 15 * skill_lv) / 100; break; case KN_SPEARBOOMERANG: // スピアブーメラン damage = damage * (100 + 50 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case KN_BRANDISHSPEAR: // ブランディッシュスピア damage = damage * (100 + 20 * skill_lv) / 100; if (skill_lv > 3 && wflag == 1) damage2 += damage / 2; if (skill_lv > 6 && wflag == 1) damage2 += damage / 4; if (skill_lv > 9 && wflag == 1) damage2 += damage / 8; if (skill_lv > 6 && wflag == 2) damage2 += damage / 2; if (skill_lv > 9 && wflag == 2) damage2 += damage / 4; if (skill_lv > 9 && wflag == 3) damage2 += damage / 2; damage += damage2; blewcount = 0; break; case KN_BOWLINGBASH: // ボウリングバッシュ damage = damage * (100 + 50 * skill_lv) / 100; blewcount = 0; break; case KN_AUTOCOUNTER: if (battle_config.monster_auto_counter_type & 1) hitrate += 20; else hitrate = 1000000; flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; break; case AS_SONICBLOW: // ソニックブロウ damage = damage * (300 + 50 * skill_lv) / 100; div_ = 8; break; case TF_SPRINKLESAND: // 砂まき damage = damage * 125 / 100; break; case MC_CARTREVOLUTION: // カートレボリューション damage = (damage * 150) / 100; break; // 以下MOB case NPC_COMBOATTACK: // 多段攻撃 div_ = skill_get_num (skill_num, skill_lv); damage *= div_; break; case NPC_RANDOMATTACK: // ランダムATK攻撃 damage = damage * (MPRAND (50, 150)) / 100; break; // 属性攻撃(適当) case NPC_WATERATTACK: case NPC_GROUNDATTACK: case NPC_FIREATTACK: case NPC_WINDATTACK: case NPC_POISONATTACK: case NPC_HOLYATTACK: case NPC_DARKNESSATTACK: case NPC_TELEKINESISATTACK: damage = damage * (100 + 25 * (skill_lv - 1)) / 100; break; case NPC_GUIDEDATTACK: hitrate = 1000000; break; case NPC_RANGEATTACK: flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case NPC_PIERCINGATT: flag = (flag & ~BF_RANGEMASK) | BF_SHORT; break; case RG_BACKSTAP: // バックスタブ damage = damage * (300 + 40 * skill_lv) / 100; hitrate = 1000000; break; case RG_RAID: // サプライズアタック damage = damage * (100 + 40 * skill_lv) / 100; break; case RG_INTIMIDATE: // インティミデイト damage = damage * (100 + 30 * skill_lv) / 100; break; case CR_SHIELDCHARGE: // シールドチャージ damage = damage * (100 + 20 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_SHORT; s_ele = 0; break; case CR_SHIELDBOOMERANG: // シールドブーメラン damage = damage * (100 + 30 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; s_ele = 0; break; case CR_HOLYCROSS: // ホーリークロス damage = damage * (100 + 35 * skill_lv) / 100; div_ = 2; break; case CR_GRANDCROSS: hitrate = 1000000; break; case AM_DEMONSTRATION: // デモンストレーション damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; break; case AM_ACIDTERROR: // アシッドテラー damage = damage * (100 + 40 * skill_lv) / 100; damage2 = damage2 * (100 + 40 * skill_lv) / 100; break; case MO_FINGEROFFENSIVE: //指弾 damage = damage * (100 + 50 * skill_lv) / 100; div_ = 1; break; case MO_INVESTIGATE: // 発 勁 if (def1 < 1000000) damage = damage * (100 + 75 * skill_lv) / 100 * (def1 + def2) / 100; hitrate = 1000000; s_ele = 0; break; case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 damage = damage * 8 + 250 + (skill_lv * 150); hitrate = 1000000; s_ele = 0; break; case MO_CHAINCOMBO: // 連打掌 damage = damage * (150 + 50 * skill_lv) / 100; div_ = 4; break; case BA_MUSICALSTRIKE: // ミュージカルストライク damage = damage * (100 + 50 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case DC_THROWARROW: // 矢撃ち damage = damage * (100 + 50 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case MO_COMBOFINISH: // 猛龍拳 damage = damage * (240 + 60 * skill_lv) / 100; break; case CH_TIGERFIST: // 伏虎拳 damage = damage * (100 + 20 * skill_lv) / 100; break; case CH_CHAINCRUSH: // 連柱崩撃 damage = damage * (100 + 20 * skill_lv) / 100; div_ = skill_get_num (skill_num, skill_lv); break; case CH_PALMSTRIKE: // 猛虎硬派山 damage = damage * (50 + 100 * skill_lv) / 100; break; case LK_SPIRALPIERCE: /* スパイラルピアース */ damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に div_ = 5; if (tsd) tsd->canmove_tick = gettick () + 1000; else if (tmd) tmd->canmove_tick = gettick () + 1000; break; case LK_HEADCRUSH: /* ヘッドクラッシュ */ damage = damage * (100 + 20 * skill_lv) / 100; break; case LK_JOINTBEAT: /* ジョイントビート */ damage = damage * (50 + 10 * skill_lv) / 100; break; case ASC_METEORASSAULT: /* メテオアサルト */ damage = damage * (40 + 40 * skill_lv) / 100; break; case SN_SHARPSHOOTING: /* シャープシューティング */ damage += damage * (30 * skill_lv) / 100; break; case CG_ARROWVULCAN: /* アローバルカン */ damage = damage * (160 + 40 * skill_lv) / 100; div_ = 9; break; case AS_SPLASHER: /* ベナムスプラッシャー */ damage = damage * (200 + 20 * skill_lv) / 100; break; } } if (skill_num != NPC_CRITICALSLASH) { // 対 象の防御力によるダメージの減少 // ディバインプロテクション(ここでいいのかな?) if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != KN_AUTOCOUNTER && def1 < 1000000) { //DEF, VIT無視 int t_def; target_count = 1 + battle_counttargeted (target, src, battle_config.vit_penaly_count_lv); if (battle_config.vit_penaly_type > 0) { if (target_count >= battle_config.vit_penaly_count) { if (battle_config.vit_penaly_type == 1) { def1 = (def1 * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; def2 = (def2 * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; t_vit = (t_vit * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; } else if (battle_config.vit_penaly_type == 2) { def1 -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; def2 -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; t_vit -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; } if (def1 < 0) def1 = 0; if (def2 < 1) def2 = 1; if (t_vit < 1) t_vit = 1; } } t_def = def2 * 8 / 10; if (battle_check_undead (s_race, battle_get_elem_type (src)) || s_race == 6) if (tsd && (skill = pc_checkskill (tsd, AL_DP)) > 0) t_def += skill * 3; vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; if (battle_config.monster_defense_type) { damage = damage - (def1 * battle_config.monster_defense_type) - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } else { damage = damage * (100 - def1) / 100 - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } } } } // 0未満だった場合1に補正 if (damage < 1) damage = 1; // 回避修正 if (hitrate < 1000000) hitrate = ((hitrate > 95) ? 95 : ((hitrate < 5) ? 5 : hitrate)); if (hitrate < 1000000 && // 必中攻撃 (t_sc_data != NULL && (t_sc_data[SC_SLEEP].timer != -1 || // 睡眠は必中 t_sc_data[SC_STAN].timer != -1 || // スタンは必中 t_sc_data[SC_FREEZE].timer != -1 || (t_sc_data[SC_STONE].timer != -1 && t_sc_data[SC_STONE].val2 == 0)))) // 凍結は必中 hitrate = 1000000; if (type == 0 && MRAND (100) >= hitrate) { damage = damage2 = 0; dmg_lv = ATK_FLEE; } else { dmg_lv = ATK_DEF; } if (tsd) { int cardfix = 100, i; cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属 性によるダメージ耐性 cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 if (mob_db[md->mob_class].mode & 0x20) cardfix = cardfix * (100 - tsd->subrace[10]) / 100; else cardfix = cardfix * (100 - tsd->subrace[11]) / 100; for (i = 0; i < tsd->add_def_class_count; i++) { if (tsd->add_def_classid[i] == md->mob_class) { cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; break; } } if (flag & BF_LONG) cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; if (flag & BF_SHORT) cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; damage = damage * cardfix / 100; } if (t_sc_data) { int cardfix = 100; if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; if (cardfix != 100) damage = damage * cardfix / 100; } if (t_sc_data && t_sc_data[SC_ASSUMPTIO].timer != -1) { //アシャンプティオ if (!map[target->m].flag.pvp) damage = damage / 3; else damage = damage / 2; } if (damage < 0) damage = 0; // 属 性の適用 if (!((battle_config.mob_ghostring_fix == 1) && (battle_get_element (target) == 8) && (target->type == BL_PC))) // [MouseJstr] if (skill_num != 0 || s_ele != 0 || !battle_config.mob_attack_attr_none) damage = battle_attr_fix (damage, s_ele, battle_get_element (target)); if (sc_data && sc_data[SC_AURABLADE].timer != -1) /* オーラブレード 必中 */ damage += sc_data[SC_AURABLADE].val1 * 10; if (skill_num == PA_PRESSURE) /* プレッシャー 必中? */ damage = 700 + 100 * skill_lv; // インベナム修正 if (skill_num == TF_POISON) { damage = battle_attr_fix (damage + 15 * skill_lv, s_ele, battle_get_element (target)); } if (skill_num == MC_CARTREVOLUTION) { damage = battle_attr_fix (damage, 0, battle_get_element (target)); } // 完全回避の判定 if (skill_num == 0 && skill_lv >= 0 && tsd != NULL && MRAND (1000) < battle_get_flee2 (target)) { damage = 0; type = 0x0b; dmg_lv = ATK_LUCKY; } if (battle_config.enemy_perfect_flee) { if (skill_num == 0 && skill_lv >= 0 && tmd != NULL && MRAND (1000) < battle_get_flee2 (target)) { damage = 0; type = 0x0b; dmg_lv = ATK_LUCKY; } } // if(def1 >= 1000000 && damage > 0) if (t_mode & 0x40 && damage > 0) damage = 1; if (tsd && tsd->special_state.no_weapon_damage) damage = 0; if (skill_num != CR_GRANDCROSS) damage = battle_calc_damage (src, target, damage, div_, skill_num, skill_lv, flag); wd.damage = damage; wd.damage2 = 0; wd.type = type; wd.div_ = div_; wd.amotion = battle_get_amotion (src); if (skill_num == KN_AUTOCOUNTER) wd.amotion >>= 1; wd.dmotion = battle_get_dmotion (target); wd.blewcount = blewcount; wd.flag = flag; wd.dmg_lv = dmg_lv; return wd; } int battle_is_unarmed (struct block_list *bl) { if (!bl) return 0; if (bl->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) bl; return (sd->equip_index[EQUIP_SHIELD] == -1 && sd->equip_index[EQUIP_WEAPON] == -1); } else return 0; } /* * ========================================================================= * PCの武器による攻撃 *------------------------------------------------------------------------- */ static struct Damage battle_calc_pc_weapon_attack (struct block_list *src, struct block_list *target, int skill_num, int skill_lv, int wflag) { struct map_session_data *sd = (struct map_session_data *) src, *tsd = NULL; struct mob_data *tmd = NULL; int hitrate, flee, cri = 0, atkmin, atkmax; int dex, luk, target_count = 1; int def1 = battle_get_def (target); int def2 = battle_get_def2 (target); int t_vit = battle_get_vit (target); struct Damage wd; int damage, damage2, damage3 = 0, damage4 = 0, type, div_, blewcount = skill_get_blewcount (skill_num, skill_lv); int flag, skill, dmg_lv = 0; int t_mode = 0, t_race = 0, t_size = 1, s_race = 7, s_ele = 0; struct status_change *sc_data, *t_sc_data; short *sc_count; short *option, *opt1, *opt2; int atkmax_ = 0, atkmin_ = 0, s_ele_; //二刀流用 int watk, watk_, cardfix, t_ele; int da = 0, i, t_class, ac_flag = 0; int idef_flag = 0, idef_flag_ = 0; int target_distance; //return前の処理があるので情報出力部のみ変更 if (src == NULL || target == NULL || sd == NULL) { nullpo_info (NLP_MARK); memset (&wd, 0, sizeof (wd)); return wd; } // アタッカー s_race = battle_get_race (src); //種族 s_ele = battle_get_attack_element (src); //属性 s_ele_ = battle_get_attack_element2 (src); //左手属性 sc_data = battle_get_sc_data (src); //ステータス異常 sc_count = battle_get_sc_count (src); //ステータス異常の数 option = battle_get_option (src); //鷹とかペコとかカートとか opt1 = battle_get_opt1 (src); //石化、凍結、スタン、睡眠、暗闇 opt2 = battle_get_opt2 (src); //毒、呪い、沈黙、暗闇? if (skill_num != CR_GRANDCROSS) //グランドクロスでないなら sd->state.attack_type = BF_WEAPON; //攻撃タイプは武器攻撃 // ターゲット if (target->type == BL_PC) //対象がPCなら tsd = (struct map_session_data *) target; //tsdに代入(tmdはNULL) else if (target->type == BL_MOB) //対象がMobなら tmd = (struct mob_data *) target; //tmdに代入(tsdはNULL) t_race = battle_get_race (target); //対象の種族 t_ele = battle_get_elem_type (target); //対象の属性 t_size = battle_get_size (target); //対象のサイズ t_mode = battle_get_mode (target); //対象のMode t_sc_data = battle_get_sc_data (target); //対象のステータス異常 //オートカウンター処理ここから if ((skill_num == 0 || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) || (target->type == BL_MOB && battle_config.monster_auto_counter_type & 2)) && skill_lv >= 0) { if (skill_num != CR_GRANDCROSS && t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1) { //グランドクロスでなく、対象がオートカウンター状態の場合 int dir = map_calc_dir (src, target->x, target->y), t_dir = battle_get_dir (target); int dist = distance (src->x, src->y, target->x, target->y); if (dist <= 0 || map_check_dir (dir, t_dir)) { //対象との距離が0以下、または対象の正面? memset (&wd, 0, sizeof (wd)); t_sc_data[SC_AUTOCOUNTER].val3 = 0; t_sc_data[SC_AUTOCOUNTER].val4 = 1; if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) { //自分がオートカウンター状態 int range = battle_get_range (target); if ((target->type == BL_PC && ((struct map_session_data *) target)->status.weapon != 11 && dist <= range + 1) || //対象がPCで武器が弓矢でなく射程内 (target->type == BL_MOB && range <= 3 && dist <= range + 1)) //または対象がMobで射程が3以下で射程内 t_sc_data[SC_AUTOCOUNTER].val3 = src->id; } return wd; //ダメージ構造体を返して終了 } else ac_flag = 1; } } //オートカウンター処理ここまで flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 // 回避率計算、回避判定は後で flee = battle_get_flee (target); if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効 target_count += battle_counttargeted (target, src, battle_config.agi_penaly_count_lv); //対象の数を算出 if (battle_config.agi_penaly_type > 0) { if (target_count >= battle_config.agi_penaly_count) { //ペナルティ設定より対象が多い if (battle_config.agi_penaly_type == 1) //回避率がagi_penaly_num%ずつ減少 flee = (flee * (100 - (target_count - (battle_config.agi_penaly_count - 1)) * battle_config.agi_penaly_num)) / 100; else if (battle_config.agi_penaly_type == 2) //回避率がagi_penaly_num分減少 flee -= (target_count - (battle_config.agi_penaly_count - 1)) * battle_config.agi_penaly_num; if (flee < 1) flee = 1; //回避率は最低でも1 } } hitrate = battle_get_hit (src) - flee + 80; //命中率計算 { // [fate] Reduce hit chance by distance int dx = abs (src->x - target->x); int dy = abs (src->y - target->y); int malus_dist; target_distance = MAX (dx, dy); malus_dist = MAX (0, target_distance - (skill_power (sd, AC_OWL) / 75)); hitrate -= (malus_dist * (malus_dist + 1)); } dex = battle_get_dex (src); //DEX luk = battle_get_luk (src); //LUK watk = battle_get_atk (src); //ATK watk_ = battle_get_atk_ (src); //ATK左手 type = 0; // normal div_ = 1; // single attack if (skill_num == HW_MAGICCRASHER) { /* マジッククラッシャーはMATKで殴る */ damage = damage2 = battle_get_matk1 (src); //damega,damega2初登場、base_atkの取得 } else { damage = damage2 = battle_get_baseatk (&sd->bl); //damega,damega2初登場、base_atkの取得 } if (sd->attackrange > 2) { // [fate] ranged weapon? const int range_damage_bonus = 80; // up to 31.25% bonus for long-range hit damage = damage * (256 + ((range_damage_bonus * target_distance) / sd->attackrange)) >> 8; damage2 = damage2 * (256 + ((range_damage_bonus * target_distance) / sd->attackrange)) >> 8; } atkmin = atkmin_ = dex; //最低ATKはDEXで初期化? sd->state.arrow_atk = 0; //arrow_atk初期化 if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]]) atkmin = atkmin * (80 + sd->inventory_data[sd->equip_index[9]]->wlv * 20) / 100; if (sd->equip_index[8] >= 0 && sd->inventory_data[sd->equip_index[8]]) atkmin_ = atkmin_ * (80 + sd->inventory_data[sd->equip_index[8]]->wlv * 20) / 100; if (sd->status.weapon == 11) { //武器が弓矢の場合 atkmin = watk * ((atkmin < watk) ? atkmin : watk) / 100; //弓用最低ATK計算 flag = (flag & ~BF_RANGEMASK) | BF_LONG; //遠距離攻撃フラグを有効 if (sd->arrow_ele > 0) //属性矢なら属性を矢の属性に変更 s_ele = sd->arrow_ele; sd->state.arrow_atk = 1; //arrow_atk有効化 } // サイズ修正 // ペコ騎乗していて、槍で攻撃した場合は中型のサイズ修正を100にする // ウェポンパーフェクション,ドレイクC if (((sd->special_state.no_sizefix) || (pc_isriding (sd) && (sd->status.weapon == 4 || sd->status.weapon == 5) && t_size == 1) || skill_num == MO_EXTREMITYFIST)) { //ペコ騎乗していて、槍で中型を攻撃 atkmax = watk; atkmax_ = watk_; } else { atkmax = (watk * sd->atkmods[t_size]) / 100; atkmin = (atkmin * sd->atkmods[t_size]) / 100; atkmax_ = (watk_ * sd->atkmods_[t_size]) / 100; atkmin_ = (atkmin_ * sd->atkmods[t_size]) / 100; } if ((sc_data != NULL && sc_data[SC_WEAPONPERFECTION].timer != -1) || (sd->special_state.no_sizefix)) { // ウェポンパーフェクション || ドレイクカード atkmax = watk; atkmax_ = watk_; } if (atkmin > atkmax && !(sd->state.arrow_atk)) atkmin = atkmax; //弓は最低が上回る場合あり if (atkmin_ > atkmax_) atkmin_ = atkmax_; if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) { // マキシマイズパワー atkmin = atkmax; atkmin_ = atkmax_; } //ダブルアタック判定 if (sd->weapontype1 == 0x01) { if (skill_num == 0 && skill_lv >= 0 && (skill = pc_checkskill (sd, TF_DOUBLE)) > 0) da = (MRAND (100) < (skill * 5)) ? 1 : 0; } //三段掌 if (skill_num == 0 && skill_lv >= 0 && (skill = pc_checkskill (sd, MO_TRIPLEATTACK)) > 0 && sd->status.weapon <= 16 && !sd->state.arrow_atk) { da = (MRAND (100) < (30 - skill)) ? 2 : 0; } if (sd->double_rate > 0 && da == 0 && skill_num == 0 && skill_lv >= 0) da = (MRAND (100) < sd->double_rate) ? 1 : 0; // 過剰精錬ボーナス if (sd->overrefine > 0) damage += MPRAND (1, sd->overrefine); if (sd->overrefine_ > 0) damage2 += MPRAND (1, sd->overrefine_); if (da == 0) { //ダブルアタックが発動していない // クリティカル計算 cri = battle_get_critical (src); if (sd->state.arrow_atk) cri += sd->arrow_cri; if (sd->status.weapon == 16) // カタールの場合、クリティカルを倍に cri <<= 1; cri -= battle_get_luk (target) * 3; if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に cri <<= 1; if (ac_flag) cri = 1000; if (skill_num == KN_AUTOCOUNTER) { if (!(battle_config.pc_auto_counter_type & 1)) cri = 1000; else cri <<= 1; } if (skill_num == SN_SHARPSHOOTING && MRAND (100) < 50) cri = 1000; } if (tsd && tsd->critical_def) cri = cri * (100 - tsd->critical_def) / 100; if (da == 0 && (skill_num == 0 || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING) && skill_lv >= 0 && //ダブルアタックが発動していない (MRAND (1000)) < cri) // 判定(スキルの場合は無視) { damage += atkmax; damage2 += atkmax_; if (sd->atk_rate != 100) { damage = (damage * sd->atk_rate) / 100; damage2 = (damage2 * sd->atk_rate) / 100; } if (sd->state.arrow_atk) damage += sd->arrow_atk; type = 0x0a; /* if(def1 < 1000000) { if(sd->def_ratio_atk_ele & (1<def_ratio_atk_race & (1<def_ratio_atk_ele_ & (1<def_ratio_atk_race_ & (1<def_ratio_atk_race & (1<<10)) { damage = (damage * (def1 + def2))/100; idef_flag = 1; } if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<10)) { damage2 = (damage2 * (def1 + def2))/100; idef_flag_ = 1; } } else { if(!idef_flag && sd->def_ratio_atk_race & (1<<11)) { damage = (damage * (def1 + def2))/100; idef_flag = 1; } if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<11)) { damage2 = (damage2 * (def1 + def2))/100; idef_flag_ = 1; } } }*/ } else { int vitbonusmax; if (atkmax > atkmin) damage += atkmin + MRAND ((atkmax - atkmin + 1)); else damage += atkmin; if (atkmax_ > atkmin_) damage2 += atkmin_ + MRAND ((atkmax_ - atkmin_ + 1)); else damage2 += atkmin_; if (sd->atk_rate != 100) { damage = (damage * sd->atk_rate) / 100; damage2 = (damage2 * sd->atk_rate) / 100; } if (sd->state.arrow_atk) { if (sd->arrow_atk > 0) damage += MRAND ((sd->arrow_atk + 1)); hitrate += sd->arrow_hit; } if (skill_num != MO_INVESTIGATE && def1 < 1000000) { if (sd->def_ratio_atk_ele & (1 << t_ele) || sd->def_ratio_atk_race & (1 << t_race)) { damage = (damage * (def1 + def2)) / 100; idef_flag = 1; } if (sd->def_ratio_atk_ele_ & (1 << t_ele) || sd->def_ratio_atk_race_ & (1 << t_race)) { damage2 = (damage2 * (def1 + def2)) / 100; idef_flag_ = 1; } if (t_mode & 0x20) { if (!idef_flag && sd->def_ratio_atk_race & (1 << 10)) { damage = (damage * (def1 + def2)) / 100; idef_flag = 1; } if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 10)) { damage2 = (damage2 * (def1 + def2)) / 100; idef_flag_ = 1; } } else { if (!idef_flag && sd->def_ratio_atk_race & (1 << 11)) { damage = (damage * (def1 + def2)) / 100; idef_flag = 1; } if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 11)) { damage2 = (damage2 * (def1 + def2)) / 100; idef_flag_ = 1; } } } // スキル修正1(攻撃力倍化系) // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 // バッシュ,マグナムブレイク, // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, // メマーナイト,カートレボリューション // ダブルストレイフィング,アローシャワー,チャージアロー, // ソニックブロー if (sc_data) { //状態異常中のダメージ追加 if (sc_data[SC_OVERTHRUST].timer != -1) { // オーバートラスト damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; damage2 += damage2 * (5 * sc_data[SC_OVERTHRUST].val1) / 100; } if (sc_data[SC_TRUESIGHT].timer != -1) { // トゥルーサイト damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; damage2 += damage2 * (2 * sc_data[SC_TRUESIGHT].val1) / 100; } if (sc_data[SC_BERSERK].timer != -1) { // バーサーク damage += damage * 50 / 100; damage2 += damage2 * 50 / 100; } } if (skill_num > 0) { int i; if ((i = skill_get_pl (skill_num)) > 0) s_ele = s_ele_ = i; flag = (flag & ~BF_SKILLMASK) | BF_SKILL; switch (skill_num) { case SM_BASH: // バッシュ damage = damage * (100 + 30 * skill_lv) / 100; damage2 = damage2 * (100 + 30 * skill_lv) / 100; hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; break; case SM_MAGNUM: // マグナムブレイク damage = damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; damage2 = damage2 * (5 * skill_lv + (wflag) ? 65 : 115) / 100; break; case MC_MAMMONITE: // メマーナイト damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; break; case AC_DOUBLE: // ダブルストレイフィング if (!sd->state.arrow_atk && sd->arrow_atk > 0) { int arr = MRAND ((sd->arrow_atk + 1)); damage += arr; damage2 += arr; } damage = damage * (180 + 20 * skill_lv) / 100; damage2 = damage2 * (180 + 20 * skill_lv) / 100; div_ = 2; if (sd->arrow_ele > 0) { s_ele = sd->arrow_ele; s_ele_ = sd->arrow_ele; } flag = (flag & ~BF_RANGEMASK) | BF_LONG; sd->state.arrow_atk = 1; break; case AC_SHOWER: // アローシャワー if (!sd->state.arrow_atk && sd->arrow_atk > 0) { int arr = MRAND ((sd->arrow_atk + 1)); damage += arr; damage2 += arr; } damage = damage * (75 + 5 * skill_lv) / 100; damage2 = damage2 * (75 + 5 * skill_lv) / 100; if (sd->arrow_ele > 0) { s_ele = sd->arrow_ele; s_ele_ = sd->arrow_ele; } flag = (flag & ~BF_RANGEMASK) | BF_LONG; sd->state.arrow_atk = 1; break; case AC_CHARGEARROW: // チャージアロー if (!sd->state.arrow_atk && sd->arrow_atk > 0) { int arr = MRAND ((sd->arrow_atk + 1)); damage += arr; damage2 += arr; } damage = damage * 150 / 100; damage2 = damage2 * 150 / 100; if (sd->arrow_ele > 0) { s_ele = sd->arrow_ele; s_ele_ = sd->arrow_ele; } flag = (flag & ~BF_RANGEMASK) | BF_LONG; sd->state.arrow_atk = 1; break; case KN_PIERCE: // ピアース damage = damage * (100 + 10 * skill_lv) / 100; damage2 = damage2 * (100 + 10 * skill_lv) / 100; hitrate = hitrate * (100 + 5 * skill_lv) / 100; div_ = t_size + 1; damage *= div_; damage2 *= div_; break; case KN_SPEARSTAB: // スピアスタブ damage = damage * (100 + 15 * skill_lv) / 100; damage2 = damage2 * (100 + 15 * skill_lv) / 100; break; case KN_SPEARBOOMERANG: // スピアブーメラン damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case KN_BRANDISHSPEAR: // ブランディッシュスピア damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; if (skill_lv > 3 && wflag == 1) damage3 += damage / 2; if (skill_lv > 6 && wflag == 1) damage3 += damage / 4; if (skill_lv > 9 && wflag == 1) damage3 += damage / 8; if (skill_lv > 6 && wflag == 2) damage3 += damage / 2; if (skill_lv > 9 && wflag == 2) damage3 += damage / 4; if (skill_lv > 9 && wflag == 3) damage3 += damage / 2; damage += damage3; if (skill_lv > 3 && wflag == 1) damage4 += damage2 / 2; if (skill_lv > 6 && wflag == 1) damage4 += damage2 / 4; if (skill_lv > 9 && wflag == 1) damage4 += damage2 / 8; if (skill_lv > 6 && wflag == 2) damage4 += damage2 / 2; if (skill_lv > 9 && wflag == 2) damage4 += damage2 / 4; if (skill_lv > 9 && wflag == 3) damage4 += damage2 / 2; damage2 += damage4; blewcount = 0; break; case KN_BOWLINGBASH: // ボウリングバッシュ damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; blewcount = 0; break; case KN_AUTOCOUNTER: if (battle_config.pc_auto_counter_type & 1) hitrate += 20; else hitrate = 1000000; flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; break; case AS_SONICBLOW: // ソニックブロウ hitrate += 30; // hitrate +30, thanks to midas damage = damage * (300 + 50 * skill_lv) / 100; damage2 = damage2 * (300 + 50 * skill_lv) / 100; div_ = 8; break; case TF_SPRINKLESAND: // 砂まき damage = damage * 125 / 100; damage2 = damage2 * 125 / 100; break; case MC_CARTREVOLUTION: // カートレボリューション if (sd->cart_max_weight > 0 && sd->cart_weight > 0) { damage = (damage * (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + (sd->cart_weight * 100 / sd->cart_max_weight))) / 100; damage2 = (damage2 * (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + (sd->cart_weight * 100 / sd->cart_max_weight))) / 100; } else { damage = (damage * 150) / 100; damage2 = (damage2 * 150) / 100; } break; // 以下MOB case NPC_COMBOATTACK: // 多段攻撃 div_ = skill_get_num (skill_num, skill_lv); damage *= div_; damage2 *= div_; break; case NPC_RANDOMATTACK: // ランダムATK攻撃 damage = damage * (MPRAND (50, 150)) / 100; damage2 = damage2 * (MPRAND (50, 150)) / 100; break; // 属性攻撃(適当) case NPC_WATERATTACK: case NPC_GROUNDATTACK: case NPC_FIREATTACK: case NPC_WINDATTACK: case NPC_POISONATTACK: case NPC_HOLYATTACK: case NPC_DARKNESSATTACK: case NPC_TELEKINESISATTACK: damage = damage * (100 + 25 * skill_lv) / 100; damage2 = damage2 * (100 + 25 * skill_lv) / 100; break; case NPC_GUIDEDATTACK: hitrate = 1000000; break; case NPC_RANGEATTACK: flag = (flag & ~BF_RANGEMASK) | BF_LONG; break; case NPC_PIERCINGATT: flag = (flag & ~BF_RANGEMASK) | BF_SHORT; break; case RG_BACKSTAP: // バックスタブ if (battle_config.backstab_bow_penalty == 1 && sd->status.weapon == 11) { damage = (damage * (300 + 40 * skill_lv) / 100) / 2; damage2 = (damage2 * (300 + 40 * skill_lv) / 100) / 2; } else { damage = damage * (300 + 40 * skill_lv) / 100; damage2 = damage2 * (300 + 40 * skill_lv) / 100; } hitrate = 1000000; break; case RG_RAID: // サプライズアタック damage = damage * (100 + 40 * skill_lv) / 100; damage2 = damage2 * (100 + 40 * skill_lv) / 100; break; case RG_INTIMIDATE: // インティミデイト damage = damage * (100 + 30 * skill_lv) / 100; damage2 = damage2 * (100 + 30 * skill_lv) / 100; break; case CR_SHIELDCHARGE: // シールドチャージ damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_SHORT; s_ele = 0; break; case CR_SHIELDBOOMERANG: // シールドブーメラン damage = damage * (100 + 30 * skill_lv) / 100; damage2 = damage2 * (100 + 30 * skill_lv) / 100; flag = (flag & ~BF_RANGEMASK) | BF_LONG; s_ele = 0; break; case CR_HOLYCROSS: // ホーリークロス damage = damage * (100 + 35 * skill_lv) / 100; damage2 = damage2 * (100 + 35 * skill_lv) / 100; div_ = 2; break; case CR_GRANDCROSS: hitrate = 1000000; break; case AM_DEMONSTRATION: // デモンストレーション damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; break; case AM_ACIDTERROR: // アシッドテラー damage = damage * (100 + 40 * skill_lv) / 100; damage2 = damage2 * (100 + 40 * skill_lv) / 100; break; case MO_FINGEROFFENSIVE: //指弾 if (battle_config.finger_offensive_type == 0) { damage = damage * (100 + 50 * skill_lv) / 100 * sd->spiritball_old; damage2 = damage2 * (100 + 50 * skill_lv) / 100 * sd->spiritball_old; div_ = sd->spiritball_old; } else { damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; div_ = 1; } break; case MO_INVESTIGATE: // 発 勁 if (def1 < 1000000) { damage = damage * (100 + 75 * skill_lv) / 100 * (def1 + def2) / 100; damage2 = damage2 * (100 + 75 * skill_lv) / 100 * (def1 + def2) / 100; } hitrate = 1000000; s_ele = 0; s_ele_ = 0; break; case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 damage = damage * (8 + ((sd->status.sp) / 10)) + 250 + (skill_lv * 150); damage2 = damage2 * (8 + ((sd->status.sp) / 10)) + 250 + (skill_lv * 150); sd->status.sp = 0; clif_updatestatus (sd, SP_SP); hitrate = 1000000; s_ele = 0; s_ele_ = 0; break; case MO_CHAINCOMBO: // 連打掌 damage = damage * (150 + 50 * skill_lv) / 100; damage2 = damage2 * (150 + 50 * skill_lv) / 100; div_ = 4; break; case MO_COMBOFINISH: // 猛龍拳 damage = damage * (240 + 60 * skill_lv) / 100; damage2 = damage2 * (240 + 60 * skill_lv) / 100; break; case BA_MUSICALSTRIKE: // ミュージカルストライク if (!sd->state.arrow_atk && sd->arrow_atk > 0) { int arr = MRAND ((sd->arrow_atk + 1)); damage += arr; damage2 += arr; } damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; if (sd->arrow_ele > 0) { s_ele = sd->arrow_ele; s_ele_ = sd->arrow_ele; } flag = (flag & ~BF_RANGEMASK) | BF_LONG; sd->state.arrow_atk = 1; break; case DC_THROWARROW: // 矢撃ち if (!sd->state.arrow_atk && sd->arrow_atk > 0) { int arr = MRAND ((sd->arrow_atk + 1)); damage += arr; damage2 += arr; } damage = damage * (100 + 50 * skill_lv) / 100; damage2 = damage2 * (100 + 50 * skill_lv) / 100; if (sd->arrow_ele > 0) { s_ele = sd->arrow_ele; s_ele_ = sd->arrow_ele; } flag = (flag & ~BF_RANGEMASK) | BF_LONG; sd->state.arrow_atk = 1; break; case CH_TIGERFIST: // 伏虎拳 damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; break; case CH_CHAINCRUSH: // 連柱崩撃 damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; div_ = skill_get_num (skill_num, skill_lv); break; case CH_PALMSTRIKE: // 猛虎硬派山 damage = damage * (50 + 100 * skill_lv) / 100; damage2 = damage2 * (50 + 100 * skill_lv) / 100; break; case LK_SPIRALPIERCE: /* スパイラルピアース */ damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に damage2 = damage2 * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に div_ = 5; if (tsd) tsd->canmove_tick = gettick () + 1000; else if (tmd) tmd->canmove_tick = gettick () + 1000; break; case LK_HEADCRUSH: /* ヘッドクラッシュ */ damage = damage * (100 + 20 * skill_lv) / 100; damage2 = damage2 * (100 + 20 * skill_lv) / 100; break; case LK_JOINTBEAT: /* ジョイントビート */ damage = damage * (50 + 10 * skill_lv) / 100; damage2 = damage2 * (50 + 10 * skill_lv) / 100; break; case ASC_METEORASSAULT: /* メテオアサルト */ damage = damage * (40 + 40 * skill_lv) / 100; damage2 = damage2 * (40 + 40 * skill_lv) / 100; break; case SN_SHARPSHOOTING: /* シャープシューティング */ damage += damage * (30 * skill_lv) / 100; damage2 += damage2 * (30 * skill_lv) / 100; break; case CG_ARROWVULCAN: /* アローバルカン */ damage = damage * (160 + 40 * skill_lv) / 100; damage2 = damage2 * (160 + 40 * skill_lv) / 100; div_ = 9; break; case AS_SPLASHER: /* ベナムスプラッシャー */ damage = damage * (200 + 20 * skill_lv + 20 * pc_checkskill (sd, AS_POISONREACT)) / 100; damage2 = damage2 * (200 + 20 * skill_lv + 20 * pc_checkskill (sd, AS_POISONREACT)) / 100; break; case PA_SACRIFICE: if (sd) { int hp, mhp, damage3; hp = battle_get_hp (src); mhp = battle_get_max_hp (src); damage3 = mhp * ((skill_lv / 2) + (50 / 100)) / 100; damage = (((skill_lv * 15) + 90) / 100) * damage3 / 100; damage2 = (((skill_lv * 15) + 90) / 100) * damage3 / 100; } break; case ASC_BREAKER: // -- moonsoul (special damage for ASC_BREAKER skill) if (sd) { int damage3; int mdef1 = battle_get_mdef (target); int mdef2 = battle_get_mdef2 (target); int imdef_flag = 0; damage = ((damage * 5) + (skill_lv * battle_get_int (src) * 5) + MRAND (500) + 500) / 2; damage2 = ((damage2 * 5) + (skill_lv * battle_get_int (src) * 5) + MRAND (500) + 500) / 2; damage3 = damage; hitrate = 1000000; if (sd->ignore_mdef_ele & (1 << t_ele) || sd->ignore_mdef_race & (1 << t_race)) imdef_flag = 1; if (t_mode & 0x20) { if (sd->ignore_mdef_race & (1 << 10)) imdef_flag = 1; } else { if (sd->ignore_mdef_race & (1 << 11)) imdef_flag = 1; } if (!imdef_flag) { if (battle_config.magic_defense_type) { damage3 = damage3 - (mdef1 * battle_config.magic_defense_type) - mdef2; } else { damage3 = (damage3 * (100 - mdef1)) / 100 - mdef2; } } if (damage3 < 1) damage3 = 1; damage3 = battle_attr_fix (damage2, s_ele_, battle_get_element (target)); } break; } } if (da == 2) { //三段掌が発動しているか type = 0x08; div_ = 255; //三段掌用に… damage = damage * (100 + 20 * pc_checkskill (sd, MO_TRIPLEATTACK)) / 100; } if (skill_num != NPC_CRITICALSLASH) { // 対 象の防御力によるダメージの減少 // ディバインプロテクション(ここでいいのかな?) if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != KN_AUTOCOUNTER && def1 < 1000000) { //DEF, VIT無視 int t_def; target_count = 1 + battle_counttargeted (target, src, battle_config.vit_penaly_count_lv); if (battle_config.vit_penaly_type > 0) { if (target_count >= battle_config.vit_penaly_count) { if (battle_config.vit_penaly_type == 1) { def1 = (def1 * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; def2 = (def2 * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; t_vit = (t_vit * (100 - (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num)) / 100; } else if (battle_config.vit_penaly_type == 2) { def1 -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; def2 -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; t_vit -= (target_count - (battle_config.vit_penaly_count - 1)) * battle_config.vit_penaly_num; } if (def1 < 0) def1 = 0; if (def2 < 1) def2 = 1; if (t_vit < 1) t_vit = 1; } } t_def = def2 * 8 / 10; vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; if (sd->ignore_def_ele & (1 << t_ele) || sd->ignore_def_race & (1 << t_race)) idef_flag = 1; if (sd->ignore_def_ele_ & (1 << t_ele) || sd->ignore_def_race_ & (1 << t_race)) idef_flag_ = 1; if (t_mode & 0x20) { if (sd->ignore_def_race & (1 << 10)) idef_flag = 1; if (sd->ignore_def_race_ & (1 << 10)) idef_flag_ = 1; } else { if (sd->ignore_def_race & (1 << 11)) idef_flag = 1; if (sd->ignore_def_race_ & (1 << 11)) idef_flag_ = 1; } if (!idef_flag) { if (battle_config.player_defense_type) { damage = damage - (def1 * battle_config.player_defense_type) - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } else { damage = damage * (100 - def1) / 100 - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } } if (!idef_flag_) { if (battle_config.player_defense_type) { damage2 = damage2 - (def1 * battle_config.player_defense_type) - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } else { damage2 = damage2 * (100 - def1) / 100 - t_def - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); } } } } } // 精錬ダメージの追加 if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) { //DEF, VIT無視 damage += battle_get_atk2 (src); damage2 += battle_get_atk_2 (src); } if (skill_num == CR_SHIELDBOOMERANG) { if (sd->equip_index[8] >= 0) { int index = sd->equip_index[8]; if (sd->inventory_data[index] && sd->inventory_data[index]->type == 5) { damage += sd->inventory_data[index]->weight / 10; damage += sd->status.inventory[index].refine * pc_getrefinebonus (0, 1); } } } if (skill_num == LK_SPIRALPIERCE) { /* スパイラルピアース */ if (sd->equip_index[9] >= 0) { //重量で追加ダメージらしいのでシールドブーメランを参考に追加 int index = sd->equip_index[9]; if (sd->inventory_data[index] && sd->inventory_data[index]->type == 4) { damage += (int) (double) (sd->inventory_data[index]->weight * (0.8 * skill_lv * 4 / 10)); damage += sd->status.inventory[index].refine * pc_getrefinebonus (0, 1); } } } // 0未満だった場合1に補正 if (damage < 1) damage = 1; if (damage2 < 1) damage2 = 1; // スキル修正2(修練系) // 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応) if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != CR_GRANDCROSS) { //修練ダメージ無視 damage = battle_addmastery (sd, target, damage, 0); damage2 = battle_addmastery (sd, target, damage2, 1); } if (sd->perfect_hit > 0) { if (MRAND (100) < sd->perfect_hit) hitrate = 1000000; } // 回避修正 hitrate = (hitrate < 5) ? 5 : hitrate; if (hitrate < 1000000 && // 必中攻撃 (t_sc_data != NULL && (t_sc_data[SC_SLEEP].timer != -1 || // 睡眠は必中 t_sc_data[SC_STAN].timer != -1 || // スタンは必中 t_sc_data[SC_FREEZE].timer != -1 || (t_sc_data[SC_STONE].timer != -1 && t_sc_data[SC_STONE].val2 == 0)))) // 凍結は必中 hitrate = 1000000; if (type == 0 && MRAND (100) >= hitrate) { damage = damage2 = 0; dmg_lv = ATK_FLEE; } else { dmg_lv = ATK_DEF; } // スキル修正3(武器研究) if ((skill = pc_checkskill (sd, BS_WEAPONRESEARCH)) > 0) { damage += skill * 2; damage2 += skill * 2; } //Advanced Katar Research by zanetheinsane if (sd->weapontype1 == 0x10 || sd->weapontype2 == 0x10) { if ((skill = pc_checkskill (sd, ASC_KATAR)) > 0) { damage += (damage * ((skill * 2) + 10)) / 100; } } //スキルによるダメージ補正ここまで //カードによるダメージ追加処理ここから cardfix = 100; if (!sd->state.arrow_atk) { //弓矢以外 if (!battle_config.left_cardfix_to_right) { //左手カード補正設定無し cardfix = cardfix * (100 + sd->addrace[t_race]) / 100; // 種族によるダメージ修正 cardfix = cardfix * (100 + sd->addele[t_ele]) / 100; // 属性によるダメージ修正 cardfix = cardfix * (100 + sd->addsize[t_size]) / 100; // サイズによるダメージ修正 } else { cardfix = cardfix * (100 + sd->addrace[t_race] + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正(左手による追加あり) cardfix = cardfix * (100 + sd->addele[t_ele] + sd->addele_[t_ele]) / 100; // 属性によるダメージ修正(左手による追加あり) cardfix = cardfix * (100 + sd->addsize[t_size] + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正(左手による追加あり) } } else { //弓矢 cardfix = cardfix * (100 + sd->addrace[t_race] + sd->arrow_addrace[t_race]) / 100; // 種族によるダメージ修正(弓矢による追加あり) cardfix = cardfix * (100 + sd->addele[t_ele] + sd->arrow_addele[t_ele]) / 100; // 属性によるダメージ修正(弓矢による追加あり) cardfix = cardfix * (100 + sd->addsize[t_size] + sd->arrow_addsize[t_size]) / 100; // サイズによるダメージ修正(弓矢による追加あり) } if (t_mode & 0x20) { //ボス if (!sd->state.arrow_atk) { //弓矢攻撃以外なら if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し cardfix = cardfix * (100 + sd->addrace[10]) / 100; //ボスモンスターに追加ダメージ else //左手カード補正設定あり cardfix = cardfix * (100 + sd->addrace[10] + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ(左手による追加あり) } else //弓矢攻撃 cardfix = cardfix * (100 + sd->addrace[10] + sd->arrow_addrace[10]) / 100; //ボスモンスターに追加ダメージ(弓矢による追加あり) } else { //ボスじゃない if (!sd->state.arrow_atk) { //弓矢攻撃以外 if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し cardfix = cardfix * (100 + sd->addrace[11]) / 100; //ボス以外モンスターに追加ダメージ else //左手カード補正設定あり cardfix = cardfix * (100 + sd->addrace[11] + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ(左手による追加あり) } else cardfix = cardfix * (100 + sd->addrace[11] + sd->arrow_addrace[11]) / 100; //ボス以外モンスターに追加ダメージ(弓矢による追加あり) } //特定Class用補正処理(少女の日記→ボンゴン用?) t_class = battle_get_class (target); for (i = 0; i < sd->add_damage_class_count; i++) { if (sd->add_damage_classid[i] == t_class) { cardfix = cardfix * (100 + sd->add_damage_classrate[i]) / 100; break; } } if (skill_num != CR_GRANDCROSS || !battle_config.gx_cardfix) damage = damage * cardfix / 100; //カード補正によるダメージ増加 //カードによるダメージ増加処理ここまで //カードによるダメージ追加処理(左手)ここから cardfix = 100; if (!battle_config.left_cardfix_to_right) { //左手カード補正設定無し cardfix = cardfix * (100 + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正左手 cardfix = cardfix * (100 + sd->addele_[t_ele]) / 100; // 属 性によるダメージ修正左手 cardfix = cardfix * (100 + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正左手 if (t_mode & 0x20) //ボス cardfix = cardfix * (100 + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ左手 else cardfix = cardfix * (100 + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ左手 } //特定Class用補正処理左手(少女の日記→ボンゴン用?) for (i = 0; i < sd->add_damage_class_count_; i++) { if (sd->add_damage_classid_[i] == t_class) { cardfix = cardfix * (100 + sd->add_damage_classrate_[i]) / 100; break; } } if (skill_num != CR_GRANDCROSS) damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ増加 //カードによるダメージ増加処理(左手)ここまで // -- moonsoul (cardfix for magic damage portion of ASC_BREAKER) if (skill_num == ASC_BREAKER) damage3 = damage3 * cardfix / 100; //カードによるダメージ減衰処理ここから if (tsd) { //対象がPCの場合 cardfix = 100; cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属性によるダメージ耐性 if (battle_get_mode (src) & 0x20) cardfix = cardfix * (100 - tsd->subrace[10]) / 100; //ボスからの攻撃はダメージ減少 else cardfix = cardfix * (100 - tsd->subrace[11]) / 100; //ボス以外からの攻撃はダメージ減少 //特定Class用補正処理左手(少女の日記→ボンゴン用?) for (i = 0; i < tsd->add_def_class_count; i++) { if (tsd->add_def_classid[i] == sd->status.pc_class) { cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; break; } } if (flag & BF_LONG) cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; //遠距離攻撃はダメージ減少(ホルンCとか) if (flag & BF_SHORT) cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; //近距離攻撃はダメージ減少(該当無し?) damage = damage * cardfix / 100; //カード補正によるダメージ減少 damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ減少 } //カードによるダメージ減衰処理ここまで //対象にステータス異常がある場合のダメージ減算処理ここから if (t_sc_data) { cardfix = 100; if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) //ディフェンダー状態で遠距離攻撃 cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; //ディフェンダーによる減衰 if (cardfix != 100) { damage = damage * cardfix / 100; //ディフェンダー補正によるダメージ減少 damage2 = damage2 * cardfix / 100; //ディフェンダー補正による左手ダメージ減少 } if (t_sc_data[SC_ASSUMPTIO].timer != -1) { //アスムプティオ if (!map[target->m].flag.pvp) { damage = damage / 3; damage2 = damage2 / 3; } else { damage = damage / 2; damage2 = damage2 / 2; } } } //対象にステータス異常がある場合のダメージ減算処理ここまで if (damage < 0) damage = 0; if (damage2 < 0) damage2 = 0; // 属 性の適用 damage = battle_attr_fix (damage, s_ele, battle_get_element (target)); damage2 = battle_attr_fix (damage2, s_ele_, battle_get_element (target)); // 星のかけら、気球の適用 damage += sd->star; damage2 += sd->star_; damage += sd->spiritball * 3; damage2 += sd->spiritball * 3; if (sc_data && sc_data[SC_AURABLADE].timer != -1) { /* オーラブレード 必中 */ damage += sc_data[SC_AURABLADE].val1 * 10; damage2 += sc_data[SC_AURABLADE].val1 * 10; } if (skill_num == PA_PRESSURE) { /* プレッシャー 必中? */ damage = 700 + 100 * skill_lv; damage2 = 700 + 100 * skill_lv; } // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ! // >map_session_data に左手ダメージ(atk,atk2)追加して // >pc_calcstatus()でやるべきかな? // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して // pc_calcstatus()でデータを入力しています //左手のみ武器装備 if (sd->weapontype1 == 0 && sd->weapontype2 > 0) { damage = damage2; damage2 = 0; } // 右手、左手修練の適用 if (sd->status.weapon > 16) { // 二刀流か? int dmg = damage, dmg2 = damage2; // 右手修練(60% 〜 100%) 右手全般 skill = pc_checkskill (sd, AS_RIGHT); damage = damage * (50 + (skill * 10)) / 100; if (dmg > 0 && damage < 1) damage = 1; // 左手修練(40% 〜 80%) 左手全般 skill = pc_checkskill (sd, AS_LEFT); damage2 = damage2 * (30 + (skill * 10)) / 100; if (dmg2 > 0 && damage2 < 1) damage2 = 1; } else //二刀流でなければ左手ダメージは0 damage2 = 0; // 右手,短剣のみ if (da == 1) { //ダブルアタックが発動しているか div_ = 2; damage += damage; type = 0x08; } if (sd->status.weapon == 16) { // カタール追撃ダメージ skill = pc_checkskill (sd, TF_DOUBLE); damage2 = damage * (1 + (skill * 2)) / 100; if (damage > 0 && damage2 < 1) damage2 = 1; } // インベナム修正 if (skill_num == TF_POISON) { damage = battle_attr_fix (damage + 15 * skill_lv, s_ele, battle_get_element (target)); } if (skill_num == MC_CARTREVOLUTION) { damage = battle_attr_fix (damage, 0, battle_get_element (target)); } // 完全回避の判定 if (skill_num == 0 && skill_lv >= 0 && tsd != NULL && div_ < 255 && MRAND (1000) < battle_get_flee2 (target)) { damage = damage2 = 0; type = 0x0b; dmg_lv = ATK_LUCKY; } // 対象が完全回避をする設定がONなら if (battle_config.enemy_perfect_flee) { if (skill_num == 0 && skill_lv >= 0 && tmd != NULL && div_ < 255 && MRAND (1000) < battle_get_flee2 (target)) { damage = damage2 = 0; type = 0x0b; dmg_lv = ATK_LUCKY; } } //MobのModeに頑強フラグが立っているときの処理 if (t_mode & 0x40) { if (damage > 0) damage = 1; if (damage2 > 0) damage2 = 1; } //bNoWeaponDamage(設定アイテム無し?)でグランドクロスじゃない場合はダメージが0 if (tsd && tsd->special_state.no_weapon_damage && skill_num != CR_GRANDCROSS) damage = damage2 = 0; if (skill_num != CR_GRANDCROSS && (damage > 0 || damage2 > 0)) { if (damage2 < 1) // ダメージ最終修正 damage = battle_calc_damage (src, target, damage, div_, skill_num, skill_lv, flag); else if (damage < 1) // 右手がミス? damage2 = battle_calc_damage (src, target, damage2, div_, skill_num, skill_lv, flag); else { // 両 手/カタールの場合はちょっと計算ややこしい int d1 = damage + damage2, d2 = damage2; damage = battle_calc_damage (src, target, damage + damage2, div_, skill_num, skill_lv, flag); damage2 = (d2 * 100 / d1) * damage / 100; if (damage > 1 && damage2 < 1) damage2 = 1; damage -= damage2; } } /* For executioner card [Valaris] */ if (src->type == BL_PC && sd->random_attack_increase_add > 0 && sd->random_attack_increase_per > 0 && skill_num == 0) { if (MRAND (100) < sd->random_attack_increase_per) { if (damage > 0) damage *= sd->random_attack_increase_add / 100; if (damage2 > 0) damage2 *= sd->random_attack_increase_add / 100; } } /* End addition */ // -- moonsoul (final combination of phys, mag damage for ASC_BREAKER) if (skill_num == ASC_BREAKER) { damage += damage3; damage2 += damage3; } wd.damage = damage; wd.damage2 = damage2; wd.type = type; wd.div_ = div_; wd.amotion = battle_get_amotion (src); if (skill_num == KN_AUTOCOUNTER) wd.amotion >>= 1; wd.dmotion = battle_get_dmotion (target); wd.blewcount = blewcount; wd.flag = flag; wd.dmg_lv = dmg_lv; return wd; } /*========================================== * 武器ダメージ計算 *------------------------------------------ */ struct Damage battle_calc_weapon_attack (struct block_list *src, struct block_list *target, int skill_num, int skill_lv, int wflag) { struct Damage wd; //return前の処理があるので情報出力部のみ変更 if (src == NULL || target == NULL) { nullpo_info (NLP_MARK); memset (&wd, 0, sizeof (wd)); return wd; } else if (src->type == BL_PC) wd = battle_calc_pc_weapon_attack (src, target, skill_num, skill_lv, wflag); // weapon breaking [Valaris] else if (src->type == BL_MOB) wd = battle_calc_mob_weapon_attack (src, target, skill_num, skill_lv, wflag); else memset (&wd, 0, sizeof (wd)); if (battle_config.equipment_breaking && src->type == BL_PC && (wd.damage > 0 || wd.damage2 > 0)) { struct map_session_data *sd = (struct map_session_data *) src; if (sd->status.weapon && sd->status.weapon != 11) { int breakrate = 1; if (target->type == BL_PC && sd->sc_data[SC_MELTDOWN].timer != -1) { breakrate += 100 * sd->sc_data[SC_MELTDOWN].val1; if (MRAND (10000) < breakrate * battle_config.equipment_break_rate / 100 || breakrate >= 10000) pc_breakweapon ((struct map_session_data *) target); } if (sd->sc_data[SC_OVERTHRUST].timer != -1) breakrate += 20 * sd->sc_data[SC_OVERTHRUST].val1; if (wd.type == 0x0a) breakrate *= 2; if (MRAND (10000) < breakrate * battle_config.equipment_break_rate / 100 || breakrate >= 10000) { pc_breakweapon (sd); memset (&wd, 0, sizeof (wd)); } } } if (battle_config.equipment_breaking && target->type == BL_PC && (wd.damage > 0 || wd.damage2 > 0)) { int breakrate = 1; if (src->type == BL_PC && ((struct map_session_data *) src)-> sc_data[SC_MELTDOWN].timer != -1) breakrate += 70 * ((struct map_session_data *) src)->sc_data[SC_MELTDOWN].val1; if (wd.type == 0x0a) breakrate *= 2; if (MRAND (10000) < breakrate * battle_config.equipment_break_rate / 100 || breakrate >= 10000) { pc_breakarmor ((struct map_session_data *) target); } } return wd; } /*========================================== * 魔法ダメージ計算 *------------------------------------------ */ struct Damage battle_calc_magic_attack (struct block_list *bl, struct block_list *target, int skill_num, int skill_lv, int flag) { int mdef1 = battle_get_mdef (target); int mdef2 = battle_get_mdef2 (target); int matk1, matk2, damage = 0, div_ = 1, blewcount = skill_get_blewcount (skill_num, skill_lv), rdamage = 0; struct Damage md; int aflag; int normalmagic_flag = 1; int ele = 0, race = 7, t_ele = 0, t_race = 7, t_mode = 0, cardfix, t_class, i; struct map_session_data *sd = NULL, *tsd = NULL; struct mob_data *tmd = NULL; //return前の処理があるので情報出力部のみ変更 if (bl == NULL || target == NULL) { nullpo_info (NLP_MARK); memset (&md, 0, sizeof (md)); return md; } matk1 = battle_get_matk1 (bl); matk2 = battle_get_matk2 (bl); ele = skill_get_pl (skill_num); race = battle_get_race (bl); t_ele = battle_get_elem_type (target); t_race = battle_get_race (target); t_mode = battle_get_mode (target); #define MATK_FIX( a,b ) { matk1=matk1*(a)/(b); matk2=matk2*(a)/(b); } if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) { sd->state.attack_type = BF_MAGIC; if (sd->matk_rate != 100) MATK_FIX (sd->matk_rate, 100); sd->state.arrow_atk = 0; } if (target->type == BL_PC) tsd = (struct map_session_data *) target; else if (target->type == BL_MOB) tmd = (struct mob_data *) target; aflag = BF_MAGIC | BF_LONG | BF_SKILL; if (skill_num > 0) { switch (skill_num) { // 基本ダメージ計算(スキルごとに処理) // ヒールor聖体 case AL_HEAL: case PR_BENEDICTIO: damage = skill_calc_heal (bl, skill_lv) / 2; normalmagic_flag = 0; break; case PR_ASPERSIO: /* アスペルシオ */ damage = 40; //固定ダメージ normalmagic_flag = 0; break; case PR_SANCTUARY: // サンクチュアリ damage = (skill_lv > 6) ? 388 : skill_lv * 50; normalmagic_flag = 0; blewcount |= 0x10000; break; case ALL_RESURRECTION: case PR_TURNUNDEAD: // 攻撃リザレクションとターンアンデッド if (target->type != BL_PC && battle_check_undead (t_race, t_ele)) { int hp, mhp, thres; hp = battle_get_hp (target); mhp = battle_get_max_hp (target); thres = (skill_lv * 20) + battle_get_luk (bl) + battle_get_int (bl) + battle_get_lv (bl) + ((200 - hp * 200 / mhp)); if (thres > 700) thres = 700; // if(battle_config.battle_log) // printf("ターンアンデッド! 確率%d ‰(千分率)\n", thres); if (MRAND (1000) < thres && !(t_mode & 0x20)) // 成功 damage = hp; else // 失敗 damage = battle_get_lv (bl) + battle_get_int (bl) + skill_lv * 10; } normalmagic_flag = 0; break; case MG_NAPALMBEAT: // ナパームビート(分散計算込み) MATK_FIX (70 + skill_lv * 10, 100); if (flag > 0) { MATK_FIX (1, flag); } else { if (battle_config.error_log) printf ("battle_calc_magic_attack(): napam enemy count=0 !\n"); } break; case MG_FIREBALL: // ファイヤーボール { const int drate[] = { 100, 90, 70 }; if (flag > 2) matk1 = matk2 = 0; else MATK_FIX ((95 + skill_lv * 5) * drate[flag], 10000); } break; case MG_FIREWALL: // ファイヤーウォール /* if( (t_ele!=3 && !battle_check_undead(t_race,t_ele)) || target->type==BL_PC ) //PCは火属性でも飛ぶ?そもそもダメージ受ける? blewcount |= 0x10000; else blewcount = 0; */ if ((t_ele == 3 || battle_check_undead (t_race, t_ele)) && target->type != BL_PC) blewcount = 0; else blewcount |= 0x10000; MATK_FIX (1, 2); break; case MG_THUNDERSTORM: // サンダーストーム MATK_FIX (80, 100); break; case MG_FROSTDIVER: // フロストダイバ MATK_FIX (100 + skill_lv * 10, 100); break; case WZ_FROSTNOVA: // フロストダイバ MATK_FIX (((100 + skill_lv * 10) * (2 / 3)), 100); break; case WZ_FIREPILLAR: // ファイヤーピラー if (mdef1 < 1000000) mdef1 = mdef2 = 0; // MDEF無視 MATK_FIX (1, 5); matk1 += 50; matk2 += 50; break; case WZ_SIGHTRASHER: MATK_FIX (100 + skill_lv * 20, 100); break; case WZ_METEOR: case WZ_JUPITEL: // ユピテルサンダー break; case WZ_VERMILION: // ロードオブバーミリオン MATK_FIX (skill_lv * 20 + 80, 100); break; case WZ_WATERBALL: // ウォーターボール matk1 += skill_lv * 30; matk2 += skill_lv * 30; break; case WZ_STORMGUST: // ストームガスト MATK_FIX (skill_lv * 40 + 100, 100); blewcount |= 0x10000; break; case AL_HOLYLIGHT: // ホーリーライト MATK_FIX (125, 100); break; case AL_RUWACH: MATK_FIX (145, 100); break; case HW_NAPALMVULCAN: // ナパームビート(分散計算込み) MATK_FIX (70 + skill_lv * 10, 100); if (flag > 0) { MATK_FIX (1, flag); } else { if (battle_config.error_log) printf ("battle_calc_magic_attack(): napalmvulcan enemy count=0 !\n"); } break; } } if (normalmagic_flag) { // 一般魔法ダメージ計算 int imdef_flag = 0; if (matk1 > matk2) damage = matk2 + MRAND ((matk1 - matk2 + 1)); else damage = matk2; if (sd) { if (sd->ignore_mdef_ele & (1 << t_ele) || sd->ignore_mdef_race & (1 << t_race)) imdef_flag = 1; if (t_mode & 0x20) { if (sd->ignore_mdef_race & (1 << 10)) imdef_flag = 1; } else { if (sd->ignore_mdef_race & (1 << 11)) imdef_flag = 1; } } if (!imdef_flag) { if (battle_config.magic_defense_type) { damage = damage - (mdef1 * battle_config.magic_defense_type) - mdef2; } else { damage = (damage * (100 - mdef1)) / 100 - mdef2; } } if (damage < 1) damage = 1; } if (sd) { cardfix = 100; cardfix = cardfix * (100 + sd->magic_addrace[t_race]) / 100; cardfix = cardfix * (100 + sd->magic_addele[t_ele]) / 100; if (t_mode & 0x20) cardfix = cardfix * (100 + sd->magic_addrace[10]) / 100; else cardfix = cardfix * (100 + sd->magic_addrace[11]) / 100; t_class = battle_get_class (target); for (i = 0; i < sd->add_magic_damage_class_count; i++) { if (sd->add_magic_damage_classid[i] == t_class) { cardfix = cardfix * (100 + sd->add_magic_damage_classrate[i]) / 100; break; } } damage = damage * cardfix / 100; } if (tsd) { int s_class = battle_get_class (bl); cardfix = 100; cardfix = cardfix * (100 - tsd->subele[ele]) / 100; // 属 性によるダメージ耐性 cardfix = cardfix * (100 - tsd->subrace[race]) / 100; // 種族によるダメージ耐性 cardfix = cardfix * (100 - tsd->magic_subrace[race]) / 100; if (battle_get_mode (bl) & 0x20) cardfix = cardfix * (100 - tsd->magic_subrace[10]) / 100; else cardfix = cardfix * (100 - tsd->magic_subrace[11]) / 100; for (i = 0; i < tsd->add_mdef_class_count; i++) { if (tsd->add_mdef_classid[i] == s_class) { cardfix = cardfix * (100 - tsd->add_mdef_classrate[i]) / 100; break; } } cardfix = cardfix * (100 - tsd->magic_def_rate) / 100; damage = damage * cardfix / 100; } if (damage < 0) damage = 0; damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属 性修正 if (skill_num == CR_GRANDCROSS) { // グランドクロス struct Damage wd; wd = battle_calc_weapon_attack (bl, target, skill_num, skill_lv, flag); damage = (damage + wd.damage) * (100 + 40 * skill_lv) / 100; if (battle_config.gx_dupele) damage = battle_attr_fix (damage, ele, battle_get_element (target)); //属性2回かかる if (bl == target) damage = damage / 2; //反動は半分 } div_ = skill_get_num (skill_num, skill_lv); if (div_ > 1 && skill_num != WZ_VERMILION) damage *= div_; // if(mdef1 >= 1000000 && damage > 0) if (t_mode & 0x40 && damage > 0) damage = 1; if (tsd && tsd->special_state.no_magic_damage) { if (battle_config.gtb_pvp_only != 0) { // [MouseJstr] if ((map[target->m].flag.pvp || map[target->m].flag.gvg) && target->type == BL_PC) damage = (damage * (100 - battle_config.gtb_pvp_only)) / 100; } else damage = 0; // 黄 金蟲カード(魔法ダメージ0) } damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 /* magic_damage_return by [AppleGirl] and [Valaris] */ if (target->type == BL_PC && tsd && tsd->magic_damage_return > 0) { rdamage += damage * tsd->magic_damage_return / 100; if (rdamage < 1) rdamage = 1; clif_damage (target, bl, gettick (), 0, 0, rdamage, 0, 0, 0); battle_damage (target, bl, rdamage, 0); } /* end magic_damage_return */ md.damage = damage; md.div_ = div_; md.amotion = battle_get_amotion (bl); md.dmotion = battle_get_dmotion (target); md.damage2 = 0; md.type = 0; md.blewcount = blewcount; md.flag = aflag; return md; } /*========================================== * その他ダメージ計算 *------------------------------------------ */ struct Damage battle_calc_misc_attack (struct block_list *bl, struct block_list *target, int skill_num, int skill_lv, int flag) { int int_ = battle_get_int (bl); // int luk=battle_get_luk(bl); int dex = battle_get_dex (bl); int skill, ele, race, cardfix; struct map_session_data *sd = NULL, *tsd = NULL; int damage = 0, div_ = 1, blewcount = skill_get_blewcount (skill_num, skill_lv); struct Damage md; int damagefix = 1; int aflag = BF_MISC | BF_LONG | BF_SKILL; //return前の処理があるので情報出力部のみ変更 if (bl == NULL || target == NULL) { nullpo_info (NLP_MARK); memset (&md, 0, sizeof (md)); return md; } if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) { sd->state.attack_type = BF_MISC; sd->state.arrow_atk = 0; } if (target->type == BL_PC) tsd = (struct map_session_data *) target; switch (skill_num) { case HT_LANDMINE: // ランドマイン damage = skill_lv * (dex + 75) * (100 + int_) / 100; break; case HT_BLASTMINE: // ブラストマイン damage = skill_lv * (dex / 2 + 50) * (100 + int_) / 100; break; case HT_CLAYMORETRAP: // クレイモアートラップ damage = skill_lv * (dex / 2 + 75) * (100 + int_) / 100; break; case HT_BLITZBEAT: // ブリッツビート if (sd == NULL || (skill = pc_checkskill (sd, HT_STEELCROW)) <= 0) skill = 0; damage = (dex / 10 + int_ / 2 + skill * 3 + 40) * 2; if (flag > 1) damage /= flag; break; case TF_THROWSTONE: // 石投げ damage = 30; damagefix = 0; break; case BA_DISSONANCE: // 不協和音 damage = (skill_lv) * 20 + pc_checkskill (sd, BA_MUSICALLESSON) * 3; break; case NPC_SELFDESTRUCTION: // 自爆 damage = battle_get_hp (bl) - (bl == target ? 1 : 0); damagefix = 0; break; case NPC_SMOKING: // タバコを吸う damage = 3; damagefix = 0; break; case NPC_DARKBREATH: { struct status_change *sc_data = battle_get_sc_data (target); int hitrate = battle_get_hit (bl) - battle_get_flee (target) + 80; hitrate = ((hitrate > 95) ? 95 : ((hitrate < 5) ? 5 : hitrate)); if (sc_data && (sc_data[SC_SLEEP].timer != -1 || sc_data[SC_STAN].timer != -1 || sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0))) hitrate = 1000000; if (MRAND (100) < hitrate) { damage = 500 + (skill_lv - 1) * 1000 + MRAND (1000); if (damage > 9999) damage = 9999; } } break; case SN_FALCONASSAULT: /* ファルコンアサルト */ skill = pc_checkskill (sd, HT_BLITZBEAT); damage = (100 + 50 * skill_lv + (dex / 10 + int_ / 2 + skill * 3 + 40) * 2); break; } ele = skill_get_pl (skill_num); race = battle_get_race (bl); if (damagefix) { if (damage < 1 && skill_num != NPC_DARKBREATH) damage = 1; if (tsd) { cardfix = 100; cardfix = cardfix * (100 - tsd->subele[ele]) / 100; // 属性によるダメージ耐性 cardfix = cardfix * (100 - tsd->subrace[race]) / 100; // 種族によるダメージ耐性 cardfix = cardfix * (100 - tsd->misc_def_rate) / 100; damage = damage * cardfix / 100; } if (damage < 0) damage = 0; damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属性修正 } div_ = skill_get_num (skill_num, skill_lv); if (div_ > 1) damage *= div_; if (damage > 0 && (damage < div_ || (battle_get_def (target) >= 1000000 && battle_get_mdef (target) >= 1000000))) { damage = div_; } damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 md.damage = damage; md.div_ = div_; md.amotion = battle_get_amotion (bl); md.dmotion = battle_get_dmotion (target); md.damage2 = 0; md.type = 0; md.blewcount = blewcount; md.flag = aflag; return md; } /*========================================== * ダメージ計算一括処理用 *------------------------------------------ */ struct Damage battle_calc_attack (int attack_type, struct block_list *bl, struct block_list *target, int skill_num, int skill_lv, int flag) { struct Damage d; memset (&d, 0, sizeof (d)); switch (attack_type) { case BF_WEAPON: return battle_calc_weapon_attack (bl, target, skill_num, skill_lv, flag); case BF_MAGIC: return battle_calc_magic_attack (bl, target, skill_num, skill_lv, flag); case BF_MISC: return battle_calc_misc_attack (bl, target, skill_num, skill_lv, flag); default: if (battle_config.error_log) printf ("battle_calc_attack: unknwon attack type ! %d\n", attack_type); break; } return d; } /*========================================== * 通常攻撃処理まとめ *------------------------------------------ */ int battle_weapon_attack (struct block_list *src, struct block_list *target, unsigned int tick, int flag) { struct map_session_data *sd = NULL; struct status_change *sc_data = battle_get_sc_data (src), *t_sc_data = battle_get_sc_data (target); short *opt1; int race = 7, ele = 0; int damage, rdamage = 0; struct Damage wd; nullpo_retr (0, src); nullpo_retr (0, target); if (src->type == BL_PC) sd = (struct map_session_data *) src; if (src->prev == NULL || target->prev == NULL) return 0; if (src->type == BL_PC && pc_isdead (sd)) return 0; if (target->type == BL_PC && pc_isdead ((struct map_session_data *) target)) return 0; opt1 = battle_get_opt1 (src); if (opt1 && *opt1 > 0) { battle_stopattack (src); return 0; } if (sc_data && sc_data[SC_BLADESTOP].timer != -1) { battle_stopattack (src); return 0; } race = battle_get_race (target); ele = battle_get_elem_type (target); if (battle_check_target (src, target, BCT_ENEMY) > 0 && battle_check_range (src, target, 0)) { // 攻撃対象となりうるので攻撃 if (sd && sd->status.weapon == 11) { if (sd->equip_index[10] >= 0) { if (battle_config.arrow_decrement) pc_delitem (sd, sd->equip_index[10], 1, 0); } else { clif_arrow_fail (sd, 0); return 0; } } if (flag & 0x8000) { if (sd && battle_config.pc_attack_direction_change) sd->dir = sd->head_dir = map_calc_dir (src, target->x, target->y); else if (src->type == BL_MOB && battle_config.monster_attack_direction_change) ((struct mob_data *) src)->dir = map_calc_dir (src, target->x, target->y); wd = battle_calc_weapon_attack (src, target, KN_AUTOCOUNTER, flag & 0xff, 0); } else wd = battle_calc_weapon_attack (src, target, 0, 0, 0); // significantly increase injuries for hasted characters if (wd.damage > 0 && (t_sc_data[SC_HASTE].timer != -1)) { wd.damage = (wd.damage * (16 + t_sc_data[SC_HASTE].val1)) >> 4; } if (wd.damage > 0 && t_sc_data[SC_PHYS_SHIELD].timer != -1 && target->type == BL_PC) { int reduction = t_sc_data[SC_PHYS_SHIELD].val1; if (reduction > wd.damage) reduction = wd.damage; wd.damage -= reduction; MAP_LOG_PC (((struct map_session_data *) target), "MAGIC-ABSORB-DMG %d", reduction); } if ((damage = wd.damage + wd.damage2) > 0 && src != target) { if (wd.flag & BF_SHORT) { if (target->type == BL_PC) { struct map_session_data *tsd = (struct map_session_data *) target; if (tsd && tsd->short_weapon_damage_return > 0) { rdamage += damage * tsd->short_weapon_damage_return / 100; if (rdamage < 1) rdamage = 1; } } if (t_sc_data && t_sc_data[SC_REFLECTSHIELD].timer != -1) { rdamage += damage * t_sc_data[SC_REFLECTSHIELD].val2 / 100; if (rdamage < 1) rdamage = 1; } } else if (wd.flag & BF_LONG) { if (target->type == BL_PC) { struct map_session_data *tsd = (struct map_session_data *) target; if (tsd && tsd->long_weapon_damage_return > 0) { rdamage += damage * tsd->long_weapon_damage_return / 100; if (rdamage < 1) rdamage = 1; } } } if (rdamage > 0) clif_damage (src, src, tick, wd.amotion, 0, rdamage, 1, 4, 0); } if (wd.div_ == 255 && sd) { //三段掌 int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); int skilllv; if (wd.damage + wd.damage2 < battle_get_hp (target)) { if ((skilllv = pc_checkskill (sd, MO_CHAINCOMBO)) > 0) delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 skill_status_change_start (src, SC_COMBO, MO_TRIPLEATTACK, skilllv, 0, 0, delay, 0); } sd->attackabletime = sd->canmove_tick = tick + delay; clif_combo_delay (src, delay); clif_skill_damage (src, target, tick, wd.amotion, wd.dmotion, wd.damage, 3, MO_TRIPLEATTACK, pc_checkskill (sd, MO_TRIPLEATTACK), -1); } else { clif_damage (src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_, wd.type, wd.damage2); //二刀流左手とカタール追撃のミス表示(無理やり〜) if (sd && sd->status.weapon >= 16 && wd.damage2 == 0) clif_damage (src, target, tick + 10, wd.amotion, wd.dmotion, 0, 1, 0, 0); } if (sd && sd->splash_range > 0 && (wd.damage > 0 || wd.damage2 > 0)) skill_castend_damage_id (src, target, 0, -1, tick, 0); map_freeblock_lock (); if (src->type == BL_PC) { int weapon_index = sd->equip_index[9]; int weapon = 0; if (sd->inventory_data[weapon_index] && sd->status.inventory[weapon_index].equip & 0x2) weapon = sd->inventory_data[weapon_index]->nameid; MAP_LOG ("PC%d %d:%d,%d WPNDMG %s%d %d FOR %d WPN %d", sd->status.char_id, src->m, src->x, src->y, (target->type == BL_PC) ? "PC" : "MOB", (target->type == BL_PC) ? ((struct map_session_data *) target)-> status.char_id : target->id, (target->type == BL_PC) ? 0 : ((struct mob_data *) target)->mob_class, wd.damage + wd.damage2, weapon); } if (target->type == BL_PC) { struct map_session_data *sd2 = (struct map_session_data *) target; MAP_LOG ("PC%d %d:%d,%d WPNINJURY %s%d %d FOR %d", sd2->status.char_id, target->m, target->x, target->y, (src->type == BL_PC) ? "PC" : "MOB", (src->type == BL_PC) ? ((struct map_session_data *) src)-> status.char_id : src->id, (src->type == BL_PC) ? 0 : ((struct mob_data *) src)->mob_class, wd.damage + wd.damage2); } battle_damage (src, target, (wd.damage + wd.damage2), 0); if (target->prev != NULL && (target->type != BL_PC || (target->type == BL_PC && !pc_isdead ((struct map_session_data *) target)))) { if (wd.damage > 0 || wd.damage2 > 0) { skill_additional_effect (src, target, 0, 0, BF_WEAPON, tick); if (sd) { if (sd->weapon_coma_ele[ele] > 0 && MRAND (10000) < sd->weapon_coma_ele[ele]) battle_damage (src, target, battle_get_max_hp (target), 1); if (sd->weapon_coma_race[race] > 0 && MRAND (10000) < sd->weapon_coma_race[race]) battle_damage (src, target, battle_get_max_hp (target), 1); if (battle_get_mode (target) & 0x20) { if (sd->weapon_coma_race[10] > 0 && MRAND (10000) < sd->weapon_coma_race[10]) battle_damage (src, target, battle_get_max_hp (target), 1); } else { if (sd->weapon_coma_race[11] > 0 && MRAND (10000) < sd->weapon_coma_race[11]) battle_damage (src, target, battle_get_max_hp (target), 1); } } } } if (sc_data && sc_data[SC_AUTOSPELL].timer != -1 && MRAND (100) < sc_data[SC_AUTOSPELL].val4) { int skilllv = sc_data[SC_AUTOSPELL].val3, i, f = 0; i = MRAND (100); if (i >= 50) skilllv -= 2; else if (i >= 15) skilllv--; if (skilllv < 1) skilllv = 1; if (sd) { int sp = skill_get_sp (sc_data[SC_AUTOSPELL].val2, skilllv) * 2 / 3; if (sd->status.sp >= sp) { if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) || i == 32) f = skill_castend_pos2 (src, target->x, target->y, sc_data[SC_AUTOSPELL].val2, skilllv, tick, flag); else { switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) { case 0: case 2: f = skill_castend_damage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); break; case 1: /* 支援系 */ if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL || (sc_data[SC_AUTOSPELL].val2 == ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead (race, ele)) f = skill_castend_damage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); else f = skill_castend_nodamage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); break; } } if (!f) pc_heal (sd, 0, -sp); } } else { if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) || i == 32) skill_castend_pos2 (src, target->x, target->y, sc_data[SC_AUTOSPELL].val2, skilllv, tick, flag); else { switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) { case 0: case 2: skill_castend_damage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); break; case 1: /* 支援系 */ if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL || (sc_data[SC_AUTOSPELL].val2 == ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead (race, ele)) skill_castend_damage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); else skill_castend_nodamage_id (src, target, sc_data [SC_AUTOSPELL].val2, skilllv, tick, flag); break; } } } } if (sd) { if (sd->autospell_id > 0 && sd->autospell_lv > 0 && MRAND (100) < sd->autospell_rate) { int skilllv = sd->autospell_lv, i, f = 0, sp; i = MRAND (100); if (i >= 50) skilllv -= 2; else if (i >= 15) skilllv--; if (skilllv < 1) skilllv = 1; sp = skill_get_sp (sd->autospell_id, skilllv) * 2 / 3; if (sd->status.sp >= sp) { if ((i = skill_get_inf (sd->autospell_id) == 2) || i == 32) f = skill_castend_pos2 (src, target->x, target->y, sd->autospell_id, skilllv, tick, flag); else { switch (skill_get_nk (sd->autospell_id)) { case 0: case 2: f = skill_castend_damage_id (src, target, sd->autospell_id, skilllv, tick, flag); break; case 1: /* 支援系 */ if ((sd->autospell_id == AL_HEAL || (sd->autospell_id == ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead (race, ele)) f = skill_castend_damage_id (src, target, sd->autospell_id, skilllv, tick, flag); else f = skill_castend_nodamage_id (src, target, sd->autospell_id, skilllv, tick, flag); break; } } if (!f) pc_heal (sd, 0, -sp); } } if (wd.flag & BF_WEAPON && src != target && (wd.damage > 0 || wd.damage2 > 0)) { int hp = 0, sp = 0; if (sd->hp_drain_rate && wd.damage > 0 && MRAND (100) < sd->hp_drain_rate) { hp += (wd.damage * sd->hp_drain_per) / 100; } if (sd->hp_drain_rate_ && wd.damage2 > 0 && MRAND (100) < sd->hp_drain_rate_) { hp += (wd.damage2 * sd->hp_drain_per_) / 100; } if (sd->sp_drain_rate && wd.damage > 0 && MRAND (100) < sd->sp_drain_rate) { sp += (wd.damage * sd->sp_drain_per) / 100; } if (sd->sp_drain_rate_ && wd.damage2 > 0 && MRAND (100) < sd->sp_drain_rate_) { sp += (wd.damage2 * sd->sp_drain_per_) / 100; } if (hp || sp) pc_heal (sd, hp, sp); } } if (rdamage > 0) battle_damage (target, src, rdamage, 0); if (t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1 && t_sc_data[SC_AUTOCOUNTER].val4 > 0) { if (t_sc_data[SC_AUTOCOUNTER].val3 == src->id) battle_weapon_attack (target, src, tick, 0x8000 | t_sc_data[SC_AUTOCOUNTER].val1); skill_status_change_end (target, SC_AUTOCOUNTER, -1); } if (t_sc_data && t_sc_data[SC_BLADESTOP_WAIT].timer != -1) { int lv = t_sc_data[SC_BLADESTOP_WAIT].val1; skill_status_change_end (target, SC_BLADESTOP_WAIT, -1); skill_status_change_start (src, SC_BLADESTOP, lv, 1, (int) src, (int) target, skill_get_time2 (MO_BLADESTOP, lv), 0); skill_status_change_start (target, SC_BLADESTOP, lv, 2, (int) target, (int) src, skill_get_time2 (MO_BLADESTOP, lv), 0); } if (t_sc_data && t_sc_data[SC_SPLASHER].timer != -1) //殴ったので対象のベナムスプラッシャー状態を解除 skill_status_change_end (target, SC_SPLASHER, -1); map_freeblock_unlock (); } return wd.dmg_lv; } int battle_check_undead (int race, int element) { if (battle_config.undead_detect_type == 0) { if (element == 9) return 1; } else if (battle_config.undead_detect_type == 1) { if (race == 1) return 1; } else { if (element == 9 || race == 1) return 1; } return 0; } /*========================================== * 敵味方判定(1=肯定,0=否定,-1=エラー) * flag&0xf0000 = 0x00000:敵じゃないか判定(ret:1=敵ではない) * = 0x10000:パーティー判定(ret:1=パーティーメンバ) * = 0x20000:全て(ret:1=敵味方両方) * = 0x40000:敵か判定(ret:1=敵) * = 0x50000:パーティーじゃないか判定(ret:1=パーティでない) *------------------------------------------ */ int battle_check_target (struct block_list *src, struct block_list *target, int flag) { int s_p, s_g, t_p, t_g; struct block_list *ss = src; nullpo_retr (0, src); nullpo_retr (0, target); if (flag & 0x40000) { // 反転フラグ int ret = battle_check_target (src, target, flag & 0x30000); if (ret != -1) return !ret; return -1; } if (flag & 0x20000) { if (target->type == BL_MOB || target->type == BL_PC) return 1; else return -1; } if (src->type == BL_SKILL && target->type == BL_SKILL) // 対象がスキルユニットなら無条件肯定 return -1; if (target->type == BL_PC && ((struct map_session_data *) target)->invincible_timer != -1) return -1; if (target->type == BL_SKILL) { switch (((struct skill_unit *) target)->group->unit_id) { case 0x8d: case 0x8f: case 0x98: return 0; break; } } // スキルユニットの場合、親を求める if (src->type == BL_SKILL) { int inf2 = skill_get_inf2 (((struct skill_unit *) src)->group->skill_id); if ((ss = map_id2bl (((struct skill_unit *) src)->group->src_id)) == NULL) return -1; if (ss->prev == NULL) return -1; if (inf2 & 0x80 && (map[src->m].flag.pvp || pc_iskiller ((struct map_session_data *) src, (struct map_session_data *) target)) && // [MouseJstr] !(target->type == BL_PC && pc_isinvisible ((struct map_session_data *) target))) return 0; if (ss == target) { if (inf2 & 0x100) return 0; if (inf2 & 0x200) return -1; } } // Mobでmaster_idがあってspecial_mob_aiなら、召喚主を求める if (src->type == BL_MOB) { struct mob_data *md = (struct mob_data *) src; if (md && md->master_id > 0) { if (md->master_id == target->id) // 主なら肯定 return 1; if (md->state.special_mob_ai) { if (target->type == BL_MOB) { //special_mob_aiで対象がMob struct mob_data *tmd = (struct mob_data *) target; if (tmd) { if (tmd->master_id != md->master_id) //召喚主が一緒でなければ否定 return 0; else { //召喚主が一緒なので肯定したいけど自爆は否定 if (md->state.special_mob_ai > 2) return 0; else return 1; } } } } if ((ss = map_id2bl (md->master_id)) == NULL) return -1; } } if (src == target || ss == target) // 同じなら肯定 return 1; if (target->type == BL_PC && pc_isinvisible ((struct map_session_data *) target)) return -1; if (src->prev == NULL || // 死んでるならエラー (src->type == BL_PC && pc_isdead ((struct map_session_data *) src))) return -1; if ((ss->type == BL_PC && target->type == BL_MOB) || (ss->type == BL_MOB && target->type == BL_PC)) return 0; // PCvsMOBなら否定 s_p = battle_get_party_id (ss); s_g = battle_get_guild_id (ss); t_p = battle_get_party_id (target); t_g = battle_get_guild_id (target); if (flag & 0x10000) { if (s_p && t_p && s_p == t_p) // 同じパーティなら肯定(味方) return 1; else // パーティ検索なら同じパーティじゃない時点で否定 return 0; } if (ss->type == BL_MOB && s_g > 0 && t_g > 0 && s_g == t_g) // 同じギルド/mobクラスなら肯定(味方) return 1; //printf("ss:%d src:%d target:%d flag:0x%x %d %d ",ss->id,src->id,target->id,flag,src->type,target->type); //printf("p:%d %d g:%d %d\n",s_p,t_p,s_g,t_g); if (ss->type == BL_PC && target->type == BL_PC) { // 両方PVPモードなら否定(敵) struct skill_unit *su = NULL; if (src->type == BL_SKILL) su = (struct skill_unit *) src; if (map[ss->m].flag.pvp || pc_iskiller ((struct map_session_data *) ss, (struct map_session_data *) target)) { // [MouseJstr] if (su && su->group->target_flag == BCT_NOENEMY) return 1; else if (battle_config.pk_mode && (((struct map_session_data *) ss)->status.pc_class == 0 || ((struct map_session_data *) target)-> status.pc_class == 0)) return 1; // prevent novice engagement in pk_mode [Valaris] else if (map[ss->m].flag.pvp_noparty && s_p > 0 && t_p > 0 && s_p == t_p) return 1; else if (map[ss->m].flag.pvp_noguild && s_g > 0 && t_g > 0 && s_g == t_g) return 1; return 0; } if (map[src->m].flag.gvg) { struct guild *g = NULL; if (su && su->group->target_flag == BCT_NOENEMY) return 1; if (s_g > 0 && s_g == t_g) return 1; if (map[src->m].flag.gvg_noparty && s_p > 0 && t_p > 0 && s_p == t_p) return 1; if ((g = guild_search (s_g))) { int i; for (i = 0; i < MAX_GUILDALLIANCE; i++) { if (g->alliance[i].guild_id > 0 && g->alliance[i].guild_id == t_g) { if (g->alliance[i].opposition) return 0; //敵対ギルドなら無条件に敵 else return 1; //同盟ギルドなら無条件に味方 } } } return 0; } } return 1; // 該当しないので無関係人物(まあ敵じゃないので味方) } /*========================================== * 射程判定 *------------------------------------------ */ int battle_check_range (struct block_list *src, struct block_list *bl, int range) { int dx, dy; struct walkpath_data wpd; int arange; nullpo_retr (0, src); nullpo_retr (0, bl); dx = abs (bl->x - src->x); dy = abs (bl->y - src->y); arange = ((dx > dy) ? dx : dy); if (src->m != bl->m) // 違うマップ return 0; if (range > 0 && range < arange) // 遠すぎる return 0; if (arange < 2) // 同じマスか隣接 return 1; // if(bl->type == BL_SKILL && ((struct skill_unit *)bl)->group->unit_id == 0x8d) // return 1; // 障害物判定 wpd.path_len = 0; wpd.path_pos = 0; wpd.path_half = 0; if (path_search (&wpd, src->m, src->x, src->y, bl->x, bl->y, 0x10001) != -1) return 1; dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); return (path_search (&wpd, src->m, src->x + dx, src->y + dy, bl->x - dx, bl->y - dy, 0x10001) != -1) ? 1 : 0; } /*========================================== * Return numerical value of a switch configuration (modified by [Yor]) * on/off, english, fran軋is, deutsch, espal *------------------------------------------ */ int battle_config_switch (const char *str) { if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 || strcasecmp (str, "si") == 0) return 1; if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) return 0; return atoi (str); } /*========================================== * 設定ファイルを読み込む *------------------------------------------ */ int battle_config_read (const char *cfgName) { int i; char line[1024], w1[1024], w2[1024]; FILE *fp; static int count = 0; if ((count++) == 0) { battle_config.warp_point_debug = 0; battle_config.enemy_critical = 0; battle_config.enemy_critical_rate = 100; battle_config.enemy_str = 1; battle_config.enemy_perfect_flee = 0; battle_config.cast_rate = 100; battle_config.delay_rate = 100; battle_config.delay_dependon_dex = 0; battle_config.sdelay_attack_enable = 0; battle_config.left_cardfix_to_right = 0; battle_config.pc_skill_add_range = 0; battle_config.skill_out_range_consume = 1; battle_config.mob_skill_add_range = 0; battle_config.pc_damage_delay = 1; battle_config.pc_damage_delay_rate = 100; battle_config.defnotenemy = 1; battle_config.random_monster_checklv = 1; battle_config.attr_recover = 1; battle_config.flooritem_lifetime = LIFETIME_FLOORITEM * 1000; battle_config.item_auto_get = 0; battle_config.drop_pickup_safety_zone = 20; battle_config.item_first_get_time = 3000; battle_config.item_second_get_time = 1000; battle_config.item_third_get_time = 1000; battle_config.mvp_item_first_get_time = 10000; battle_config.mvp_item_second_get_time = 10000; battle_config.mvp_item_third_get_time = 2000; battle_config.drop_rate0item = 0; battle_config.base_exp_rate = 100; battle_config.job_exp_rate = 100; battle_config.pvp_exp = 1; battle_config.gtb_pvp_only = 0; battle_config.death_penalty_type = 0; battle_config.death_penalty_base = 0; battle_config.death_penalty_job = 0; battle_config.zeny_penalty = 0; battle_config.restart_hp_rate = 0; battle_config.restart_sp_rate = 0; battle_config.mvp_item_rate = 100; battle_config.mvp_exp_rate = 100; battle_config.mvp_hp_rate = 100; battle_config.monster_hp_rate = 100; battle_config.monster_max_aspd = 199; battle_config.atc_gmonly = 0; battle_config.gm_allskill = 0; battle_config.gm_allequip = 0; battle_config.gm_skilluncond = 0; battle_config.guild_max_castles = 0; battle_config.skillfree = 0; battle_config.skillup_limit = 0; battle_config.wp_rate = 100; battle_config.pp_rate = 100; battle_config.monster_active_enable = 1; battle_config.monster_damage_delay_rate = 100; battle_config.monster_loot_type = 0; battle_config.mob_skill_use = 1; battle_config.mob_count_rate = 100; battle_config.quest_skill_learn = 0; battle_config.quest_skill_reset = 1; battle_config.basic_skill_check = 1; battle_config.guild_emperium_check = 1; battle_config.guild_exp_limit = 50; battle_config.pc_invincible_time = 5000; battle_config.skill_min_damage = 0; battle_config.finger_offensive_type = 0; battle_config.heal_exp = 0; battle_config.resurrection_exp = 0; battle_config.shop_exp = 0; battle_config.combo_delay_rate = 100; battle_config.item_check = 1; battle_config.wedding_modifydisplay = 0; battle_config.natural_healhp_interval = 6000; battle_config.natural_healsp_interval = 8000; battle_config.natural_heal_skill_interval = 10000; battle_config.natural_heal_weight_rate = 50; battle_config.itemheal_regeneration_factor = 1; battle_config.item_name_override_grffile = 1; battle_config.arrow_decrement = 1; battle_config.max_aspd = 199; battle_config.max_hp = 32500; battle_config.max_sp = 32500; battle_config.max_lv = 99; // [MouseJstr] battle_config.max_parameter = 99; battle_config.max_cart_weight = 8000; battle_config.pc_skill_log = 0; battle_config.mob_skill_log = 0; battle_config.battle_log = 0; battle_config.save_log = 0; battle_config.error_log = 1; battle_config.etc_log = 1; battle_config.save_clothcolor = 0; battle_config.undead_detect_type = 0; battle_config.pc_auto_counter_type = 1; battle_config.monster_auto_counter_type = 1; battle_config.agi_penaly_type = 0; battle_config.agi_penaly_count = 3; battle_config.agi_penaly_num = 0; battle_config.agi_penaly_count_lv = ATK_FLEE; battle_config.vit_penaly_type = 0; battle_config.vit_penaly_count = 3; battle_config.vit_penaly_num = 0; battle_config.vit_penaly_count_lv = ATK_DEF; battle_config.player_defense_type = 0; battle_config.monster_defense_type = 0; battle_config.magic_defense_type = 0; battle_config.pc_skill_reiteration = 0; battle_config.monster_skill_reiteration = 0; battle_config.pc_skill_nofootset = 0; battle_config.monster_skill_nofootset = 0; battle_config.pc_cloak_check_type = 0; battle_config.monster_cloak_check_type = 0; battle_config.gvg_short_damage_rate = 100; battle_config.gvg_long_damage_rate = 100; battle_config.gvg_magic_damage_rate = 100; battle_config.gvg_misc_damage_rate = 100; battle_config.gvg_eliminate_time = 7000; battle_config.mob_changetarget_byskill = 0; battle_config.pc_attack_direction_change = 1; battle_config.monster_attack_direction_change = 1; battle_config.pc_undead_nofreeze = 0; battle_config.pc_land_skill_limit = 1; battle_config.monster_land_skill_limit = 1; battle_config.party_skill_penaly = 1; battle_config.monster_class_change_full_recover = 0; battle_config.produce_item_name_input = 1; battle_config.produce_potion_name_input = 1; battle_config.making_arrow_name_input = 1; battle_config.holywater_name_input = 1; battle_config.display_delay_skill_fail = 1; battle_config.chat_warpportal = 0; battle_config.mob_warpportal = 0; battle_config.dead_branch_active = 0; battle_config.show_steal_in_same_party = 0; battle_config.enable_upper_class = 0; battle_config.pc_attack_attr_none = 0; battle_config.mob_attack_attr_none = 1; battle_config.mob_ghostring_fix = 0; battle_config.gx_allhit = 0; battle_config.gx_cardfix = 0; battle_config.gx_dupele = 1; battle_config.gx_disptype = 1; battle_config.player_skill_partner_check = 1; battle_config.hide_GM_session = 0; battle_config.unit_movement_type = 0; battle_config.invite_request_check = 1; battle_config.skill_removetrap_type = 0; battle_config.disp_experience = 0; battle_config.item_rate_common = 100; battle_config.item_rate_equip = 100; battle_config.item_rate_card = 100; battle_config.item_rate_heal = 100; // Added by Valaris battle_config.item_rate_use = 100; // End battle_config.item_drop_common_min = 1; // Added by TyrNemesis^ battle_config.item_drop_common_max = 10000; battle_config.item_drop_equip_min = 1; battle_config.item_drop_equip_max = 10000; battle_config.item_drop_card_min = 1; battle_config.item_drop_card_max = 10000; battle_config.item_drop_mvp_min = 1; battle_config.item_drop_mvp_max = 10000; // End Addition battle_config.item_drop_heal_min = 1; // Added by Valaris battle_config.item_drop_heal_max = 10000; battle_config.item_drop_use_min = 1; battle_config.item_drop_use_max = 10000; // End battle_config.prevent_logout = 1; // Added by RoVeRT battle_config.maximum_level = 255; // Added by Valaris battle_config.drops_by_luk = 0; // [Valaris] battle_config.equipment_breaking = 0; // [Valaris] battle_config.equipment_break_rate = 100; // [Valaris] battle_config.pk_mode = 0; // [Valaris] battle_config.multi_level_up = 0; // [Valaris] battle_config.backstab_bow_penalty = 0; // Akaru battle_config.night_at_start = 0; // added by [Yor] battle_config.day_duration = 2 * 60 * 60 * 1000; // added by [Yor] (2 hours) battle_config.night_duration = 30 * 60 * 1000; // added by [Yor] (30 minutes) battle_config.show_mob_hp = 0; // [Valaris] battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level) battle_config.any_warp_GM_min_level = 20; // added by [Yor] battle_config.packet_ver_flag = 63; // added by [Yor] battle_config.min_hair_style = 0; battle_config.max_hair_style = 20; battle_config.min_hair_color = 0; battle_config.max_hair_color = 9; battle_config.min_cloth_color = 0; battle_config.max_cloth_color = 4; battle_config.castrate_dex_scale = 150; battle_config.area_size = 14; battle_config.chat_lame_penalty = 2; battle_config.chat_spam_threshold = 10; battle_config.chat_spam_flood = 10; battle_config.chat_spam_ban = 1; battle_config.chat_spam_warn = 8; battle_config.chat_maxline = 255; battle_config.packet_spam_threshold = 2; battle_config.packet_spam_flood = 30; battle_config.packet_spam_kick = 1; battle_config.mask_ip_gms = 1; battle_config.mob_splash_radius = -1; } fp = fopen_ (cfgName, "r"); if (fp == NULL) { printf ("file not found: %s\n", cfgName); return 1; } while (fgets (line, 1020, fp)) { const struct { char str[128]; int *val; } data[] = { { "warp_point_debug", &battle_config.warp_point_debug}, { "enemy_critical", &battle_config.enemy_critical}, { "enemy_critical_rate", &battle_config.enemy_critical_rate}, { "enemy_str", &battle_config.enemy_str}, { "enemy_perfect_flee", &battle_config.enemy_perfect_flee}, { "casting_rate", &battle_config.cast_rate}, { "delay_rate", &battle_config.delay_rate}, { "delay_dependon_dex", &battle_config.delay_dependon_dex}, { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable}, { "left_cardfix_to_right", &battle_config.left_cardfix_to_right}, { "player_skill_add_range", &battle_config.pc_skill_add_range}, { "skill_out_range_consume", &battle_config.skill_out_range_consume}, { "monster_skill_add_range", &battle_config.mob_skill_add_range}, { "player_damage_delay", &battle_config.pc_damage_delay}, { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate}, { "defunit_not_enemy", &battle_config.defnotenemy}, { "random_monster_checklv", &battle_config.random_monster_checklv}, { "attribute_recover", &battle_config.attr_recover}, { "flooritem_lifetime", &battle_config.flooritem_lifetime}, { "item_auto_get", &battle_config.item_auto_get}, { "drop_pickup_safety_zone", &battle_config.drop_pickup_safety_zone}, { "item_first_get_time", &battle_config.item_first_get_time}, { "item_second_get_time", &battle_config.item_second_get_time}, { "item_third_get_time", &battle_config.item_third_get_time}, { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time}, { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time}, { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time}, { "item_rate", &battle_config.item_rate}, { "drop_rate0item", &battle_config.drop_rate0item}, { "base_exp_rate", &battle_config.base_exp_rate}, { "job_exp_rate", &battle_config.job_exp_rate}, { "pvp_exp", &battle_config.pvp_exp}, { "gtb_pvp_only", &battle_config.gtb_pvp_only}, { "guild_max_castles", &battle_config.guild_max_castles}, { "death_penalty_type", &battle_config.death_penalty_type}, { "death_penalty_base", &battle_config.death_penalty_base}, { "death_penalty_job", &battle_config.death_penalty_job}, { "zeny_penalty", &battle_config.zeny_penalty}, { "restart_hp_rate", &battle_config.restart_hp_rate}, { "restart_sp_rate", &battle_config.restart_sp_rate}, { "mvp_hp_rate", &battle_config.mvp_hp_rate}, { "mvp_item_rate", &battle_config.mvp_item_rate}, { "mvp_exp_rate", &battle_config.mvp_exp_rate}, { "monster_hp_rate", &battle_config.monster_hp_rate}, { "monster_max_aspd", &battle_config.monster_max_aspd}, { "atcommand_gm_only", &battle_config.atc_gmonly}, { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit}, { "gm_all_skill", &battle_config.gm_allskill}, { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra}, { "gm_all_equipment", &battle_config.gm_allequip}, { "gm_skill_unconditional", &battle_config.gm_skilluncond}, { "player_skillfree", &battle_config.skillfree}, { "player_skillup_limit", &battle_config.skillup_limit}, { "weapon_produce_rate", &battle_config.wp_rate}, { "potion_produce_rate", &battle_config.pp_rate}, { "monster_active_enable", &battle_config.monster_active_enable}, { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate}, { "monster_loot_type", &battle_config.monster_loot_type}, { "mob_skill_use", &battle_config.mob_skill_use}, { "mob_count_rate", &battle_config.mob_count_rate}, { "quest_skill_learn", &battle_config.quest_skill_learn}, { "quest_skill_reset", &battle_config.quest_skill_reset}, { "basic_skill_check", &battle_config.basic_skill_check}, { "guild_emperium_check", &battle_config.guild_emperium_check}, { "guild_exp_limit", &battle_config.guild_exp_limit}, { "player_invincible_time", &battle_config.pc_invincible_time}, { "skill_min_damage", &battle_config.skill_min_damage}, { "finger_offensive_type", &battle_config.finger_offensive_type}, { "heal_exp", &battle_config.heal_exp}, { "resurrection_exp", &battle_config.resurrection_exp}, { "shop_exp", &battle_config.shop_exp}, { "combo_delay_rate", &battle_config.combo_delay_rate}, { "item_check", &battle_config.item_check}, { "wedding_modifydisplay", &battle_config.wedding_modifydisplay}, { "natural_healhp_interval", &battle_config.natural_healhp_interval}, { "natural_healsp_interval", &battle_config.natural_healsp_interval}, { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval}, { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate}, { "itemheal_regeneration_factor", &battle_config.itemheal_regeneration_factor}, { "item_name_override_grffile", &battle_config.item_name_override_grffile}, { "arrow_decrement", &battle_config.arrow_decrement}, { "max_aspd", &battle_config.max_aspd}, { "max_hp", &battle_config.max_hp}, { "max_sp", &battle_config.max_sp}, { "max_lv", &battle_config.max_lv}, { "max_parameter", &battle_config.max_parameter}, { "max_cart_weight", &battle_config.max_cart_weight}, { "player_skill_log", &battle_config.pc_skill_log}, { "monster_skill_log", &battle_config.mob_skill_log}, { "battle_log", &battle_config.battle_log}, { "save_log", &battle_config.save_log}, { "error_log", &battle_config.error_log}, { "etc_log", &battle_config.etc_log}, { "save_clothcolor", &battle_config.save_clothcolor}, { "undead_detect_type", &battle_config.undead_detect_type}, { "player_auto_counter_type", &battle_config.pc_auto_counter_type}, { "monster_auto_counter_type", &battle_config.monster_auto_counter_type}, { "agi_penaly_type", &battle_config.agi_penaly_type}, { "agi_penaly_count", &battle_config.agi_penaly_count}, { "agi_penaly_num", &battle_config.agi_penaly_num}, { "agi_penaly_count_lv", &battle_config.agi_penaly_count_lv}, { "vit_penaly_type", &battle_config.vit_penaly_type}, { "vit_penaly_count", &battle_config.vit_penaly_count}, { "vit_penaly_num", &battle_config.vit_penaly_num}, { "vit_penaly_count_lv", &battle_config.vit_penaly_count_lv}, { "player_defense_type", &battle_config.player_defense_type}, { "monster_defense_type", &battle_config.monster_defense_type}, { "magic_defense_type", &battle_config.magic_defense_type}, { "player_skill_reiteration", &battle_config.pc_skill_reiteration}, { "monster_skill_reiteration", &battle_config.monster_skill_reiteration}, { "player_skill_nofootset", &battle_config.pc_skill_nofootset}, { "monster_skill_nofootset", &battle_config.monster_skill_nofootset}, { "player_cloak_check_type", &battle_config.pc_cloak_check_type}, { "monster_cloak_check_type", &battle_config.monster_cloak_check_type}, { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate}, { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate}, { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate}, { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate}, { "gvg_eliminate_time", &battle_config.gvg_eliminate_time}, { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill}, { "player_attack_direction_change", &battle_config.pc_attack_direction_change}, { "monster_attack_direction_change", &battle_config.monster_attack_direction_change}, { "player_land_skill_limit", &battle_config.pc_land_skill_limit}, { "monster_land_skill_limit", &battle_config.monster_land_skill_limit}, { "party_skill_penaly", &battle_config.party_skill_penaly}, { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover}, { "produce_item_name_input", &battle_config.produce_item_name_input}, { "produce_potion_name_input", &battle_config.produce_potion_name_input}, { "making_arrow_name_input", &battle_config.making_arrow_name_input}, { "holywater_name_input", &battle_config.holywater_name_input}, { "display_delay_skill_fail", &battle_config.display_delay_skill_fail}, { "chat_warpportal", &battle_config.chat_warpportal}, { "mob_warpportal", &battle_config.mob_warpportal}, { "dead_branch_active", &battle_config.dead_branch_active}, { "show_steal_in_same_party", &battle_config.show_steal_in_same_party}, { "enable_upper_class", &battle_config.enable_upper_class}, { "mob_attack_attr_none", &battle_config.mob_attack_attr_none}, { "mob_ghostring_fix", &battle_config.mob_ghostring_fix}, { "pc_attack_attr_none", &battle_config.pc_attack_attr_none}, { "gx_allhit", &battle_config.gx_allhit}, { "gx_cardfix", &battle_config.gx_cardfix}, { "gx_dupele", &battle_config.gx_dupele}, { "gx_disptype", &battle_config.gx_disptype}, { "player_skill_partner_check", &battle_config.player_skill_partner_check}, { "hide_GM_session", &battle_config.hide_GM_session}, { "unit_movement_type", &battle_config.unit_movement_type}, { "invite_request_check", &battle_config.invite_request_check}, { "skill_removetrap_type", &battle_config.skill_removetrap_type}, { "disp_experience", &battle_config.disp_experience}, { "castle_defense_rate", &battle_config.castle_defense_rate}, { "riding_weight", &battle_config.riding_weight}, { "item_rate_common", &battle_config.item_rate_common}, // Added by RoVeRT { "item_rate_equip", &battle_config.item_rate_equip}, { "item_rate_card", &battle_config.item_rate_card}, // End Addition { "item_rate_heal", &battle_config.item_rate_heal}, // Added by Valaris { "item_rate_use", &battle_config.item_rate_use}, // End { "item_drop_common_min", &battle_config.item_drop_common_min}, // Added by TyrNemesis^ { "item_drop_common_max", &battle_config.item_drop_common_max}, { "item_drop_equip_min", &battle_config.item_drop_equip_min}, { "item_drop_equip_max", &battle_config.item_drop_equip_max}, { "item_drop_card_min", &battle_config.item_drop_card_min}, { "item_drop_card_max", &battle_config.item_drop_card_max}, { "item_drop_mvp_min", &battle_config.item_drop_mvp_min}, { "item_drop_mvp_max", &battle_config.item_drop_mvp_max}, // End Addition { "prevent_logout", &battle_config.prevent_logout}, // Added by RoVeRT { "alchemist_summon_reward", &battle_config.alchemist_summon_reward}, // [Valaris] { "maximum_level", &battle_config.maximum_level}, // [Valaris] { "drops_by_luk", &battle_config.drops_by_luk}, // [Valaris] { "monsters_ignore_gm", &battle_config.monsters_ignore_gm}, // [Valaris] { "equipment_breaking", &battle_config.equipment_breaking}, // [Valaris] { "equipment_break_rate", &battle_config.equipment_break_rate}, // [Valaris] { "pk_mode", &battle_config.pk_mode}, // [Valaris] { "multi_level_up", &battle_config.multi_level_up}, // [Valaris] { "backstab_bow_penalty", &battle_config.backstab_bow_penalty}, { "night_at_start", &battle_config.night_at_start}, // added by [Yor] { "day_duration", &battle_config.day_duration}, // added by [Yor] { "night_duration", &battle_config.night_duration}, // added by [Yor] { "show_mob_hp", &battle_config.show_mob_hp}, // [Valaris] { "hack_info_GM_level", &battle_config.hack_info_GM_level}, // added by [Yor] { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level}, // added by [Yor] { "packet_ver_flag", &battle_config.packet_ver_flag}, // added by [Yor] { "min_hair_style", &battle_config.min_hair_style}, // added by [MouseJstr] { "max_hair_style", &battle_config.max_hair_style}, // added by [MouseJstr] { "min_hair_color", &battle_config.min_hair_color}, // added by [MouseJstr] { "max_hair_color", &battle_config.max_hair_color}, // added by [MouseJstr] { "min_cloth_color", &battle_config.min_cloth_color}, // added by [MouseJstr] { "max_cloth_color", &battle_config.max_cloth_color}, // added by [MouseJstr] { "castrate_dex_scale", &battle_config.castrate_dex_scale}, // added by [MouseJstr] { "area_size", &battle_config.area_size}, // added by [MouseJstr] { "muting_players", &battle_config.muting_players}, // added by [Apple] { "chat_lame_penalty", &battle_config.chat_lame_penalty}, { "chat_spam_threshold", &battle_config.chat_spam_threshold}, { "chat_spam_flood", &battle_config.chat_spam_flood}, { "chat_spam_ban", &battle_config.chat_spam_ban}, { "chat_spam_warn", &battle_config.chat_spam_warn}, { "chat_maxline", &battle_config.chat_maxline}, { "packet_spam_threshold", &battle_config.packet_spam_threshold}, { "packet_spam_flood", &battle_config.packet_spam_flood}, { "packet_spam_kick", &battle_config.packet_spam_kick}, { "mask_ip_gms", &battle_config.mask_ip_gms}, { "mob_splash_radius", &battle_config.mob_splash_radius}, }; if (line[0] == '/' && line[1] == '/') continue; if (sscanf (line, "%[^:]:%s", w1, w2) != 2) continue; for (i = 0; i < sizeof (data) / (sizeof (data[0])); i++) if (strcasecmp (w1, data[i].str) == 0) *data[i].val = battle_config_switch (w2); if (strcasecmp (w1, "import") == 0) battle_config_read (w2); } fclose_ (fp); if (--count == 0) { if (battle_config.flooritem_lifetime < 1000) battle_config.flooritem_lifetime = LIFETIME_FLOORITEM * 1000; if (battle_config.restart_hp_rate < 0) battle_config.restart_hp_rate = 0; else if (battle_config.restart_hp_rate > 100) battle_config.restart_hp_rate = 100; if (battle_config.restart_sp_rate < 0) battle_config.restart_sp_rate = 0; else if (battle_config.restart_sp_rate > 100) battle_config.restart_sp_rate = 100; if (battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL) battle_config.natural_healhp_interval = NATURAL_HEAL_INTERVAL; if (battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL) battle_config.natural_healsp_interval = NATURAL_HEAL_INTERVAL; if (battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL) battle_config.natural_heal_skill_interval = NATURAL_HEAL_INTERVAL; if (battle_config.natural_heal_weight_rate < 50) battle_config.natural_heal_weight_rate = 50; if (battle_config.natural_heal_weight_rate > 101) battle_config.natural_heal_weight_rate = 101; battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd * 10; if (battle_config.monster_max_aspd < 10) battle_config.monster_max_aspd = 10; if (battle_config.monster_max_aspd > 1000) battle_config.monster_max_aspd = 1000; battle_config.max_aspd = 2000 - battle_config.max_aspd * 10; if (battle_config.max_aspd < 10) battle_config.max_aspd = 10; if (battle_config.max_aspd > 1000) battle_config.max_aspd = 1000; if (battle_config.max_hp > 1000000) battle_config.max_hp = 1000000; if (battle_config.max_hp < 100) battle_config.max_hp = 100; if (battle_config.max_sp > 1000000) battle_config.max_sp = 1000000; if (battle_config.max_sp < 100) battle_config.max_sp = 100; if (battle_config.max_parameter < 10) battle_config.max_parameter = 10; if (battle_config.max_parameter > 10000) battle_config.max_parameter = 10000; if (battle_config.max_cart_weight > 1000000) battle_config.max_cart_weight = 1000000; if (battle_config.max_cart_weight < 100) battle_config.max_cart_weight = 100; battle_config.max_cart_weight *= 10; if (battle_config.agi_penaly_count < 2) battle_config.agi_penaly_count = 2; if (battle_config.vit_penaly_count < 2) battle_config.vit_penaly_count = 2; if (battle_config.guild_exp_limit > 99) battle_config.guild_exp_limit = 99; if (battle_config.guild_exp_limit < 0) battle_config.guild_exp_limit = 0; if (battle_config.castle_defense_rate < 0) battle_config.castle_defense_rate = 0; if (battle_config.castle_defense_rate > 100) battle_config.castle_defense_rate = 100; if (battle_config.item_drop_common_min < 1) // Added by TyrNemesis^ battle_config.item_drop_common_min = 1; if (battle_config.item_drop_common_max > 10000) battle_config.item_drop_common_max = 10000; if (battle_config.item_drop_equip_min < 1) battle_config.item_drop_equip_min = 1; if (battle_config.item_drop_equip_max > 10000) battle_config.item_drop_equip_max = 10000; if (battle_config.item_drop_card_min < 1) battle_config.item_drop_card_min = 1; if (battle_config.item_drop_card_max > 10000) battle_config.item_drop_card_max = 10000; if (battle_config.item_drop_mvp_min < 1) battle_config.item_drop_mvp_min = 1; if (battle_config.item_drop_mvp_max > 10000) battle_config.item_drop_mvp_max = 10000; // End Addition if (battle_config.night_at_start < 0) // added by [Yor] battle_config.night_at_start = 0; else if (battle_config.night_at_start > 1) // added by [Yor] battle_config.night_at_start = 1; if (battle_config.day_duration < 0) // added by [Yor] battle_config.day_duration = 0; if (battle_config.night_duration < 0) // added by [Yor] battle_config.night_duration = 0; if (battle_config.hack_info_GM_level < 0) // added by [Yor] battle_config.hack_info_GM_level = 0; else if (battle_config.hack_info_GM_level > 100) battle_config.hack_info_GM_level = 100; if (battle_config.any_warp_GM_min_level < 0) // added by [Yor] battle_config.any_warp_GM_min_level = 0; else if (battle_config.any_warp_GM_min_level > 100) battle_config.any_warp_GM_min_level = 100; if (battle_config.chat_spam_ban < 0) battle_config.chat_spam_ban = 0; else if (battle_config.chat_spam_ban > 32767) battle_config.chat_spam_ban = 32767; if (battle_config.chat_spam_flood < 0) battle_config.chat_spam_flood = 0; else if (battle_config.chat_spam_flood > 32767) battle_config.chat_spam_flood = 32767; if (battle_config.chat_spam_warn < 0) battle_config.chat_spam_warn = 0; else if (battle_config.chat_spam_warn > 32767) battle_config.chat_spam_warn = 32767; if (battle_config.chat_spam_threshold < 0) battle_config.chat_spam_threshold = 0; else if (battle_config.chat_spam_threshold > 32767) battle_config.chat_spam_threshold = 32767; if (battle_config.chat_maxline < 1) battle_config.chat_maxline = 1; else if (battle_config.chat_maxline > 512) battle_config.chat_maxline = 512; if (battle_config.packet_spam_threshold < 0) battle_config.packet_spam_threshold = 0; else if (battle_config.packet_spam_threshold > 32767) battle_config.packet_spam_threshold = 32767; if (battle_config.packet_spam_flood < 0) battle_config.packet_spam_flood = 0; else if (battle_config.packet_spam_flood > 32767) battle_config.packet_spam_flood = 32767; if (battle_config.packet_spam_kick < 0) battle_config.packet_spam_kick = 0; else if (battle_config.packet_spam_kick > 1) battle_config.packet_spam_kick = 1; if (battle_config.mask_ip_gms < 0) battle_config.mask_ip_gms = 0; else if (battle_config.mask_ip_gms > 1) battle_config.mask_ip_gms = 1; // at least 1 client must be accepted if ((battle_config.packet_ver_flag & 63) == 0) // added by [Yor] battle_config.packet_ver_flag = 63; // accept all clients } return 0; }