// $Id: skill.c,v 1.8 2004/09/25 05:32:19 MouseJstr Exp $ /* スキル関係 */ #include #include #include #include #include "../common/timer.hpp" #include "../common/nullpo.hpp" #include "../common/mt_rand.hpp" #include "magic.hpp" #include "battle.hpp" #include "clif.hpp" #include "intif.hpp" #include "itemdb.hpp" #include "map.hpp" #include "mob.hpp" #include "party.hpp" #include "pc.hpp" #include "script.hpp" #include "skill.hpp" #include "../common/socket.hpp" #ifdef MEMWATCH #include "memwatch.hpp" #endif #define SKILLUNITTIMER_INVERVAL 100 #define STATE_BLIND 0x10 /* スキル番号=>ステータス異常番号変換テーブル */ int SkillStatusChangeTable[] = { /* skill.hのenumのSC_***とあわせること */ /* 0- */ -1, -1, -1, -1, -1, -1, SC_PROVOKE, /* プロボック */ -1, 1, -1, /* 10- */ SC_SIGHT, /* サイト */ -1, -1, -1, -1, SC_FREEZE, /* フロストダイバー */ SC_STONE, /* ストーンカース */ -1, -1, -1, /* 20- */ -1, -1, -1, -1, SC_RUWACH, /* ルアフ */ -1, -1, -1, -1, SC_INCREASEAGI, /* 速度増加 */ /* 30- */ SC_DECREASEAGI, /* 速度減少 */ -1, SC_SIGNUMCRUCIS, /* シグナムクルシス */ SC_ANGELUS, /* エンジェラス */ SC_BLESSING, /* ブレッシング */ -1, -1, -1, -1, -1, /* 40- */ -1, -1, -1, -1, -1, SC_CONCENTRATE, /* 集中力向上 */ -1, -1, -1, -1, /* 50- */ -1, SC_HIDING, /* ハイディング */ -1, -1, -1, -1, -1, -1, -1, -1, /* 60- */ SC_TWOHANDQUICKEN, /* 2HQ */ SC_AUTOCOUNTER, -1, -1, -1, -1, SC_IMPOSITIO, /* インポシティオマヌス */ SC_SUFFRAGIUM, /* サフラギウム */ SC_ASPERSIO, /* アスペルシオ */ SC_BENEDICTIO, /* 聖体降福 */ /* 70- */ -1, SC_SLOWPOISON, -1, SC_KYRIE, /* キリエエレイソン */ SC_MAGNIFICAT, /* マグニフィカート */ SC_GLORIA, /* グロリア */ SC_DIVINA, /* レックスディビーナ */ -1, SC_AETERNA, /* レックスエーテルナ */ -1, /* 80- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90- */ -1, -1, SC_QUAGMIRE, /* クァグマイア */ -1, -1, -1, -1, -1, -1, -1, /* 100- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 110- */ -1, SC_ADRENALINE, /* アドレナリンラッシュ */ SC_WEAPONPERFECTION, /* ウェポンパーフェクション */ SC_OVERTHRUST, /* オーバートラスト */ SC_MAXIMIZEPOWER, /* マキシマイズパワー */ -1, -1, -1, -1, -1, /* 120- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 130- */ -1, -1, -1, -1, -1, SC_CLOAKING, /* クローキング */ SC_STAN, /* ソニックブロー */ -1, SC_ENCPOISON, /* エンチャントポイズン */ SC_POISONREACT, /* ポイズンリアクト */ /* 140- */ SC_POISON, /* ベノムダスト */ SC_SPLASHER, /* ベナムスプラッシャー */ -1, SC_TRICKDEAD, /* 死んだふり */ -1, -1, -1, -1, -1, -1, /* 150- */ -1, -1, -1, -1, -1, SC_LOUD, /* ラウドボイス */ -1, SC_ENERGYCOAT, /* エナジーコート */ -1, -1, /* 160- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, SC_SELFDESTRUCTION, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, SC_KEEPING, -1, -1, SC_BARRIER, -1, -1, SC_HALLUCINATION, -1, -1, /* 210- */ -1, -1, -1, -1, -1, SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, -1, /* 220- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 230- */ -1, -1, -1, -1, SC_CP_WEAPON, SC_CP_SHIELD, SC_CP_ARMOR, SC_CP_HELM, -1, -1, /* 240- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, SC_AUTOGUARD, /* 250- */ -1, -1, SC_REFLECTSHIELD, -1, -1, SC_DEVOTION, SC_PROVIDENCE, SC_DEFENDER, SC_SPEARSQUICKEN, -1, /* 260- */ -1, -1, -1, -1, -1, -1, -1, -1, SC_STEELBODY, SC_BLADESTOP_WAIT, /* 270- */ SC_EXPLOSIONSPIRITS, SC_EXTREMITYFIST, -1, -1, -1, -1, SC_MAGICROD, -1, -1, -1, /* 280- */ SC_FLAMELAUNCHER, SC_FROSTWEAPON, SC_LIGHTNINGLOADER, SC_SEISMICWEAPON, -1, SC_VOLCANO, SC_DELUGE, SC_VIOLENTGALE, SC_LANDPROTECTOR, -1, /* 290- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 300- */ -1, -1, -1, -1, -1, -1, SC_LULLABY, SC_RICHMANKIM, SC_ETERNALCHAOS, SC_DRUMBATTLE, /* 310- */ SC_NIBELUNGEN, SC_ROKISWEIL, SC_INTOABYSS, SC_SIEGFRIED, -1, -1, -1, SC_DISSONANCE, -1, SC_WHISTLE, /* 320- */ SC_ASSNCROS, SC_POEMBRAGI, SC_APPLEIDUN, -1, -1, SC_UGLYDANCE, -1, SC_HUMMING, SC_DONTFORGETME, SC_FORTUNE, /* 330- */ SC_SERVICE4U, SC_SELFDESTRUCTION, -1, -1, -1, -1, -1, -1, -1, -1, /* 340- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 350- */ -1, -1, -1, -1, -1, SC_AURABLADE, SC_PARRYING, SC_CONCENTRATION, SC_TENSIONRELAX, SC_BERSERK, /* 360- */ SC_BERSERK, SC_ASSUMPTIO, SC_BASILICA, -1, -1, -1, SC_MAGICPOWER, -1, -1, SC_GOSPEL, /* 370- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 380- */ SC_TRUESIGHT, -1, -1, SC_WINDWALK, SC_MELTDOWN, -1, -1, SC_CARTBOOST, -1, SC_CHASEWALK, /* 390- */ SC_REJECTSWORD, -1, -1, -1, -1, -1, SC_MARIONETTE, -1, SC_HEADCRUSH, SC_JOINTBEAT, /* 400 */ -1, -1, SC_MINDBREAKER, SC_MEMORIZE, SC_FOGWALL, SC_SPIDERWEB, -1, -1, -1, -1, /* 410- */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; struct skill_name_db skill_names[] = { {AC_CHARGEARROW, "CHARGEARROW", "Charge_Arrow"}, {AC_CONCENTRATION, "CONCENTRATION", "Improve_Concentration"}, {AC_DOUBLE, "DOUBLE", "Double_Strafe"}, {AC_MAKINGARROW, "MAKINGARROW", "Arrow_Creation"}, {AC_OWL, "OWL", "Owl's_Eye"}, {AC_SHOWER, "SHOWER", "Arrow_Shower"}, {AC_VULTURE, "VULTURE", "Vulture's_Eye"}, {ALL_RESURRECTION, "RESURRECTION", "Resurrection"}, {AL_ANGELUS, "ANGELUS", "Angelus"}, {AL_BLESSING, "BLESSING", "Blessing"}, {AL_CRUCIS, "CRUCIS", "Signum_Crusis"}, {AL_CURE, "CURE", "Cure"}, {AL_DECAGI, "DECAGI", "Decrease_AGI"}, {AL_DEMONBANE, "DEMONBANE", "Demon_Bane"}, {AL_DP, "DP", "Divine_Protection"}, {AL_HEAL, "HEAL", "Heal"}, {AL_HOLYLIGHT, "HOLYLIGHT", "Holy_Light"}, {AL_HOLYWATER, "HOLYWATER", "Aqua_Benedicta"}, {AL_INCAGI, "INCAGI", "Increase_AGI"}, {AL_PNEUMA, "PNEUMA", "Pneuma"}, {AL_RUWACH, "RUWACH", "Ruwach"}, {AL_TELEPORT, "TELEPORT", "Teleport"}, {AL_WARP, "WARP", "Warp_Portal"}, {AM_ACIDTERROR, "ACIDTERROR", "Acid_Terror"}, {AM_AXEMASTERY, "AXEMASTERY", "Axe_Mastery"}, {AM_BERSERKPITCHER, "BERSERKPITCHER", "Berserk Pitcher"}, {AM_BIOETHICS, "BIOETHICS", "Bioethics"}, {AM_BIOTECHNOLOGY, "BIOTECHNOLOGY", "Biotechnology"}, {AM_CALLHOMUN, "CALLHOMUN", "Call_Homunculus"}, {AM_CANNIBALIZE, "CANNIBALIZE", "Bio_Cannibalize"}, {AM_CP_ARMOR, "ARMOR", "Chemical_Protection_Armor"}, {AM_CP_HELM, "HELM", "Chemical_Protection_Helm"}, {AM_CP_SHIELD, "SHIELD", "Chemical_Protection_Shield"}, {AM_CP_WEAPON, "WEAPON", "Chemical_Protection_Weapon"}, {AM_CREATECREATURE, "CREATECREATURE", "Life_Creation"}, {AM_CULTIVATION, "CULTIVATION", "Cultivation"}, {AM_DEMONSTRATION, "DEMONSTRATION", "Demonstration"}, {AM_DRILLMASTER, "DRILLMASTER", "Drillmaster"}, {AM_FLAMECONTROL, "FLAMECONTROL", "Flame_Control"}, {AM_HEALHOMUN, "HEALHOMUN", "Heal_Homunculus"}, {AM_LEARNINGPOTION, "LEARNINGPOTION", "AM_LEARNINGPOTION"}, {AM_PHARMACY, "PHARMACY", "Pharmacy"}, {AM_POTIONPITCHER, "POTIONPITCHER", "Potion_Pitcher"}, {AM_REST, "REST", "Sabbath"}, {AM_RESURRECTHOMUN, "RESURRECTHOMUN", "Ressurect_Homunculus"}, {AM_SPHEREMINE, "SPHEREMINE", "Sphere_Mine"}, {ASC_BREAKER, "BREAKER", "Breaker"}, {ASC_CDP, "CDP", "Create_Deadly_Poison"}, {ASC_EDP, "EDP", "Deadly_Poison_Enchantment"}, {ASC_HALLUCINATION, "HALLUCINATION", "Hallucination_Walk"}, {ASC_KATAR, "KATAR", "Advanced_Katar_Mastery"}, {ASC_METEORASSAULT, "METEORASSAULT", "Meteor_Assault"}, {AS_CLOAKING, "CLOAKING", "Cloaking"}, {AS_ENCHANTPOISON, "ENCHANTPOISON", "Enchant_Poison"}, {AS_GRIMTOOTH, "GRIMTOOTH", "Grimtooth"}, {AS_KATAR, "KATAR", "Katar_Mastery"}, {AS_LEFT, "LEFT", "Lefthand_Mastery"}, {AS_POISONREACT, "POISONREACT", "Poison_React"}, {AS_RIGHT, "RIGHT", "Righthand_Mastery"}, {AS_SONICBLOW, "SONICBLOW", "Sonic_Blow"}, {AS_SPLASHER, "SPLASHER", "Venom_Splasher"}, {AS_VENOMDUST, "VENOMDUST", "Venom_Dust"}, {BA_APPLEIDUN, "APPLEIDUN", "Apple_of_Idun"}, {BA_ASSASSINCROSS, "ASSASSINCROSS", "Assassin_Cross"}, {BA_DISSONANCE, "DISSONANCE", "Dissonance"}, {BA_FROSTJOKE, "FROSTJOKE", "Dumb_Joke"}, {BA_MUSICALLESSON, "MUSICALLESSON", "Musical_Lesson"}, {BA_MUSICALSTRIKE, "MUSICALSTRIKE", "Musical_Strike"}, {BA_POEMBRAGI, "POEMBRAGI", "Poem_of_Bragi"}, {BA_WHISTLE, "WHISTLE", "Whistle"}, {BD_ADAPTATION, "ADAPTATION", "Adaption"}, {BD_DRUMBATTLEFIELD, "DRUMBATTLEFIELD", "Drumb_BattleField"}, {BD_ENCORE, "ENCORE", "Encore"}, {BD_ETERNALCHAOS, "ETERNALCHAOS", "Eternal_Chaos"}, {BD_INTOABYSS, "INTOABYSS", "Into_the_Abyss"}, {BD_LULLABY, "LULLABY", "Lullaby"}, {BD_RAGNAROK, "RAGNAROK", "Ragnarok"}, {BD_RICHMANKIM, "RICHMANKIM", "Rich_Mankim"}, {BD_RINGNIBELUNGEN, "RINGNIBELUNGEN", "Ring_of_Nibelugen"}, {BD_ROKISWEIL, "ROKISWEIL", "Loki's_Wail"}, {BD_SIEGFRIED, "SIEGFRIED", "Invulnerable_Siegfried"}, {BS_ADRENALINE, "ADRENALINE", "Adrenaline_Rush"}, {BS_ADRENALINE2, "ADRENALINE2", "Adrenaline Rush 2"}, {BS_AXE, "AXE", "Smith_Axe"}, {BS_DAGGER, "DAGGER", "Smith_Dagger"}, {BS_ENCHANTEDSTONE, "ENCHANTEDSTONE", "Enchantedstone_Craft"}, {BS_FINDINGORE, "FINDINGORE", "Ore_Discovery"}, {BS_HAMMERFALL, "HAMMERFALL", "Hammer_Fall"}, {BS_HILTBINDING, "HILTBINDING", "Hilt_Binding"}, {BS_IRON, "IRON", "Iron_Tempering"}, {BS_KNUCKLE, "KNUCKLE", "Smith_Knucklebrace"}, {BS_MACE, "MACE", "Smith_Mace"}, {BS_MAXIMIZE, "MAXIMIZE", "Power_Maximize"}, {BS_ORIDEOCON, "ORIDEOCON", "Orideocon_Research"}, {BS_OVERTHRUST, "OVERTHRUST", "Power-Thrust"}, {BS_REPAIRWEAPON, "REPAIRWEAPON", "Weapon_Repair"}, {BS_SKINTEMPER, "SKINTEMPER", "Skin_Tempering"}, {BS_SPEAR, "SPEAR", "Smith_Spear"}, {BS_STEEL, "STEEL", "Steel_Tempering"}, {BS_SWORD, "SWORD", "Smith_Sword"}, {BS_TWOHANDSWORD, "TWOHANDSWORD", "Smith_Two-handed_Sword"}, {BS_WEAPONPERFECT, "WEAPONPERFECT", "Weapon_Perfection"}, {BS_WEAPONRESEARCH, "WEAPONRESEARCH", "Weaponry_Research"}, {CG_ARROWVULCAN, "ARROWVULCAN", "Vulcan_Arrow"}, {CG_MARIONETTE, "MARIONETTE", "Marionette_Control"}, {CG_MOONLIT, "MOONLIT", "Moonlight_Petals"}, {CH_CHAINCRUSH, "CHAINCRUSH", "Chain_Crush_Combo"}, {CH_PALMSTRIKE, "PALMSTRIKE", "Palm_Push_Strike"}, {CH_SOULCOLLECT, "SOULCOLLECT", "Collect_Soul"}, {CH_TIGERFIST, "TIGERFIST", "Tiger_Knuckle_Fist"}, {CR_ALCHEMY, "ALCHEMY", "Alchemy"}, {CR_AUTOGUARD, "AUTOGUARD", "Guard"}, {CR_DEFENDER, "DEFENDER", "Defender"}, {CR_DEVOTION, "DEVOTION", "Sacrifice"}, {CR_GRANDCROSS, "GRANDCROSS", "Grand_Cross"}, {CR_HOLYCROSS, "HOLYCROSS", "Holy_Cross"}, {CR_PROVIDENCE, "PROVIDENCE", "Providence"}, {CR_REFLECTSHIELD, "REFLECTSHIELD", "Shield_Reflect"}, {CR_SHIELDBOOMERANG, "SHIELDBOOMERANG", "Shield_Boomerang"}, {CR_SHIELDCHARGE, "SHIELDCHARGE", "Shield_Charge"}, {CR_SPEARQUICKEN, "SPEARQUICKEN", "Spear_Quicken"}, {CR_SYNTHESISPOTION, "SYNTHESISPOTION", "Potion_Synthesis"}, {CR_TRUST, "TRUST", "Faith"}, {DC_DANCINGLESSON, "DANCINGLESSON", "Dancing_Lesson"}, {DC_DONTFORGETME, "DONTFORGETME", "Don't_Forget_Me"}, {DC_FORTUNEKISS, "FORTUNEKISS", "Fortune_Kiss"}, {DC_HUMMING, "HUMMING", "Humming"}, {DC_SCREAM, "SCREAM", "Scream"}, {DC_SERVICEFORYOU, "SERVICEFORYOU", "Prostitute"}, {DC_THROWARROW, "THROWARROW", "Throw_Arrow"}, {DC_UGLYDANCE, "UGLYDANCE", "Ugly_Dance"}, {HP_ASSUMPTIO, "ASSUMPTIO", "Assumptio"}, {HP_BASILICA, "BASILICA", "Basilica"}, {HP_MEDITATIO, "MEDITATIO", "Meditation"}, {HT_ANKLESNARE, "ANKLESNARE", "Ankle_Snare"}, {HT_BEASTBANE, "BEASTBANE", "Beast_Bane"}, {HT_BLASTMINE, "BLASTMINE", "Blast_Mine"}, {HT_BLITZBEAT, "BLITZBEAT", "Blitz_Beat"}, {HT_CLAYMORETRAP, "CLAYMORETRAP", "Claymore_Trap"}, {HT_DETECTING, "DETECTING", "Detect"}, {HT_FALCON, "FALCON", "Falconry_Mastery"}, {HT_FLASHER, "FLASHER", "Flasher"}, {HT_FREEZINGTRAP, "FREEZINGTRAP", "Freezing_Trap"}, {HT_LANDMINE, "LANDMINE", "Land_Mine"}, {HT_REMOVETRAP, "REMOVETRAP", "Remove_Trap"}, {HT_SANDMAN, "SANDMAN", "Sandman"}, {HT_SHOCKWAVE, "SHOCKWAVE", "Shockwave_Trap"}, {HT_SKIDTRAP, "SKIDTRAP", "Skid_Trap"}, {HT_SPRINGTRAP, "SPRINGTRAP", "Spring_Trap"}, {HT_STEELCROW, "STEELCROW", "Steel_Crow"}, {HT_TALKIEBOX, "TALKIEBOX", "Talkie_Box"}, {HW_MAGICCRASHER, "MAGICCRASHER", "Magic_Crasher"}, {HW_MAGICPOWER, "MAGICPOWER", "Magic_Power"}, {HW_NAPALMVULCAN, "NAPALMVULCAN", "Napalm_Vulcan"}, {HW_SOULDRAIN, "SOULDRAIN", "Soul_Drain"}, {KN_AUTOCOUNTER, "AUTOCOUNTER", "Counter_Attack"}, {KN_BOWLINGBASH, "BOWLINGBASH", "Bowling_Bash"}, {KN_BRANDISHSPEAR, "BRANDISHSPEAR", "Brandish_Spear"}, {KN_CAVALIERMASTERY, "CAVALIERMASTERY", "Cavalier_Mastery"}, {KN_PIERCE, "PIERCE", "Pierce"}, {KN_RIDING, "RIDING", "Peco_Peco_Ride"}, {KN_SPEARBOOMERANG, "SPEARBOOMERANG", "Spear_Boomerang"}, {KN_SPEARMASTERY, "SPEARMASTERY", "Spear_Mastery"}, {KN_SPEARSTAB, "SPEARSTAB", "Spear_Stab"}, {KN_TWOHANDQUICKEN, "TWOHANDQUICKEN", "Twohand_Quicken"}, {LK_AURABLADE, "AURABLADE", "Aura_Blade"}, {LK_BERSERK, "BERSERK", "Berserk"}, {LK_CONCENTRATION, "CONCENTRATION", "Concentration"}, {LK_FURY, "FURY", "LK_FURY"}, {LK_HEADCRUSH, "HEADCRUSH", "Head_Crusher"}, {LK_JOINTBEAT, "JOINTBEAT", "Joint_Beat"}, {LK_PARRYING, "PARRYING", "Parrying"}, {LK_SPIRALPIERCE, "SPIRALPIERCE", "Spiral_Pierce"}, {LK_TENSIONRELAX, "TENSIONRELAX", "Tension_Relax"}, {MC_CARTREVOLUTION, "CARTREVOLUTION", "Cart_Revolution"}, {MC_CHANGECART, "CHANGECART", "Change_Cart"}, {MC_DISCOUNT, "DISCOUNT", "Discount"}, {MC_IDENTIFY, "IDENTIFY", "Item_Appraisal"}, {MC_INCCARRY, "INCCARRY", "Enlarge_Weight_Limit"}, {MC_LOUD, "LOUD", "Lord_Exclamation"}, {MC_MAMMONITE, "MAMMONITE", "Mammonite"}, {MC_OVERCHARGE, "OVERCHARGE", "Overcharge"}, {MC_PUSHCART, "PUSHCART", "Pushcart"}, {MG_COLDBOLT, "COLDBOLT", "Cold_Bolt"}, {MG_ENERGYCOAT, "ENERGYCOAT", "Energy_Coat"}, {MG_FIREBALL, "FIREBALL", "Fire_Ball"}, {MG_FIREBOLT, "FIREBOLT", "Fire_Bolt"}, {MG_FIREWALL, "FIREWALL", "Fire_Wall"}, {MG_FROSTDIVER, "FROSTDIVER", "Frost_Diver"}, {MG_LIGHTNINGBOLT, "LIGHTNINGBOLT", "Lightening_Bolt"}, {MG_NAPALMBEAT, "NAPALMBEAT", "Napalm_Beat"}, {MG_SAFETYWALL, "SAFETYWALL", "Safety_Wall"}, {MG_SIGHT, "SIGHT", "Sight"}, {MG_SOULSTRIKE, "SOULSTRIKE", "Soul_Strike"}, {MG_SRECOVERY, "SRECOVERY", "Increase_SP_Recovery"}, {MG_STONECURSE, "STONECURSE", "Stone_Curse"}, {MG_THUNDERSTORM, "THUNDERSTORM", "Thunderstorm"}, {MO_ABSORBSPIRITS, "ABSORBSPIRITS", "Absorb_Spirits"}, {MO_BLADESTOP, "BLADESTOP", "Blade_Stop"}, {MO_BODYRELOCATION, "BODYRELOCATION", "Body_Relocation"}, {MO_CALLSPIRITS, "CALLSPIRITS", "Call_Spirits"}, {MO_CHAINCOMBO, "CHAINCOMBO", "Chain_Combo"}, {MO_COMBOFINISH, "COMBOFINISH", "Combo_Finish"}, {MO_DODGE, "DODGE", "Dodge"}, {MO_EXPLOSIONSPIRITS, "EXPLOSIONSPIRITS", "Explosion_Spirits"}, {MO_EXTREMITYFIST, "EXTREMITYFIST", "Extremity_Fist"}, {MO_FINGEROFFENSIVE, "FINGEROFFENSIVE", "Finger_Offensive"}, {MO_INVESTIGATE, "INVESTIGATE", "Investigate"}, {MO_IRONHAND, "IRONHAND", "Iron_Hand"}, {MO_SPIRITSRECOVERY, "SPIRITSRECOVERY", "Spirit_Recovery"}, {MO_STEELBODY, "STEELBODY", "Steel_Body"}, {MO_TRIPLEATTACK, "TRIPLEATTACK", "Triple_Blows"}, {NPC_ATTRICHANGE, "ATTRICHANGE", "NPC_ATTRICHANGE"}, {NPC_BARRIER, "BARRIER", "NPC_BARRIER"}, {NPC_BLINDATTACK, "BLINDATTACK", "NPC_BLINDATTACK"}, {NPC_BLOODDRAIN, "BLOODDRAIN", "NPC_BLOODDRAIN"}, {NPC_CHANGEDARKNESS, "CHANGEDARKNESS", "NPC_CHANGEDARKNESS"}, {NPC_CHANGEFIRE, "CHANGEFIRE", "NPC_CHANGEFIRE"}, {NPC_CHANGEGROUND, "CHANGEGROUND", "NPC_CHANGEGROUND"}, {NPC_CHANGEHOLY, "CHANGEHOLY", "NPC_CHANGEHOLY"}, {NPC_CHANGEPOISON, "CHANGEPOISON", "NPC_CHANGEPOISON"}, {NPC_CHANGETELEKINESIS, "CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS"}, {NPC_CHANGEWATER, "CHANGEWATER", "NPC_CHANGEWATER"}, {NPC_CHANGEWIND, "CHANGEWIND", "NPC_CHANGEWIND"}, {NPC_COMBOATTACK, "COMBOATTACK", "NPC_COMBOATTACK"}, {NPC_CRITICALSLASH, "CRITICALSLASH", "NPC_CRITICALSLASH"}, {NPC_CURSEATTACK, "CURSEATTACK", "NPC_CURSEATTACK"}, {NPC_DARKBLESSING, "DARKBLESSING", "NPC_DARKBLESSING"}, {NPC_DARKBREATH, "DARKBREATH", "NPC_DARKBREATH"}, {NPC_DARKCROSS, "DARKCROSS", "NPC_DARKCROSS"}, {NPC_DARKNESSATTACK, "DARKNESSATTACK", "NPC_DARKNESSATTACK"}, {NPC_DEFENDER, "DEFENDER", "NPC_DEFENDER"}, {NPC_EMOTION, "EMOTION", "NPC_EMOTION"}, {NPC_ENERGYDRAIN, "ENERGYDRAIN", "NPC_ENERGYDRAIN"}, {NPC_FIREATTACK, "FIREATTACK", "NPC_FIREATTACK"}, {NPC_GROUNDATTACK, "GROUNDATTACK", "NPC_GROUNDATTACK"}, {NPC_GUIDEDATTACK, "GUIDEDATTACK", "NPC_GUIDEDATTACK"}, {NPC_HALLUCINATION, "HALLUCINATION", "NPC_HALLUCINATION"}, {NPC_HOLYATTACK, "HOLYATTACK", "NPC_HOLYATTACK"}, {NPC_KEEPING, "KEEPING", "NPC_KEEPING"}, {NPC_LICK, "LICK", "NPC_LICK"}, {NPC_MAGICALATTACK, "MAGICALATTACK", "NPC_MAGICALATTACK"}, {NPC_MENTALBREAKER, "MENTALBREAKER", "NPC_MENTALBREAKER"}, {NPC_METAMORPHOSIS, "METAMORPHOSIS", "NPC_METAMORPHOSIS"}, {NPC_PETRIFYATTACK, "PETRIFYATTACK", "NPC_PETRIFYATTACK"}, {NPC_PIERCINGATT, "PIERCINGATT", "NPC_PIERCINGATT"}, {NPC_POISON, "POISON", "NPC_POISON"}, {NPC_POISONATTACK, "POISONATTACK", "NPC_POISONATTACK"}, {NPC_RANDOMATTACK, "RANDOMATTACK", "NPC_RANDOMATTACK"}, {NPC_RANGEATTACK, "RANGEATTACK", "NPC_RANGEATTACK"}, {NPC_REBIRTH, "REBIRTH", "NPC_REBIRTH"}, {NPC_SELFDESTRUCTION, "SELFDESTRUCTION", "Kabooooom!"}, {NPC_SELFDESTRUCTION2, "SELFDESTRUCTION2", "NPC_SELFDESTRUCTION2"}, {NPC_SILENCEATTACK, "SILENCEATTACK", "NPC_SILENCEATTACK"}, {NPC_SLEEPATTACK, "SLEEPATTACK", "NPC_SLEEPATTACK"}, {NPC_SMOKING, "SMOKING", "NPC_SMOKING"}, {NPC_SPLASHATTACK, "SPLASHATTACK", "NPC_SPLASHATTACK"}, {NPC_STUNATTACK, "STUNATTACK", "NPC_STUNATTACK"}, {NPC_SUICIDE, "SUICIDE", "NPC_SUICIDE"}, {NPC_SUMMONMONSTER, "SUMMONMONSTER", "NPC_SUMMONMONSTER"}, {NPC_SUMMONSLAVE, "SUMMONSLAVE", "NPC_SUMMONSLAVE"}, {NPC_TELEKINESISATTACK, "TELEKINESISATTACK", "NPC_TELEKINESISATTACK"}, {NPC_TRANSFORMATION, "TRANSFORMATION", "NPC_TRANSFORMATION"}, {NPC_WATERATTACK, "WATERATTACK", "NPC_WATERATTACK"}, {NPC_WINDATTACK, "WINDATTACK", "NPC_WINDATTACK"}, {NV_EMOTE, "EMOTE", "Emote_Skill"}, {NV_TRADE, "TRADE", "Trade_Skill"}, {NV_PARTY, "PARTY", "Party_Skill"}, {NV_FIRSTAID, "FIRSTAID", "First Aid"}, {NV_TRICKDEAD, "TRICKDEAD", "Play_Dead"}, {PA_GOSPEL, "GOSPEL", "Gospel"}, {PA_PRESSURE, "PRESSURE", "Pressure"}, {PA_SACRIFICE, "SACRIFICE", "Sacrificial_Ritual"}, {PF_FOGWALL, "FOGWALL", "Wall_of_Fog"}, {PF_HPCONVERSION, "HPCONVERSION", "Health_Conversion"}, {PF_MEMORIZE, "MEMORIZE", "Memorize"}, {PF_MINDBREAKER, "MINDBREAKER", "Mind_Breaker"}, {PF_SOULBURN, "SOULBURN", "Soul_Burn"}, {PF_SOULCHANGE, "SOULCHANGE", "Soul_Change"}, {PF_SPIDERWEB, "SPIDERWEB", "Spider_Web"}, {PR_ASPERSIO, "ASPERSIO", "Aspersio"}, {PR_BENEDICTIO, "BENEDICTIO", "B.S_Sacramenti"}, {PR_GLORIA, "GLORIA", "Gloria"}, {PR_IMPOSITIO, "IMPOSITIO", "Impositio_Manus"}, {PR_KYRIE, "KYRIE", "Kyrie_Eleison"}, {PR_LEXAETERNA, "LEXAETERNA", "Lex_Aeterna"}, {PR_LEXDIVINA, "LEXDIVINA", "Lex_Divina"}, {PR_MACEMASTERY, "MACEMASTERY", "Mace_Mastery"}, {PR_MAGNIFICAT, "MAGNIFICAT", "Magnificat"}, {PR_MAGNUS, "MAGNUS", "Magnus_Exorcismus"}, {PR_SANCTUARY, "SANCTUARY", "Santuary"}, {PR_SLOWPOISON, "SLOWPOISON", "Slow_Poison"}, {PR_STRECOVERY, "STRECOVERY", "Status_Recovery"}, {PR_SUFFRAGIUM, "SUFFRAGIUM", "Suffragium"}, {PR_TURNUNDEAD, "TURNUNDEAD", "Turn_Undead"}, {RG_BACKSTAP, "BACKSTAP", "Back_Stab"}, {RG_CLEANER, "CLEANER", "Remover"}, {RG_COMPULSION, "COMPULSION", "Compulsion_Discount"}, {RG_FLAGGRAFFITI, "FLAGGRAFFITI", "Flag_Graffity"}, {RG_GANGSTER, "GANGSTER", "Gangster's_Paradise"}, {RG_GRAFFITI, "GRAFFITI", "Graffiti"}, {RG_INTIMIDATE, "INTIMIDATE", "Intimidate"}, {RG_PLAGIARISM, "PLAGIARISM", "Plagiarism"}, {RG_RAID, "RAID", "Raid"}, {RG_SNATCHER, "SNATCHER", "Snatcher"}, {RG_STEALCOIN, "STEALCOIN", "Steal_Coin"}, {RG_STRIPARMOR, "STRIPARMOR", "Strip_Armor"}, {RG_STRIPHELM, "STRIPHELM", "Strip_Helm"}, {RG_STRIPSHIELD, "STRIPSHIELD", "Strip_Shield"}, {RG_STRIPWEAPON, "STRIPWEAPON", "Strip_Weapon"}, {RG_TUNNELDRIVE, "TUNNELDRIVE", "Tunnel_Drive"}, {SA_ABRACADABRA, "ABRACADABRA", "Hocus-pocus"}, {SA_ADVANCEDBOOK, "ADVANCEDBOOK", "Advanced_Book"}, {SA_AUTOSPELL, "AUTOSPELL", "Auto_Cast"}, {SA_CASTCANCEL, "CASTCANCEL", "Cast_Cancel"}, {SA_CLASSCHANGE, "CLASSCHANGE", "Class_Change"}, {SA_COMA, "COMA", "Coma"}, {SA_DEATH, "DEATH", "Death"}, {SA_DELUGE, "DELUGE", "Deluge"}, {SA_DISPELL, "DISPELL", "Dispel"}, {SA_DRAGONOLOGY, "DRAGONOLOGY", "Dragonology"}, {SA_FLAMELAUNCHER, "FLAMELAUNCHER", "Flame_Launcher"}, {SA_FORTUNE, "FORTUNE", "Fortune"}, {SA_FREECAST, "FREECAST", "Cast_Freedom"}, {SA_FROSTWEAPON, "FROSTWEAPON", "Frost_Weapon"}, {SA_FULLRECOVERY, "FULLRECOVERY", "Full_Recovery"}, {SA_GRAVITY, "GRAVITY", "Gravity"}, {SA_INSTANTDEATH, "INSTANTDEATH", "Instant_Death"}, {SA_LANDPROTECTOR, "LANDPROTECTOR", "Land_Protector"}, {SA_LEVELUP, "LEVELUP", "Level_Up"}, {SA_LIGHTNINGLOADER, "LIGHTNINGLOADER", "Lightning_Loader"}, {SA_MAGICROD, "MAGICROD", "Magic_Rod"}, {SA_MONOCELL, "MONOCELL", "Monocell"}, {SA_QUESTION, "QUESTION", "Question?"}, {SA_REVERSEORCISH, "REVERSEORCISH", "Reverse_Orcish"}, {SA_SEISMICWEAPON, "SEISMICWEAPON", "Seismic_Weapon"}, {SA_SPELLBREAKER, "SPELLBREAKER", "Break_Spell"}, {SA_SUMMONMONSTER, "SUMMONMONSTER", "Summon_Monster"}, {SA_VIOLENTGALE, "VIOLENTGALE", "Violent_Gale"}, {SA_VOLCANO, "VOLCANO", "Volcano"}, {SG_DEVIL, "DEVIL", "Devil"}, {SG_FEEL, "FEEL", "Feel"}, {SG_FRIEND, "FRIEND", "Friend"}, {SG_FUSION, "FUSION", "Fusion"}, {SG_HATE, "HATE", "Hate"}, {SG_KNOWLEDGE, "KNOWLEDGE", "Knowledge"}, {SG_MOON_ANGER, "ANGER", "Moon Anger"}, {SG_MOON_BLESS, "BLESS", "Moon Bless"}, {SG_MOON_COMFORT, "COMFORT", "Moon Comfort"}, {SG_MOON_WARM, "WARM", "Moon Warm"}, {SG_STAR_ANGER, "ANGER", "Star Anger"}, {SG_STAR_BLESS, "BLESS", "Star Bless"}, {SG_STAR_COMFORT, "COMFORT", "Star Comfort"}, {SG_STAR_WARM, "WARM", "Star Warm"}, {SG_SUN_ANGER, "ANGER", "Sun Anger"}, {SG_SUN_BLESS, "BLESS", "Sun Bless"}, {SG_SUN_COMFORT, "COMFORT", "Sun Comfort"}, {SG_SUN_WARM, "WARM", "Sun Warm"}, {SL_ALCHEMIST, "ALCHEMIST", "Alchemist"}, {SL_ASSASIN, "ASSASIN", "Assasin"}, {SL_BARDDANCER, "BARDDANCER", "Bard Dancer"}, {SL_BLACKSMITH, "BLACKSMITH", "Black Smith"}, {SL_CRUSADER, "CRUSADER", "Crusader"}, {SL_HUNTER, "HUNTER", "Hunter"}, {SL_KAAHI, "KAAHI", "Kaahi"}, {SL_KAINA, "KAINA", "Kaina"}, {SL_KAITE, "KAITE", "Kaite"}, {SL_KAIZEL, "KAIZEL", "Kaizel"}, {SL_KAUPE, "KAUPE", "Kaupe"}, {SL_KNIGHT, "KNIGHT", "Knight"}, {SL_MONK, "MONK", "Monk"}, {SL_PRIEST, "PRIEST", "Priest"}, {SL_ROGUE, "ROGUE", "Rogue"}, {SL_SAGE, "SAGE", "Sage"}, {SL_SKA, "SKA", "SKA"}, {SL_SKE, "SKE", "SKE"}, {SL_SMA, "SMA", "SMA"}, {SL_SOULLINKER, "SOULLINKER", "Soul Linker"}, {SL_STAR, "STAR", "Star"}, {SL_STIN, "STIN", "Stin"}, {SL_STUN, "STUN", "Stun"}, {SL_SUPERNOVICE, "SUPERNOVICE", "Super Novice"}, {SL_SWOO, "SWOO", "Swoo"}, {SL_WIZARD, "WIZARD", "Wizard"}, {SM_AUTOBERSERK, "AUTOBERSERK", "Auto_Berserk"}, {SM_BASH, "BASH", "Bash"}, {SM_ENDURE, "ENDURE", "Endure"}, {SM_FATALBLOW, "FATALBLOW", "Attack_Weak_Point"}, {SM_MAGNUM, "MAGNUM", "Magnum_Break"}, {SM_MOVINGRECOVERY, "MOVINGRECOVERY", "Moving_HP_Recovery"}, {SM_PROVOKE, "PROVOKE", "Provoke"}, {SM_RECOVERY, "RECOVERY", "Increase_HP_Recovery"}, {SM_SWORD, "SWORD", "Sword_Mastery"}, {SM_TWOHAND, "TWOHAND", "Two-Handed_Sword_Mastery"}, {SN_FALCONASSAULT, "FALCONASSAULT", "Falcon_Assault"}, {SN_SHARPSHOOTING, "SHARPSHOOTING", "Sharpshooting"}, {SN_SIGHT, "SIGHT", "True_Sight"}, {SN_WINDWALK, "WINDWALK", "Wind_Walk"}, {ST_CHASEWALK, "CHASEWALK", "Chase_Walk"}, {ST_REJECTSWORD, "REJECTSWORD", "Reject_Sword"}, {ST_STEALBACKPACK, "STEALBACKPACK", "Steal_Backpack"}, {TF_BACKSLIDING, "BACKSLIDING", "Back_Sliding"}, {TF_DETOXIFY, "DETOXIFY", "Detoxify"}, {TF_DOUBLE, "DOUBLE", "Double_Attack"}, {TF_HIDING, "HIDING", "Hiding"}, {TF_MISS, "MISS", "Improve_Dodge"}, {TF_PICKSTONE, "PICKSTONE", "Take_Stone"}, {TF_POISON, "POISON", "Envenom"}, {TF_SPRINKLESAND, "SPRINKLESAND", "Throw_Sand"}, {TF_STEAL, "STEAL", "Steal"}, {TF_THROWSTONE, "THROWSTONE", "Throw_Stone"}, {TK_COUNTER, "COUNTER", "Counter"}, {TK_DODGE, "DODGE", "Dodge"}, {TK_DOWNKICK, "DOWNKICK", "Down Kick"}, {TK_HIGHJUMP, "HIGHJUMP", "High Jump"}, {TK_HPTIME, "HPTIME", "HP Time"}, {TK_JUMPKICK, "JUMPKICK", "Jump Kick"}, {TK_POWER, "POWER", "Power"}, {TK_READYCOUNTER, "READYCOUNTER", "Ready Counter"}, {TK_READYDOWN, "READYDOWN", "Ready Down"}, {TK_READYSTORM, "READYSTORM", "Ready Storm"}, {TK_READYTURN, "READYTURN", "Ready Turn"}, {TK_RUN, "RUN", "TK_RUN"}, {TK_SEVENWIND, "SEVENWIND", "Seven Wind"}, {TK_SPTIME, "SPTIME", "SP Time"}, {TK_STORMKICK, "STORMKICK", "Storm Kick"}, {TK_TURNKICK, "TURNKICK", "Turn Kick"}, {WE_BABY, "BABY", "Adopt_Baby"}, {WE_CALLBABY, "CALLBABY", "Call_Baby"}, {WE_CALLPARENT, "CALLPARENT", "Call_Parent"}, {WE_CALLPARTNER, "CALLPARTNER", "I Want to See You"}, {WE_FEMALE, "FEMALE", "I Only Look Up to You"}, {WE_MALE, "MALE", "I Will Protect You"}, {WS_CARTBOOST, "CARTBOOST", "Cart_Boost"}, {WS_CREATECOIN, "CREATECOIN", "Create_Coins"}, {WS_CREATENUGGET, "CREATENUGGET", "Create_Nuggets"}, {WS_MELTDOWN, "MELTDOWN", "Meltdown"}, {WS_SYSTEMCREATE, "SYSTEMCREATE", "Create_System_tower"}, {WZ_EARTHSPIKE, "EARTHSPIKE", "Earth_Spike"}, {WZ_ESTIMATION, "ESTIMATION", "Sense"}, {WZ_FIREIVY, "FIREIVY", "Fire_Ivy"}, {WZ_FIREPILLAR, "FIREPILLAR", "Fire_Pillar"}, {WZ_FROSTNOVA, "FROSTNOVA", "Frost_Nova"}, {WZ_HEAVENDRIVE, "HEAVENDRIVE", "Heaven's_Drive"}, {WZ_ICEWALL, "ICEWALL", "Ice_Wall"}, {WZ_JUPITEL, "JUPITEL", "Jupitel_Thunder"}, {WZ_METEOR, "METEOR", "Meteor_Storm"}, {WZ_QUAGMIRE, "QUAGMIRE", "Quagmire"}, {WZ_SIGHTRASHER, "SIGHTRASHER", "Sightrasher"}, {WZ_STORMGUST, "STORMGUST", "Storm_Gust"}, {WZ_VERMILION, "VERMILION", "Lord_of_Vermilion"}, {WZ_WATERBALL, "WATERBALL", "Water_Ball"}, {0, 0, 0} }; static const int dirx[8] = { 0, -1, -1, -1, 0, 1, 1, 1 }; static const int diry[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; static int rdamage; /* スキルデータベース */ struct skill_db skill_db[MAX_SKILL_DB]; #define UNARMED_PLAYER_DAMAGE_MIN(bl) (skill_power_bl((bl), TMW_BRAWLING) >> 4) // +50 for 200 #define UNARMED_PLAYER_DAMAGE_MAX(bl) (skill_power_bl((bl), TMW_BRAWLING)) // +200 for 200 int skill_get_hit (int id) { return skill_db[id].hit; } int skill_get_inf (int id) { return skill_db[id].inf; } int skill_get_pl (int id) { return skill_db[id].pl; } int skill_get_nk (int id) { return skill_db[id].nk; } int skill_get_max (int id) { return skill_db[id].max; } int skill_get_max_raise (int id) { return skill_db[id].max_raise; } int skill_get_range (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].range[lv - 1]; } int skill_get_hp (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].hp[lv - 1]; } int skill_get_sp (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].sp[lv - 1]; } int skill_get_zeny (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].zeny[lv - 1]; } int skill_get_num (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].num[lv - 1]; } int skill_get_cast (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].cast[lv - 1]; } int skill_get_delay (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].delay[lv - 1]; } int skill_get_time (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].upkeep_time[lv - 1]; } int skill_get_time2 (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].upkeep_time2[lv - 1]; } int skill_get_castdef (int id) { return skill_db[id].cast_def_rate; } int skill_get_weapontype (int id) { return skill_db[id].weapon; } int skill_get_inf2 (int id) { return skill_db[id].inf2; } int skill_get_maxcount (int id) { return skill_db[id].maxcount; } int skill_get_blewcount (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].blewcount[lv - 1]; } int skill_get_mhp (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].mhp[lv - 1]; } static int skill_get_castnodex (int id, int lv) { return (lv <= 0) ? 0 : skill_db[id].castnodex[lv - 1]; } /* プロトタイプ */ struct skill_unit_group *skill_unitsetting (struct block_list *src, int skillid, int skilllv, int x, int y, int flag); int skill_check_condition (struct map_session_data *sd, int type); void skill_frostjoke_scream (struct block_list *bl, va_list ap); void skill_status_change_timer_sub (struct block_list *bl, va_list ap); void skill_attack_area (struct block_list *bl, va_list ap); void skill_clear_element_field (struct block_list *bl); void skill_landprotector (struct block_list *bl, va_list ap); void skill_trap_splash (struct block_list *bl, va_list ap); void skill_count_target (struct block_list *bl, va_list ap); // [MouseJstr] - skill ok to cast? and when? static int skillnotok (int skillid, struct map_session_data *sd) { if (sd == 0) return 0; if (pc_isGM (sd) >= 20) return 0; // gm's can do anything damn thing they want switch (skillid) { case AL_WARP: case AL_TELEPORT: case MC_IDENTIFY: return 0; // always allowed default: return (map[sd->bl.m].flag.noskill); } } 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; } /* スキルユニットIDを返す(これもデータベースに入れたいな) */ int skill_get_unit_id (int id, int flag) { switch (id) { case MG_SAFETYWALL: return 0x7e; /* セイフティウォール */ case MG_FIREWALL: return 0x7f; /* ファイアーウォール */ case AL_WARP: return (flag == 0) ? 0x81 : 0x80; /* ワープポータル */ case PR_BENEDICTIO: return 0x82; /* 聖体降福 */ case PR_SANCTUARY: return 0x83; /* サンクチュアリ */ case PR_MAGNUS: return 0x84; /* マグヌスエクソシズム */ case AL_PNEUMA: return 0x85; /* ニューマ */ case MG_THUNDERSTORM: return 0x86; /* サンダーストーム */ case WZ_HEAVENDRIVE: return 0x86; /* ヘヴンズドライブ */ case WZ_SIGHTRASHER: return 0x86; /* サイトラッシャー */ case WZ_METEOR: return 0x86; /* メテオストーム */ case WZ_VERMILION: return 0x86; /* ロードオブヴァーミリオン */ case WZ_FROSTNOVA: return 0x86; /* フロストノヴァ */ case WZ_STORMGUST: return 0x86; /* ストームガスト(とりあえずLoVと同じで処理) */ case CR_GRANDCROSS: return 0x86; /* グランドクロス */ case WZ_FIREPILLAR: return (flag == 0) ? 0x87 : 0x88; /* ファイアーピラー */ case HT_TALKIEBOX: return 0x99; /* トーキーボックス */ case WZ_ICEWALL: return 0x8d; /* アイスウォール */ case WZ_QUAGMIRE: return 0x8e; /* クァグマイア */ case HT_BLASTMINE: return 0x8f; /* ブラストマイン */ case HT_SKIDTRAP: return 0x90; /* スキッドトラップ */ case HT_ANKLESNARE: return 0x91; /* アンクルスネア */ case AS_VENOMDUST: return 0x92; /* ベノムダスト */ case HT_LANDMINE: return 0x93; /* ランドマイン */ case HT_SHOCKWAVE: return 0x94; /* ショックウェーブトラップ */ case HT_SANDMAN: return 0x95; /* サンドマン */ case HT_FLASHER: return 0x96; /* フラッシャー */ case HT_FREEZINGTRAP: return 0x97; /* フリージングトラップ */ case HT_CLAYMORETRAP: return 0x98; /* クレイモアートラップ */ case SA_VOLCANO: return 0x9a; /* ボルケーノ */ case SA_DELUGE: return 0x9b; /* デリュージ */ case SA_VIOLENTGALE: return 0x9c; /* バイオレントゲイル */ case SA_LANDPROTECTOR: return 0x9d; /* ランドプロテクター */ case BD_LULLABY: return 0x9e; /* 子守歌 */ case BD_RICHMANKIM: return 0x9f; /* ニヨルドの宴 */ case BD_ETERNALCHAOS: return 0xa0; /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: return 0xa1; /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: return 0xa2; /* ニーベルングの指輪 */ case BD_ROKISWEIL: return 0xa3; /* ロキの叫び */ case BD_INTOABYSS: return 0xa4; /* 深淵の中に */ case BD_SIEGFRIED: return 0xa5; /* 不死身のジークフリード */ case BA_DISSONANCE: return 0xa6; /* 不協和音 */ case BA_WHISTLE: return 0xa7; /* 口笛 */ case BA_ASSASSINCROSS: return 0xa8; /* 夕陽のアサシンクロス */ case BA_POEMBRAGI: return 0xa9; /* ブラギの詩 */ case BA_APPLEIDUN: return 0xaa; /* イドゥンの林檎 */ case DC_UGLYDANCE: return 0xab; /* 自分勝手なダンス */ case DC_HUMMING: return 0xac; /* ハミング */ case DC_DONTFORGETME: return 0xad; /* 私を忘れないで… */ case DC_FORTUNEKISS: return 0xae; /* 幸運のキス */ case DC_SERVICEFORYOU: return 0xaf; /* サービスフォーユー */ case RG_GRAFFITI: return 0xb0; /* グラフィティ */ case AM_DEMONSTRATION: return 0xb1; /* デモンストレーション */ case WE_CALLPARTNER: return 0xb2; /* あなたに逢いたい */ case PA_GOSPEL: return 0xb3; /* ゴスペル */ case HP_BASILICA: return 0xb4; /* バジリカ */ case PF_FOGWALL: return 0xb6; /* フォグウォール */ case PF_SPIDERWEB: return 0xb7; /* スパイダーウェッブ */ } return 0; /* * 0x89,0x8a,0x8b 表示無し * 0x9a 炎属性の詠唱みたいなエフェクト * 0x9b 水属性の詠唱みたいなエフェクト * 0x9c 風属性の詠唱みたいなエフェクト * 0x9d 白い小さなエフェクト * 0xb1 Alchemist Demonstration * 0xb2 = Pink Warp Portal * 0xb3 = Gospel For Paladin * 0xb4 = Basilica * 0xb5 = Empty * 0xb6 = Fog Wall for Professor * 0xb7 = Spider Web for Professor * 0xb8 = Empty * 0xb9 = */ } /*========================================== * スキル追加効果 *------------------------------------------ */ int skill_additional_effect (struct block_list *src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick) { /* MOB追加効果スキル用 */ const int sc[] = { SC_POISON, SC_BLIND, SC_SILENCE, SC_STAN, SC_STONE, SC_CURSE, SC_SLEEP }; const int sc2[] = { MG_STONECURSE, MG_FROSTDIVER, NPC_STUNATTACK, NPC_SLEEPATTACK, TF_POISON, NPC_CURSEATTACK, NPC_SILENCEATTACK, 0, NPC_BLINDATTACK }; struct map_session_data *sd = NULL; struct map_session_data *dstsd = NULL; struct mob_data *md = NULL; struct mob_data *dstmd = NULL; int skill, skill2; int rate, luk; int sc_def_mdef, sc_def_vit, sc_def_int, sc_def_luk; int sc_def_mdef2, sc_def_vit2, sc_def_int2, sc_def_luk2; int sc_def_phys_shield_spell; nullpo_retr (0, src); nullpo_retr (0, bl); if (skilllv < 0) return 0; if (src->type == BL_PC) { nullpo_retr (0, sd = (struct map_session_data *) src); } else if (src->type == BL_MOB) { nullpo_retr (0, md = (struct mob_data *) src); //未使用? } sc_def_phys_shield_spell = 0; if (battle_get_sc_data (bl)[SC_PHYS_SHIELD].timer != -1) sc_def_phys_shield_spell = battle_get_sc_data (bl)[SC_PHYS_SHIELD].val1; //対象の耐性 luk = battle_get_luk (bl); sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + luk / 3); sc_def_vit = 100 - (3 + battle_get_vit (bl) + luk / 3); sc_def_int = 100 - (3 + battle_get_int (bl) + luk / 3); sc_def_luk = 100 - (3 + luk); //自分の耐性 luk = battle_get_luk (src); sc_def_mdef2 = 100 - (3 + battle_get_mdef (src) + luk / 3); sc_def_vit2 = 100 - (3 + battle_get_vit (src) + luk / 3); sc_def_int2 = 100 - (3 + battle_get_int (src) + luk / 3); sc_def_luk2 = 100 - (3 + luk); if (bl->type == BL_PC) dstsd = (struct map_session_data *) bl; else if (bl->type == BL_MOB) { dstmd = (struct mob_data *) bl; //未使用? if (sc_def_mdef > 50) sc_def_mdef = 50; if (sc_def_vit > 50) sc_def_vit = 50; if (sc_def_int > 50) sc_def_int = 50; if (sc_def_luk > 50) sc_def_luk = 50; } if (sc_def_mdef < 0) sc_def_mdef = 0; if (sc_def_vit < 0) sc_def_vit = 0; if (sc_def_int < 0) sc_def_int = 0; switch (skillid) { case 0: /* 通常攻撃 */ /* 自動鷹 */ if (sd && pc_isfalcon (sd) && sd->status.weapon == 11 && (skill = pc_checkskill (sd, HT_BLITZBEAT)) > 0 && MRAND (1000) <= sd->paramc[5] * 10 / 3 + 1) { int lv = (sd->status.job_level + 9) / 10; skill_castend_damage_id (src, bl, HT_BLITZBEAT, (skill < lv) ? skill : lv, tick, 0xf00000); } // スナッチャー if (sd && sd->status.weapon != 11 && (skill = pc_checkskill (sd, RG_SNATCHER)) > 0) if ((skill * 15 + 55) + (skill2 = pc_checkskill (sd, TF_STEAL)) * 10 > MRAND (1000)) { if (pc_steal_item (sd, bl)) clif_skill_nodamage (src, bl, TF_STEAL, skill2, 1); else clif_skill_fail (sd, skillid, 0, 0); } break; case SM_BASH: /* バッシュ(急所攻撃) */ if (sd && (skill = pc_checkskill (sd, SM_FATALBLOW)) > 0) { if (MRAND (100) < 6 * (skilllv - 5) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (SM_FATALBLOW, skilllv), 0); } break; case TF_POISON: /* インベナム */ case AS_SPLASHER: /* ベナムスプラッシャー */ if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) skill_status_change_start (bl, SC_POISON, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); else { if (sd && skillid == TF_POISON) clif_skill_fail (sd, skillid, 0, 0); } break; case AS_SONICBLOW: /* ソニックブロー */ if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case HT_FREEZINGTRAP: /* フリージングトラップ */ rate = skilllv * 3 + 35; if (MRAND (100) < rate * sc_def_mdef / 100) skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case MG_FROSTDIVER: /* フロストダイバー */ case WZ_FROSTNOVA: /* フロストノヴァ */ rate = (skilllv * 3 + 35) * sc_def_mdef / 100 - (battle_get_int (bl) + battle_get_luk (bl)) / 15; rate = rate <= 5 ? 5 : rate; if (MRAND (100) < rate) skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); else if (sd) clif_skill_fail (sd, skillid, 0, 0); break; case WZ_STORMGUST: /* ストームガスト */ { struct status_change *sc_data = battle_get_sc_data (bl); if (sc_data) { sc_data[SC_FREEZE].val3++; if (sc_data[SC_FREEZE].val3 >= 3) skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); } } break; case HT_LANDMINE: /* ランドマイン */ if (MRAND (100) < (5 * skilllv + 30) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case HT_SHOCKWAVE: /* ショックウェーブトラップ */ if (map[bl->m].flag.pvp && dstsd) { dstsd->status.sp -= dstsd->status.sp * (5 + 15 * skilllv) / 100; pc_calcstatus (dstsd, 0); } break; case HT_SANDMAN: /* サンドマン */ if (MRAND (100) < (5 * skilllv + 30) * sc_def_int / 100) skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case TF_SPRINKLESAND: /* 砂まき */ if (MRAND (100) < 15 * sc_def_int / 100) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case TF_THROWSTONE: /* 石投げ */ if (MRAND (100) < 5 * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case CR_HOLYCROSS: /* ホーリークロス */ if (MRAND (100) < 3 * skilllv * sc_def_int / 100) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case CR_GRANDCROSS: /* グランドクロス */ { int race = battle_get_race (bl); if ((battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) && MRAND (100) < 100000 * sc_def_int / 100) //強制付与だが完全耐性には無効 skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); } break; case CR_SHIELDCHARGE: /* シールドチャージ */ if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case RG_RAID: /* サプライズアタック */ if (MRAND (100) < (10 + 3 * skilllv) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case BA_FROSTJOKE: if (MRAND (100) < (15 + 5 * skilllv) * sc_def_mdef / 100) skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case DC_SCREAM: if (MRAND (100) < (25 + 5 * skilllv) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case BD_LULLABY: /* 子守唄 */ if (MRAND (100) < 15 * sc_def_int / 100) skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; /* MOBの追加効果付きスキル */ case NPC_PETRIFYATTACK: if (MRAND (100) < sc_def_mdef) skill_status_change_start (bl, sc[skillid - NPC_POISON], skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case NPC_POISON: case NPC_SILENCEATTACK: case NPC_STUNATTACK: if (MRAND (100) < 50 - (sc_def_vit >> 2) - (sc_def_phys_shield_spell) + (skilllv >> 2)) skill_status_change_start (bl, sc[skillid - NPC_POISON], skilllv, 0, 0, 0, skilllv, 0); break; case NPC_CURSEATTACK: if (MRAND (100) < sc_def_luk) skill_status_change_start (bl, sc[skillid - NPC_POISON], skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case NPC_SLEEPATTACK: case NPC_BLINDATTACK: if (MRAND (100) < sc_def_int) skill_status_change_start (bl, sc[skillid - NPC_POISON], skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case NPC_MENTALBREAKER: if (dstsd) { int sp = dstsd->status.max_sp * (10 + skilllv) / 100; if (sp < 1) sp = 1; pc_heal (dstsd, 0, -sp); } break; // -- moonsoul (adding status effect chance given to wizard aoe skills meteor and vermillion) // case WZ_METEOR: if (MRAND (100) < sc_def_vit) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case WZ_VERMILION: if (MRAND (100) < sc_def_int) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; // -- moonsoul (stun ability of new champion skill tigerfist) // case CH_TIGERFIST: if (MRAND (100) < (5 + skilllv * 5) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case LK_SPIRALPIERCE: if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case ST_REJECTSWORD: /* フリージングトラップ */ if (MRAND (100) < (10 + skilllv * 5)) skill_status_change_start (bl, SC_AUTOCOUNTER, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case PF_FOGWALL: /* ホーリークロス */ if (MRAND (100) < 3 * skilllv * sc_def_int / 100) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case LK_HEADCRUSH: /* ヘッドクラッシュ */ { //条件が良く分からないので適当に int race = battle_get_race (bl); if (! (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) && MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) skill_status_change_start (bl, SC_HEADCRUSH, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); } break; case LK_JOINTBEAT: /* ジョイントビート */ //条件が良く分からないので適当に if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) skill_status_change_start (bl, SC_JOINTBEAT, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case PF_SPIDERWEB: /* スパイダーウェッブ */ { int sec = skill_get_time2 (skillid, skilllv); if (map[src->m].flag.pvp) //PvPでは拘束時間半減? sec = sec / 2; battle_stopwalking (bl, 1); skill_status_change_start (bl, SC_SPIDERWEB, skilllv, 0, 0, 0, sec, 0); } break; case ASC_METEORASSAULT: /* メテオアサルト */ if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) //状態異常は詳細が分からないので適当に skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case MO_EXTREMITYFIST: /* 阿修羅覇凰拳 */ //阿修羅を使うと5分間自然回復しないようになる skill_status_change_start (src, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; } if (sd && skillid != MC_CARTREVOLUTION && attack_type & BF_WEAPON) { /* カードによる追加効果 */ int i; int sc_def_card = 100; for (i = SC_STONE; i <= SC_BLIND; i++) { //対象に状態異常 if (i == SC_STONE || i == SC_FREEZE) sc_def_card = sc_def_mdef; else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) sc_def_card = sc_def_vit; else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) sc_def_card = sc_def_int; else if (i == SC_CURSE) sc_def_card = sc_def_luk; if (!sd->state.arrow_atk) { if (MRAND (10000) < (sd->addeff[i - SC_STONE]) * sc_def_card / 100) { if (battle_config.battle_log) printf ("PC %d skill_addeff: cardによる異常発動 %d %d\n", sd->bl.id, i, sd->addeff[i - SC_STONE]); skill_status_change_start (bl, i, 7, 0, 0, 0, (i == SC_CONFUSION) ? 10000 + 7000 : skill_get_time2 (sc2 [i - SC_STONE], 7), 0); } } else { if (MRAND (10000) < (sd->addeff[i - SC_STONE] + sd->arrow_addeff[i - SC_STONE]) * sc_def_card / 100) { if (battle_config.battle_log) printf ("PC %d skill_addeff: cardによる異常発動 %d %d\n", sd->bl.id, i, sd->addeff[i - SC_STONE]); skill_status_change_start (bl, i, 7, 0, 0, 0, (i == SC_CONFUSION) ? 10000 + 7000 : skill_get_time2 (sc2 [i - SC_STONE], 7), 0); } } //自分に状態異常 if (i == SC_STONE || i == SC_FREEZE) sc_def_card = sc_def_mdef2; else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) sc_def_card = sc_def_vit2; else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) sc_def_card = sc_def_int2; else if (i == SC_CURSE) sc_def_card = sc_def_luk2; if (!sd->state.arrow_atk) { if (MRAND (10000) < (sd->addeff2[i - SC_STONE]) * sc_def_card / 100) { if (battle_config.battle_log) printf ("PC %d skill_addeff: cardによる異常発動 %d %d\n", src->id, i, sd->addeff2[i - SC_STONE]); skill_status_change_start (src, i, 7, 0, 0, 0, (i == SC_CONFUSION) ? 10000 + 7000 : skill_get_time2 (sc2 [i - SC_STONE], 7), 0); } } else { if (MRAND (10000) < (sd->addeff2[i - SC_STONE] + sd->arrow_addeff2[i - SC_STONE]) * sc_def_card / 100) { if (battle_config.battle_log) printf ("PC %d skill_addeff: cardによる異常発動 %d %d\n", src->id, i, sd->addeff2[i - SC_STONE]); skill_status_change_start (src, i, 7, 0, 0, 0, (i == SC_CONFUSION) ? 10000 + 7000 : skill_get_time2 (sc2 [i - SC_STONE], 7), 0); } } } } return 0; } /*========================================================================= スキル攻撃吹き飛ばし処理 -------------------------------------------------------------------------*/ static int skill_blown (struct block_list *src, struct block_list *target, int count) { int dx = 0, dy = 0, nx, ny; int x = target->x, y = target->y; int ret, prev_state = MS_IDLE; int moveblock; struct map_session_data *sd = NULL; struct mob_data *md = NULL; struct skill_unit *su = NULL; nullpo_retr (0, src); nullpo_retr (0, target); if (target->type == BL_PC) { nullpo_retr (0, sd = (struct map_session_data *) target); } else if (target->type == BL_MOB) { nullpo_retr (0, md = (struct mob_data *) target); } else if (target->type == BL_SKILL) { nullpo_retr (0, su = (struct skill_unit *) target); } else return 0; if (!(count & 0x10000 && (sd || md || su))) { /* 指定なしなら位置関係から方向を求める */ dx = target->x - src->x; dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); dy = target->y - src->y; dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); } if (dx == 0 && dy == 0) { int dir = battle_get_dir (target); if (dir >= 0 && dir < 8) { dx = -dirx[dir]; dy = -diry[dir]; } } ret = path_blownpos (target->m, x, y, dx, dy, count & 0xffff); nx = ret >> 16; ny = ret & 0xffff; moveblock = (x / BLOCK_SIZE != nx / BLOCK_SIZE || y / BLOCK_SIZE != ny / BLOCK_SIZE); if (count & 0x20000) { battle_stopwalking (target, 1); if (sd) { sd->to_x = nx; sd->to_y = ny; sd->walktimer = 1; clif_walkok (sd); clif_movechar (sd); } else if (md) { md->to_x = nx; md->to_y = ny; prev_state = md->state.state; md->state.state = MS_WALK; clif_fixmobpos (md); } } else battle_stopwalking (target, 2); dx = nx - x; dy = ny - y; if (sd) /* 画面外に出たので消去 */ map_foreachinmovearea (clif_pcoutsight, target->m, x - AREA_SIZE, y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, dx, dy, 0, sd); else if (md) map_foreachinmovearea (clif_moboutsight, target->m, x - AREA_SIZE, y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, dx, dy, BL_PC, md); if (su) { skill_unit_move_unit_group (su->group, target->m, dx, dy); } else { // struct status_change *sc_data=battle_get_sc_data(target); if (moveblock) map_delblock (target); target->x = nx; target->y = ny; if (moveblock) map_addblock (target); /*ダンス中にエフェクトは移動しないらしい if(sc_data && sc_data[SC_DANCING].timer!=-1){ //対象がダンス中なのでエフェクトも移動 struct skill_unit_group *sg=(struct skill_unit_group *)sc_data[SC_DANCING].val2; if(sg) skill_unit_move_unit_group(sg,target->m,dx,dy); } */ } if (sd) { /* 画面内に入ってきたので表示 */ map_foreachinmovearea (clif_pcinsight, target->m, nx - AREA_SIZE, ny - AREA_SIZE, nx + AREA_SIZE, ny + AREA_SIZE, -dx, -dy, 0, sd); if (count & 0x20000) sd->walktimer = -1; } else if (md) { map_foreachinmovearea (clif_mobinsight, target->m, nx - AREA_SIZE, ny - AREA_SIZE, nx + AREA_SIZE, ny + AREA_SIZE, -dx, -dy, BL_PC, md); if (count & 0x20000) md->state.state = prev_state; } skill_unit_move (target, gettick (), (count & 0xffff) + 7); /* スキルユニットの判定 */ return 0; } /* * ========================================================================= * スキル攻撃効果処理まとめ * flagの説明。16進図 * 00XRTTff * ff = magicで計算に渡される) * TT = パケットのtype部分(0でデフォルト) * X = パケットのスキルLv * R = 予約(skill_area_subで使用する) *------------------------------------------------------------------------- */ int skill_attack (int attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) { struct Damage dmg; struct status_change *sc_data; int type, lv, damage; rdamage = 0; nullpo_retr (0, src); nullpo_retr (0, dsrc); nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); //何もしない判定ここから if (dsrc->m != bl->m) //対象が同じマップにいなければ何もしない return 0; if (src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL) //prevよくわからない※ return 0; if (src->type == BL_PC && pc_isdead ((struct map_session_data *) src)) //術者?がPCですでに死んでいたら何もしない return 0; if (dsrc->type == BL_PC && pc_isdead ((struct map_session_data *) dsrc)) //術者?がPCですでに死んでいたら何もしない return 0; if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) //対象がPCですでに死んでいたら何もしない return 0; if (skillnotok (skillid, (struct map_session_data *) bl)) return 0; // [MouseJstr] if (sc_data && sc_data[SC_HIDING].timer != -1) { //ハイディング状態で if (skill_get_pl (skillid) != 2) //スキルの属性が地属性でなければ何もしない return 0; } if (sc_data && sc_data[SC_TRICKDEAD].timer != -1) //死んだふり中は何もしない return 0; if (skillid == WZ_STORMGUST) { //使用スキルがストームガストで if (sc_data && sc_data[SC_FREEZE].timer != -1) //凍結状態なら何もしない return 0; } if (skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフロストノヴァで、dsrcとblが同じ場所なら何もしない return 0; if (src->type == BL_PC && ((struct map_session_data *) src)->chatID) //術者がPCでチャット中なら何もしない return 0; if (dsrc->type == BL_PC && ((struct map_session_data *) dsrc)->chatID) //術者がPCでチャット中なら何もしない return 0; if (src->type == BL_PC && bl && mob_gvmobcheck (((struct map_session_data *) src), bl) == 0) return 0; //何もしない判定ここまで type = -1; lv = (flag >> 20) & 0xf; dmg = battle_calc_attack (attack_type, src, bl, skillid, skilllv, flag & 0xff); //ダメージ計算 //マジックロッド処理ここから if (attack_type & BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 && src == dsrc) { //魔法攻撃でマジックロッド状態でsrc=dsrcなら dmg.damage = dmg.damage2 = 0; //ダメージ0 if (bl->type == BL_PC) { //対象がPCの場合 int sp = skill_get_sp (skillid, skilllv); //使用されたスキルのSPを吸収 sp = sp * sc_data[SC_MAGICROD].val2 / 100; //吸収率計算 if (skillid == WZ_WATERBALL && skilllv > 1) //ウォーターボールLv1以上 sp = sp / ((skilllv | 1) * (skilllv | 1)); //さらに計算? if (sp > 0x7fff) sp = 0x7fff; //SP多すぎの場合は理論最大値 else if (sp < 1) sp = 1; //1以下の場合は1 if (((struct map_session_data *) bl)->status.sp + sp > ((struct map_session_data *) bl)->status.max_sp) { //回復SP+現在のSPがMSPより大きい場合 sp = ((struct map_session_data *) bl)->status.max_sp - ((struct map_session_data *) bl)->status.sp; //SPをMSP-現在SPにする ((struct map_session_data *) bl)->status.sp = ((struct map_session_data *) bl)->status.max_sp; //現在のSPにMSPを代入 } else //回復SP+現在のSPがMSPより小さい場合は回復SPを加算 ((struct map_session_data *) bl)->status.sp += sp; clif_heal (((struct map_session_data *) bl)->fd, SP_SP, sp); //SP回復エフェクトの表示 ((struct map_session_data *) bl)->canact_tick = tick + skill_delayfix (bl, skill_get_delay (SA_MAGICROD, sc_data[SC_MAGICROD].val1)); // } clif_skill_nodamage (bl, bl, SA_MAGICROD, sc_data[SC_MAGICROD].val1, 1); //マジックロッドエフェクトを表示 } //マジックロッド処理ここまで damage = dmg.damage + dmg.damage2; if (lv == 15) lv = -1; if (flag & 0xff00) type = (flag & 0xff00) >> 8; if (damage <= 0 || damage < dmg.div_) //吹き飛ばし判定?※ dmg.blewcount = 0; if (skillid == CR_GRANDCROSS) { //グランドクロス if (battle_config.gx_disptype) dsrc = src; // 敵ダメージ白文字表示 if (src == bl) type = 4; // 反動はダメージモーションなし } //使用者がPCの場合の処理ここから if (src->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) src; nullpo_retr (0, sd); //連打掌(MO_CHAINCOMBO)ここから if (skillid == MO_CHAINCOMBO) { int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); //基本ディレイの計算 if (damage < battle_get_hp (bl)) { //ダメージが対象のHPより小さい場合 if (pc_checkskill (sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0) //猛龍拳(MO_COMBOFINISH)取得&気球保持時は+300ms delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 skill_status_change_start (src, SC_COMBO, MO_CHAINCOMBO, skilllv, 0, 0, delay, 0); //コンボ状態に } sd->attackabletime = sd->canmove_tick = tick + delay; clif_combo_delay (src, delay); //コンボディレイパケットの送信 } //連打掌(MO_CHAINCOMBO)ここまで //猛龍拳(MO_COMBOFINISH)ここから else if (skillid == MO_COMBOFINISH) { int delay = 700 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); if (damage < battle_get_hp (bl)) { //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms //伏虎拳(CH_TIGERFIST)取得時も+300ms if ((pc_checkskill (sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) || (pc_checkskill (sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) || (pc_checkskill (sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)) delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 skill_status_change_start (src, SC_COMBO, MO_COMBOFINISH, skilllv, 0, 0, delay, 0); //コンボ状態に } sd->attackabletime = sd->canmove_tick = tick + delay; clif_combo_delay (src, delay); //コンボディレイパケットの送信 } //猛龍拳(MO_COMBOFINISH)ここまで //伏虎拳(CH_TIGERFIST)ここから else if (skillid == CH_TIGERFIST) { int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); if (damage < battle_get_hp (bl)) { if (pc_checkskill (sd, CH_CHAINCRUSH) > 0) //連柱崩撃(CH_CHAINCRUSH)取得時は+300ms delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 skill_status_change_start (src, SC_COMBO, CH_TIGERFIST, skilllv, 0, 0, delay, 0); //コンボ状態に } sd->attackabletime = sd->canmove_tick = tick + delay; clif_combo_delay (src, delay); //コンボディレイパケットの送信 } //伏虎拳(CH_TIGERFIST)ここまで //連柱崩撃(CH_CHAINCRUSH)ここから else if (skillid == CH_CHAINCRUSH) { int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); if (damage < battle_get_hp (bl)) { //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms if (pc_checkskill (sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 skill_status_change_start (src, SC_COMBO, CH_CHAINCRUSH, skilllv, 0, 0, delay, 0); //コンボ状態に } sd->attackabletime = sd->canmove_tick = tick + delay; clif_combo_delay (src, delay); //コンボディレイパケットの送信 } //連柱崩撃(CH_CHAINCRUSH)ここまで } //使用者がPCの場合の処理ここまで //武器スキル?ここから //AppleGirl Was Here if (attack_type & BF_MAGIC && damage > 0 && src != bl && src == dsrc) { //Blah Blah if (bl->type == BL_PC) { //Blah Blah struct map_session_data *tsd = (struct map_session_data *) bl; if (tsd->magic_damage_return > 0) { //More Blah rdamage += damage * tsd->magic_damage_return / 100; if (rdamage < 1) rdamage = 1; } } } //Stop Here if (attack_type & BF_WEAPON && damage > 0 && src != bl && src == dsrc) { //武器スキル&ダメージあり&使用者と対象者が違う&src=dsrc if (dmg.flag & BF_SHORT) { //近距離攻撃時?※ if (bl->type == BL_PC) { //対象がPCの時 struct map_session_data *tsd = (struct map_session_data *) bl; nullpo_retr (0, tsd); if (tsd->short_weapon_damage_return > 0) { //近距離攻撃跳ね返し?※ rdamage += damage * tsd->short_weapon_damage_return / 100; if (rdamage < 1) rdamage = 1; } } if (sc_data && sc_data[SC_REFLECTSHIELD].timer != -1) { //リフレクトシールド時 rdamage += damage * sc_data[SC_REFLECTSHIELD].val2 / 100; //跳ね返し計算 if (rdamage < 1) rdamage = 1; } } else if (dmg.flag & BF_LONG) { //遠距離攻撃時?※ if (bl->type == BL_PC) { //対象がPCの時 struct map_session_data *tsd = (struct map_session_data *) bl; nullpo_retr (0, tsd); if (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, dmg.amotion, 0, rdamage, 1, 4, 0); } //武器スキル?ここまで switch (skillid) { case WZ_SIGHTRASHER: clif_skill_damage (src, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, (lv != 0) ? lv : skilllv, 5); break; case AS_SPLASHER: clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, -1, 5); break; case NPC_SELFDESTRUCTION: case NPC_SELFDESTRUCTION2: break; default: clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, (lv != 0) ? lv : skilllv, (skillid == 0) ? 5 : type); } if (dmg.blewcount > 0 && !map[src->m].flag.gvg) { /* 吹き飛ばし処理とそのパケット */ if (skillid == WZ_SIGHTRASHER) skill_blown (src, bl, dmg.blewcount); else skill_blown (dsrc, bl, dmg.blewcount); if (bl->type == BL_MOB) clif_fixmobpos ((struct mob_data *) bl); else clif_fixpos (bl); } map_freeblock_lock (); /* 実際にダメージ処理を行う */ if (skillid != KN_BOWLINGBASH || flag) battle_damage (src, bl, damage, 0); if (skillid == RG_INTIMIDATE && damage > 0 && !(battle_get_mode (bl) & 0x20) && !map[src->m].flag.gvg) { int s_lv = battle_get_lv (src), t_lv = battle_get_lv (bl); int rate = 50 + skilllv * 5; rate = rate + (s_lv - t_lv); if (MRAND (100) < rate) skill_addtimerskill (src, tick + 800, bl->id, 0, 0, skillid, skilllv, 0, flag); } /* if(damage > 0 && dmg.flag&BF_SKILL && bl->type==BL_PC && pc_checkskill((struct map_session_data *)bl,RG_PLAGIARISM)){ struct map_session_data *tsd = (struct map_session_data *)bl; nullpo_retr(0, tsd); if(!tsd->status.skill[skillid].id && !tsd->status.skill[skillid].id && !(skillid > NPC_PIERCINGATT && skillid < NPC_SUMMONMONSTER) ){ //既に盗んでいるスキルがあれば該当スキルを消す if (tsd->cloneskill_id && tsd->cloneskill_lv && tsd->status.skill[tsd->cloneskill_id].flag==13){ tsd->status.skill[tsd->cloneskill_id].id=0; tsd->status.skill[tsd->cloneskill_id].lv=0; tsd->status.skill[tsd->cloneskill_id].flag=0; } tsd->cloneskill_id=skillid; tsd->cloneskill_lv=skilllv; tsd->status.skill[skillid].id=skillid; tsd->status.skill[skillid].lv=(pc_checkskill(tsd,RG_PLAGIARISM) > skill_get_max(skillid))? skill_get_max(skillid):pc_checkskill(tsd,RG_PLAGIARISM); tsd->status.skill[skillid].flag=13;//cloneskill flag clif_skillinfoblock(tsd); } } */ /* ダメージがあるなら追加効果判定 */ if (bl->prev != NULL) { struct map_session_data *sd = (struct map_session_data *) bl; nullpo_retr (0, sd); if (bl->type != BL_PC || (sd && !pc_isdead (sd))) { if (damage > 0) skill_additional_effect (src, bl, skillid, skilllv, attack_type, tick); if (bl->type == BL_MOB && src != bl) /* スキル使用条件のMOBスキル */ { struct mob_data *md = (struct mob_data *) bl; nullpo_retr (0, md); if (battle_config.mob_changetarget_byskill == 1) { int target; target = md->target_id; if (src->type == BL_PC) md->target_id = src->id; mobskill_use (md, tick, MSC_SKILLUSED | (skillid << 16)); md->target_id = target; } else mobskill_use (md, tick, MSC_SKILLUSED | (skillid << 16)); } } } if (src->type == BL_PC && dmg.flag & BF_WEAPON && src != bl && src == dsrc && damage > 0) { struct map_session_data *sd = (struct map_session_data *) src; int hp = 0, sp = 0; nullpo_retr (0, sd); if (sd->hp_drain_rate && dmg.damage > 0 && MRAND (100) < sd->hp_drain_rate) { hp += (dmg.damage * sd->hp_drain_per) / 100; } if (sd->hp_drain_rate_ && dmg.damage2 > 0 && MRAND (100) < sd->hp_drain_rate_) { hp += (dmg.damage2 * sd->hp_drain_per_) / 100; } if (sd->sp_drain_rate > 0 && dmg.damage > 0 && MRAND (100) < sd->sp_drain_rate) { sp += (dmg.damage * sd->sp_drain_per) / 100; } if (sd->sp_drain_rate_ > 0 && dmg.damage2 > 0 && MRAND (100) < sd->sp_drain_rate_) { sp += (dmg.damage2 * sd->sp_drain_per_) / 100; } if (hp || sp) pc_heal (sd, hp, sp); } if ((skillid != KN_BOWLINGBASH || flag) && rdamage > 0) battle_damage (bl, src, rdamage, 0); if (attack_type & BF_WEAPON && sc_data && sc_data[SC_AUTOCOUNTER].timer != -1 && sc_data[SC_AUTOCOUNTER].val4 > 0) { if (sc_data[SC_AUTOCOUNTER].val3 == dsrc->id) battle_weapon_attack (bl, dsrc, tick, 0x8000 | sc_data[SC_AUTOCOUNTER].val1); skill_status_change_end (bl, SC_AUTOCOUNTER, -1); } map_freeblock_unlock (); return (dmg.damage + dmg.damage2); /* 与ダメを返す */ } /*========================================== * スキル範囲攻撃用(map_foreachinareaから呼ばれる) * flagについて:16進図を確認 * MSB <- 00fTffff ->LSB * T =ターゲット選択用(BCT_*) * ffff=自由に使用可能 * 0 =予約。0に固定 *------------------------------------------ */ static int skill_area_temp[8]; /* 一時変数。必要なら使う。 */ typedef int (*SkillFunc) (struct block_list *, struct block_list *, int, int, unsigned int, int); static void skill_area_sub (struct block_list *bl, va_list ap) { struct block_list *src; int skill_id, skill_lv, flag; unsigned int tick; SkillFunc func; nullpo_retv (bl); nullpo_retv (ap); if (bl->type != BL_PC && bl->type != BL_MOB && bl->type != BL_SKILL) return; src = va_arg (ap, struct block_list *); //ここではsrcの値を参照していないのでNULLチェックはしない skill_id = va_arg (ap, int); skill_lv = va_arg (ap, int); tick = va_arg (ap, unsigned int); flag = va_arg (ap, int); func = va_arg (ap, SkillFunc); if (battle_check_target (src, bl, flag) > 0) func (src, bl, skill_id, skill_lv, tick, flag); } static void skill_check_unit_range_sub (struct block_list *bl, va_list ap) { struct skill_unit *unit; int *c, x, y, range, sx[4], sy[4]; int t_range, tx[4], ty[4]; int i, r_flag, skillid; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (unit = (struct skill_unit *) bl); nullpo_retv (c = va_arg (ap, int *)); if (bl->prev == NULL || bl->type != BL_SKILL) return; if (!unit->alive) return; x = va_arg (ap, int); y = va_arg (ap, int); range = va_arg (ap, int); skillid = va_arg (ap, int); if (skillid == MG_SAFETYWALL || skillid == AL_PNEUMA) { if (unit->group->unit_id != 0x7e && unit->group->unit_id != 0x85) return; } else if (skillid == AL_WARP) { if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) && unit->group->unit_id != 0x92) return; } else if ((skillid >= HT_SKIDTRAP && skillid <= HT_CLAYMORETRAP) || skillid == HT_TALKIEBOX) { if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) && unit->group->unit_id != 0x92) return; } else if (skillid == WZ_FIREPILLAR) { if (unit->group->unit_id != 0x87) return; } else return; t_range = (unit->range != 0) ? unit->range : unit->group->range; tx[0] = tx[3] = unit->bl.x - t_range; tx[1] = tx[2] = unit->bl.x + t_range; ty[0] = ty[1] = unit->bl.y - t_range; ty[2] = ty[3] = unit->bl.y + t_range; sx[0] = sx[3] = x - range; sx[1] = sx[2] = x + range; sy[0] = sy[1] = y - range; sy[2] = sy[3] = y + range; for (i = r_flag = 0; i < 4; i++) { if (sx[i] >= tx[0] && sx[i] <= tx[1] && sy[i] >= ty[0] && sy[i] <= ty[2]) { r_flag = 1; break; } if (tx[i] >= sx[0] && tx[i] <= sx[1] && ty[i] >= sy[0] && ty[i] <= sy[2]) { r_flag = 1; break; } } if (r_flag) (*c)++; } int skill_check_unit_range (int m, int x, int y, int range, int skillid) { int c = 0; map_foreachinarea (skill_check_unit_range_sub, m, x - 10, y - 10, x + 10, y + 10, BL_SKILL, &c, x, y, range, skillid); return c; } static void skill_check_unit_range2_sub (struct block_list *bl, va_list ap) { int *c; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (c = va_arg (ap, int *)); if (bl->prev == NULL || (bl->type != BL_PC && bl->type != BL_MOB)) return; if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) return; (*c)++; } int skill_check_unit_range2 (int m, int x, int y, int range) { int c = 0; map_foreachinarea (skill_check_unit_range2_sub, m, x - range, y - range, x + range, y + range, 0, &c); return c; } /*========================================================================= * 範囲スキル使用処理小分けここから */ /* 対象の数をカウントする。(skill_area_temp[0]を初期化しておくこと) */ static int skill_area_sub_count (struct block_list *src, struct block_list *target, int skillid, int skilllv, unsigned int tick, int flag) { if (skill_area_temp[0] < 0xffff) skill_area_temp[0]++; return 0; } /*========================================== * *------------------------------------------ */ static void skill_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { struct map_session_data *sd = NULL; struct mob_data *md = NULL; struct block_list *src = map_id2bl (id), *target; struct skill_timerskill *skl = NULL; int range; nullpo_retv (src); if (src->prev == NULL) return; if (src->type == BL_PC) { nullpo_retv (sd = (struct map_session_data *) src); skl = &sd->skilltimerskill[data]; } else if (src->type == BL_MOB) { nullpo_retv (md = (struct mob_data *) src); skl = &md->skilltimerskill[data]; } else return; nullpo_retv (skl); skl->timer = -1; if (skl->target_id) { struct block_list tbl; target = map_id2bl (skl->target_id); if (skl->skill_id == RG_INTIMIDATE) { if (target == NULL) { target = &tbl; //初期化してないのにアドレス突っ込んでいいのかな? target->type = BL_NUL; target->m = src->m; target->prev = target->next = NULL; } } if (target == NULL) return; if (target->prev == NULL && skl->skill_id != RG_INTIMIDATE) return; if (src->m != target->m) return; if (sd && pc_isdead (sd)) return; if (target->type == BL_PC && pc_isdead ((struct map_session_data *) target) && skl->skill_id != RG_INTIMIDATE) return; switch (skl->skill_id) { case TF_BACKSLIDING: clif_skill_nodamage (src, src, skl->skill_id, skl->skill_lv, 1); break; case RG_INTIMIDATE: if (sd && !map[src->m].flag.noteleport) { int x, y, i, j, c; pc_randomwarp (sd, 3); for (i = 0; i < 16; i++) { j = MRAND (8); x = sd->bl.x + dirx[j]; y = sd->bl.y + diry[j]; if ((c = map_getcell (sd->bl.m, x, y)) != 1 && c != 5) break; } if (i >= 16) { x = sd->bl.x; y = sd->bl.y; } if (target->prev != NULL) { if (target->type == BL_PC && !pc_isdead ((struct map_session_data *) target)) pc_setpos ((struct map_session_data *) target, map[sd->bl.m].name, x, y, 3); else if (target->type == BL_MOB) mob_warp ((struct mob_data *) target, -1, x, y, 3); } } else if (md && !map[src->m].flag.monster_noteleport) { int x, y, i, j, c; mob_warp (md, -1, -1, -1, 3); for (i = 0; i < 16; i++) { j = MRAND (8); x = md->bl.x + dirx[j]; y = md->bl.y + diry[j]; if ((c = map_getcell (md->bl.m, x, y)) != 1 && c != 5) break; } if (i >= 16) { x = md->bl.x; y = md->bl.y; } if (target->prev != NULL) { if (target->type == BL_PC && !pc_isdead ((struct map_session_data *) target)) pc_setpos ((struct map_session_data *) target, map[md->bl.m].name, x, y, 3); else if (target->type == BL_MOB) mob_warp ((struct mob_data *) target, -1, x, y, 3); } } break; case BA_FROSTJOKE: /* 寒いジョーク */ case DC_SCREAM: /* スクリーム */ range = 15; //視界全体 map_foreachinarea (skill_frostjoke_scream, src->m, src->x - range, src->y - range, src->x + range, src->y + range, 0, src, skl->skill_id, skl->skill_lv, tick); break; default: skill_attack (skl->type, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag); break; } } else { if (src->m != skl->map) return; switch (skl->skill_id) { case WZ_METEOR: if (skl->type >= 0) { skill_unitsetting (src, skl->skill_id, skl->skill_lv, skl->type >> 16, skl->type & 0xFFFF, 0); clif_skill_poseffect (src, skl->skill_id, skl->skill_lv, skl->x, skl->y, tick); } else skill_unitsetting (src, skl->skill_id, skl->skill_lv, skl->x, skl->y, 0); break; } } } /*========================================== * *------------------------------------------ */ int skill_addtimerskill (struct block_list *src, unsigned int tick, int target, int x, int y, int skill_id, int skill_lv, int type, int flag) { int i; nullpo_retr (1, src); if (src->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) src; nullpo_retr (1, sd); for (i = 0; i < MAX_SKILLTIMERSKILL; i++) { if (sd->skilltimerskill[i].timer == -1) { sd->skilltimerskill[i].timer = add_timer (tick, skill_timer, src->id, i); sd->skilltimerskill[i].src_id = src->id; sd->skilltimerskill[i].target_id = target; sd->skilltimerskill[i].skill_id = skill_id; sd->skilltimerskill[i].skill_lv = skill_lv; sd->skilltimerskill[i].map = src->m; sd->skilltimerskill[i].x = x; sd->skilltimerskill[i].y = y; sd->skilltimerskill[i].type = type; sd->skilltimerskill[i].flag = flag; return 0; } } return 1; } else if (src->type == BL_MOB) { struct mob_data *md = (struct mob_data *) src; nullpo_retr (1, md); for (i = 0; i < MAX_MOBSKILLTIMERSKILL; i++) { if (md->skilltimerskill[i].timer == -1) { md->skilltimerskill[i].timer = add_timer (tick, skill_timer, src->id, i); md->skilltimerskill[i].src_id = src->id; md->skilltimerskill[i].target_id = target; md->skilltimerskill[i].skill_id = skill_id; md->skilltimerskill[i].skill_lv = skill_lv; md->skilltimerskill[i].map = src->m; md->skilltimerskill[i].x = x; md->skilltimerskill[i].y = y; md->skilltimerskill[i].type = type; md->skilltimerskill[i].flag = flag; return 0; } } return 1; } return 1; } /*========================================== * *------------------------------------------ */ int skill_cleartimerskill (struct block_list *src) { int i; nullpo_retr (0, src); if (src->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) src; nullpo_retr (0, sd); for (i = 0; i < MAX_SKILLTIMERSKILL; i++) { if (sd->skilltimerskill[i].timer != -1) { delete_timer (sd->skilltimerskill[i].timer, skill_timer); sd->skilltimerskill[i].timer = -1; } } } else if (src->type == BL_MOB) { struct mob_data *md = (struct mob_data *) src; nullpo_retr (0, md); for (i = 0; i < MAX_MOBSKILLTIMERSKILL; i++) { if (md->skilltimerskill[i].timer != -1) { delete_timer (md->skilltimerskill[i].timer, skill_timer); md->skilltimerskill[i].timer = -1; } } } return 0; } /* 範囲スキル使用処理小分けここまで * ------------------------------------------------------------------------- */ /*========================================== * スキル使用(詠唱完了、ID指定攻撃系) * (スパゲッティに向けて1歩前進!(ダメポ)) *------------------------------------------ */ int skill_castend_damage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) { struct map_session_data *sd = NULL; nullpo_retr (1, src); nullpo_retr (1, bl); if (src->type == BL_PC) sd = (struct map_session_data *) src; if (sd && pc_isdead (sd)) return 1; if ((skillid == WZ_SIGHTRASHER || skillid == CR_GRANDCROSS) && src != bl) bl = src; if (bl->prev == NULL) return 1; if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) return 1; map_freeblock_lock (); switch (skillid) { /* 武器攻撃系スキル */ case SM_BASH: /* バッシュ */ case MC_MAMMONITE: /* メマーナイト */ case AC_DOUBLE: /* ダブルストレイフィング */ case AS_SONICBLOW: /* ソニックブロー */ case KN_PIERCE: /* ピアース */ case KN_SPEARBOOMERANG: /* スピアブーメラン */ case TF_POISON: /* インベナム */ case TF_SPRINKLESAND: /* 砂まき */ case AC_CHARGEARROW: /* チャージアロー */ case KN_SPEARSTAB: /* スピアスタブ */ case RG_RAID: /* サプライズアタック */ case RG_INTIMIDATE: /* インティミデイト */ case BA_MUSICALSTRIKE: /* ミュージカルストライク */ case DC_THROWARROW: /* 矢撃ち */ case BA_DISSONANCE: /* 不協和音 */ case CR_HOLYCROSS: /* ホーリークロス */ case CR_SHIELDCHARGE: case CR_SHIELDBOOMERANG: /* 以下MOB専用 */ /* 単体攻撃、SP減少攻撃、遠距離攻撃、防御無視攻撃、多段攻撃 */ case NPC_PIERCINGATT: case NPC_MENTALBREAKER: case NPC_RANGEATTACK: case NPC_CRITICALSLASH: case NPC_COMBOATTACK: /* 必中攻撃、毒攻撃、暗黒攻撃、沈黙攻撃、スタン攻撃 */ case NPC_GUIDEDATTACK: case NPC_POISON: case NPC_BLINDATTACK: case NPC_SILENCEATTACK: case NPC_STUNATTACK: /* 石化攻撃、呪い攻撃、睡眠攻撃、ランダムATK攻撃 */ case NPC_PETRIFYATTACK: case NPC_CURSEATTACK: case NPC_SLEEPATTACK: case NPC_RANDOMATTACK: /* 水属性攻撃、地属性攻撃、火属性攻撃、風属性攻撃 */ case NPC_WATERATTACK: case NPC_GROUNDATTACK: case NPC_FIREATTACK: case NPC_WINDATTACK: /* 毒属性攻撃、聖属性攻撃、闇属性攻撃、念属性攻撃、SP減少攻撃 */ case NPC_POISONATTACK: case NPC_HOLYATTACK: case NPC_DARKNESSATTACK: case NPC_TELEKINESISATTACK: case LK_AURABLADE: /* オーラブレード */ case LK_SPIRALPIERCE: /* スパイラルピアース */ case LK_HEADCRUSH: /* ヘッドクラッシュ */ case LK_JOINTBEAT: /* ジョイントビート */ case PA_PRESSURE: /* プレッシャー */ case PA_SACRIFICE: /* サクリファイス */ case SN_SHARPSHOOTING: /* シャープシューティング */ case CG_ARROWVULCAN: /* アローバルカン */ case ASC_BREAKER: /* ソウルブレーカー */ case HW_MAGICCRASHER: /* マジッククラッシャー */ skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); break; case NPC_DARKBREATH: clif_emotion (src, 7); skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, flag); break; case MO_INVESTIGATE: /* 発勁 */ { struct status_change *sc_data = battle_get_sc_data (src); skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); if (sc_data[SC_BLADESTOP].timer != -1) skill_status_change_end (src, SC_BLADESTOP, -1); } break; case SN_FALCONASSAULT: /* ファルコンアサルト */ skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, flag); break; case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ { struct mob_data *md = (struct mob_data *) bl; nullpo_retr (1, md); skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); if (md->hp > 0) { skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); if (bl->type == BL_MOB) clif_fixmobpos ((struct mob_data *) bl); else clif_fixpos (bl); } } break; case RG_BACKSTAP: /* バックスタブ */ { int dir = map_calc_dir (src, bl->x, bl->y), t_dir = battle_get_dir (bl); int dist = distance (src->x, src->y, bl->x, bl->y); if ((dist > 0 && !map_check_dir (dir, t_dir)) || bl->type == BL_SKILL) { struct status_change *sc_data = battle_get_sc_data (src); if (sc_data && sc_data[SC_HIDING].timer != -1) skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); } else if (src->type == BL_PC) clif_skill_fail (sd, sd->skillid, 0, 0); } break; case AM_ACIDTERROR: /* アシッドテラー */ skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); if (bl->type == BL_PC && MRAND (100) < skill_get_time (skillid, skilllv) && battle_config.equipment_breaking) pc_breakarmor ((struct map_session_data *) bl); break; case MO_FINGEROFFENSIVE: /* 指弾 */ { struct status_change *sc_data = battle_get_sc_data (src); if (!battle_config.finger_offensive_type) skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); else { skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); if (sd) { for (int i = 1; i < sd->spiritball_old; i++) skill_addtimerskill (src, tick + i * 200, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag); sd->canmove_tick = tick + (sd->spiritball_old - 1) * 200; } } if (sc_data && sc_data[SC_BLADESTOP].timer != -1) skill_status_change_end (src, SC_BLADESTOP, -1); } break; case MO_CHAINCOMBO: /* 連打掌 */ { struct status_change *sc_data = battle_get_sc_data (src); skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); if (sc_data && sc_data[SC_BLADESTOP].timer != -1) skill_status_change_end (src, SC_BLADESTOP, -1); } break; case MO_COMBOFINISH: /* 猛龍拳 */ case CH_TIGERFIST: /* 伏虎拳 */ case CH_CHAINCRUSH: /* 連柱崩撃 */ case CH_PALMSTRIKE: /* 猛虎硬派山 */ skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); break; case MO_EXTREMITYFIST: /* 阿修羅覇鳳拳 */ { struct status_change *sc_data = battle_get_sc_data (src); if (sd) { struct walkpath_data wpd; int dx, dy; dx = bl->x - sd->bl.x; dy = bl->y - sd->bl.y; if (dx > 0) dx++; else if (dx < 0) dx--; if (dy > 0) dy++; else if (dy < 0) dy--; if (dx == 0 && dy == 0) dx++; if (path_search (&wpd, src->m, sd->bl.x, sd->bl.y, sd->bl.x + dx, sd->bl.y + dy, 1) == -1) { dx = bl->x - sd->bl.x; dy = bl->y - sd->bl.y; if (path_search (&wpd, src->m, sd->bl.x, sd->bl.y, sd->bl.x + dx, sd->bl.y + dy, 1) == -1) { clif_skill_fail (sd, sd->skillid, 0, 0); break; } } sd->to_x = sd->bl.x + dx; sd->to_y = sd->bl.y + dy; skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); clif_walkok (sd); clif_movechar (sd); if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; sd->attackabletime = sd->canmove_tick = tick + 100 + sd->speed * ((dx > dy) ? dx : dy); if (sd->canact_tick < sd->canmove_tick) sd->canact_tick = sd->canmove_tick; pc_movepos (sd, sd->to_x, sd->to_y); skill_status_change_end (&sd->bl, SC_COMBO, -1); } else skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); skill_status_change_end (src, SC_EXPLOSIONSPIRITS, -1); if (sc_data && sc_data[SC_BLADESTOP].timer != -1) skill_status_change_end (src, SC_BLADESTOP, -1); } break; /* 武器系範囲攻撃スキル */ case AC_SHOWER: /* アローシャワー */ case SM_MAGNUM: /* マグナムブレイク */ case AS_GRIMTOOTH: /* グリムトゥース */ case MC_CARTREVOLUTION: /* カートレヴォリューション */ case NPC_SPLASHATTACK: /* スプラッシュアタック */ case ASC_METEORASSAULT: /* メテオアサルト */ case AS_SPLASHER: /* [Valaris] */ if (flag & 1) { /* 個別にダメージを与える */ if (bl->id != skill_area_temp[1]) { int dist = 0; if (skillid == SM_MAGNUM) { /* マグナムブレイクなら中心からの距離を計算 */ int dx = abs (bl->x - skill_area_temp[2]); int dy = abs (bl->y - skill_area_temp[3]); dist = ((dx > dy) ? dx : dy); } skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500 | dist); } } else { int ar = 1; int x = bl->x, y = bl->y; if (skillid == SM_MAGNUM) { x = src->x; y = src->y; } else if (skillid == AC_SHOWER || skillid == ASC_METEORASSAULT) /* アローシャワー、メテオアサルト範囲5*5 */ ar = 2; else if (skillid == AS_SPLASHER) /* ベナムスプラッシャー範囲3*3 */ ar = 1; else if (skillid == NPC_SPLASHATTACK) /* スプラッシュアタックは範囲7*7 */ ar = 3; skill_area_temp[1] = bl->id; skill_area_temp[2] = x; skill_area_temp[3] = y; /* まずターゲットに攻撃を加える */ skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0); /* その後ターゲット以外の範囲内の敵全体に処理を行う */ map_foreachinarea (skill_area_sub, bl->m, x - ar, y - ar, x + ar, y + ar, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } break; case KN_BOWLINGBASH: /* ボウリングバッシュ */ if (flag & 1) { /* 個別にダメージを与える */ if (bl->id != skill_area_temp[1]) skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500); } else { int damage; map_freeblock_lock (); damage = skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0); if (damage > 0) { int i, c; /* 他人から聞いた動きなので間違ってる可能性大&効率が悪いっす>< */ c = skill_get_blewcount (skillid, skilllv); if (map[bl->m].flag.gvg) c = 0; for (i = 0; i < c; i++) { skill_blown (src, bl, 1); if (bl->type == BL_MOB) clif_fixmobpos ((struct mob_data *) bl); else clif_fixpos (bl); skill_area_temp[0] = 0; map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY, skill_area_sub_count); if (skill_area_temp[0] > 1) break; } skill_area_temp[1] = bl->id; skill_area_temp[2] = bl->x; skill_area_temp[3] = bl->y; /* その後ターゲット以外の範囲内の敵全体に処理を行う */ map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); battle_damage (src, bl, damage, 1); if (rdamage > 0) battle_damage (bl, src, rdamage, 0); } map_freeblock_unlock (); } break; case ALL_RESURRECTION: /* リザレクション */ case PR_TURNUNDEAD: /* ターンアンデッド */ if (bl->type != BL_PC && battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); else { map_freeblock_unlock (); return 1; } break; /* 魔法系スキル */ case MG_SOULSTRIKE: /* ソウルストライク */ case MG_COLDBOLT: /* コールドボルト */ case MG_FIREBOLT: /* ファイアーボルト */ case MG_LIGHTNINGBOLT: /* ライトニングボルト */ case WZ_EARTHSPIKE: /* アーススパイク */ case AL_HEAL: /* ヒール */ case AL_HOLYLIGHT: /* ホーリーライト */ case MG_FROSTDIVER: /* フロストダイバー */ case WZ_JUPITEL: /* ユピテルサンダー */ case NPC_MAGICALATTACK: /* MOB:魔法打撃攻撃 */ case PR_ASPERSIO: /* アスペルシオ */ skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); break; case WZ_WATERBALL: /* ウォーターボール */ skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); if (skilllv > 1) skill_status_change_start (src, SC_WATERBALL, skilllv, bl->id, 0, 0, 0, 0); break; case PR_BENEDICTIO: /* 聖体降福 */ if (battle_get_race (bl) == 1 || battle_get_race (bl) == 6) skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); break; /* 魔法系範囲攻撃スキル */ case MG_NAPALMBEAT: /* ナパームビート */ case MG_FIREBALL: /* ファイヤーボール */ if (flag & 1) { /* 個別にダメージを与える */ if (bl->id != skill_area_temp[1]) { if (skillid == MG_FIREBALL) { /* ファイヤーボールなら中心からの距離を計算 */ int dx = abs (bl->x - skill_area_temp[2]); int dy = abs (bl->y - skill_area_temp[3]); skill_area_temp[0] = ((dx > dy) ? dx : dy); } skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0] | 0x0500); } } else { int ar = (skillid == MG_NAPALMBEAT) ? 1 : 2; skill_area_temp[1] = bl->id; if (skillid == MG_NAPALMBEAT) { /* ナパームでは先に数える */ skill_area_temp[0] = 0; map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY, skill_area_sub_count); } else { skill_area_temp[0] = 0; skill_area_temp[2] = bl->x; skill_area_temp[3] = bl->y; } /* まずターゲットに攻撃を加える */ skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); /* その後ターゲット以外の範囲内の敵全体に処理を行う */ map_foreachinarea (skill_area_sub, bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } break; case HW_NAPALMVULCAN: // Fixed By SteelViruZ if (flag & 1) { if (bl->id != skill_area_temp[1]) { skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); } } else { int ar = (skillid == HW_NAPALMVULCAN) ? 1 : 2; skill_area_temp[1] = bl->id; if (skillid == HW_NAPALMVULCAN) { skill_area_temp[0] = 0; map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY, skill_area_sub_count); } else { skill_area_temp[0] = 0; skill_area_temp[2] = bl->x; skill_area_temp[3] = bl->y; } skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); map_foreachinarea (skill_area_sub, bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } break; case WZ_FROSTNOVA: /* フロストノヴァ */ skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); break; case WZ_SIGHTRASHER: clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); skill_status_change_end (src, SC_SIGHT, -1); break; /* その他 */ case HT_BLITZBEAT: /* ブリッツビート */ if (flag & 1) { /* 個別にダメージを与える */ if (bl->id != skill_area_temp[1]) skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0] | (flag & 0xf00000)); } else { skill_area_temp[0] = 0; skill_area_temp[1] = bl->id; if (flag & 0xf00000) map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY, skill_area_sub_count); /* まずターゲットに攻撃を加える */ skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0] | (flag & 0xf00000)); /* その後ターゲット以外の範囲内の敵全体に処理を行う */ map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, bl->y - 1, bl->x + 1, bl->y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } break; case CR_GRANDCROSS: /* グランドクロス */ /* スキルユニット配置 */ skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); if (sd) sd->canmove_tick = tick + 1000; else if (src->type == BL_MOB) mob_changestate ((struct mob_data *) src, MS_DELAY, 1000); break; case TF_THROWSTONE: /* 石投げ */ case NPC_SMOKING: /* スモーキング */ skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, 0); break; case NPC_SELFDESTRUCTION: /* 自爆 */ case NPC_SELFDESTRUCTION2: /* 自爆2 */ if (flag & 1) { /* 個別にダメージを与える */ if (src->type == BL_MOB) { struct mob_data *mb = (struct mob_data *) src; nullpo_retr (1, mb); mb->hp = skill_area_temp[2]; if (bl->id != skill_area_temp[1]) skill_attack (BF_MISC, src, src, bl, NPC_SELFDESTRUCTION, skilllv, tick, flag); mb->hp = 1; } } else { struct mob_data *md; if ((md = (struct mob_data *) src)) { skill_area_temp[1] = bl->id; skill_area_temp[2] = battle_get_hp (src); clif_skill_nodamage (src, src, NPC_SELFDESTRUCTION, -1, 1); map_foreachinarea (skill_area_sub, bl->m, bl->x - 5, bl->y - 5, bl->x + 5, bl->y + 5, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); battle_damage (src, src, md->hp, 0); } } break; /* HP吸収/HP吸収魔法 */ case NPC_BLOODDRAIN: case NPC_ENERGYDRAIN: { int heal; heal = skill_attack ((skillid == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); if (heal > 0) { struct block_list tbl; tbl.id = 0; tbl.m = src->m; tbl.x = src->x; tbl.y = src->y; clif_skill_nodamage (&tbl, src, AL_HEAL, heal, 1); battle_heal (NULL, src, heal, 0, 0); } } break; case 0: if (sd) { if (flag & 3) { if (bl->id != skill_area_temp[1]) skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500); } else { int ar = sd->splash_range; skill_area_temp[1] = bl->id; map_foreachinarea (skill_area_sub, bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } } break; default: map_freeblock_unlock (); return 1; } map_freeblock_unlock (); return 0; } /*========================================== * スキル使用(詠唱完了、ID指定支援系) *------------------------------------------ */ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) { struct map_session_data *sd = NULL; struct map_session_data *dstsd = NULL; struct mob_data *md = NULL; struct mob_data *dstmd = NULL; int sc_def_vit, sc_def_mdef, strip_fix, strip_time, strip_per; int sc_dex, sc_luk; //クラスチェンジ用ボスモンスターID int changeclass[] = { 1038, 1039, 1046, 1059, 1086, 1087, 1112, 1115, 1157, 1159, 1190, 1272, 1312, 1373, 1492 }; int poringclass[] = { 1002 }; nullpo_retr (1, src); nullpo_retr (1, bl); if (src->type == BL_PC) sd = (struct map_session_data *) src; else if (src->type == BL_MOB) md = (struct mob_data *) src; sc_dex = battle_get_mdef (bl); sc_luk = battle_get_luk (bl); sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3); strip_fix = battle_get_dex (src) - battle_get_dex (bl); if (bl->type == BL_PC) { nullpo_retr (1, dstsd = (struct map_session_data *) bl); } else if (bl->type == BL_MOB) { nullpo_retr (1, dstmd = (struct mob_data *) bl); if (sc_def_vit > 50) sc_def_vit = 50; if (sc_def_mdef > 50) sc_def_mdef = 50; } if (sc_def_vit < 0) sc_def_vit = 0; if (sc_def_mdef < 0) sc_def_mdef = 0; if (strip_fix < 0) strip_fix = 0; if (bl == NULL || bl->prev == NULL) return 1; if (sd && pc_isdead (sd)) return 1; if (dstsd && pc_isdead (dstsd) && skillid != ALL_RESURRECTION) return 1; if (battle_get_class (bl) == 1288) return 1; if (skillnotok (skillid, (struct map_session_data *) bl)) // [MouseJstr] return 0; map_freeblock_lock (); switch (skillid) { case AL_HEAL: /* ヒール */ { int heal = skill_calc_heal (src, skilllv); int heal_get_jobexp; int skill; struct pc_base_job s_class; if (dstsd && dstsd->special_state.no_magic_damage) heal = 0; /* 黄金蟲カード(ヒール量0) */ if (sd) { s_class = pc_calc_base_job (sd->status.pc_class); if ((skill = pc_checkskill (sd, HP_MEDITATIO)) > 0) // メディテイティオ heal += heal * (skill * 2 / 100); if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && s_class.job == 23 && sd->status.sex == 0) //自分も対象もPC、対象が自分のパートナー、自分がスパノビ、自分が♀なら heal = heal * 2; //スパノビの嫁が旦那にヒールすると2倍になる } clif_skill_nodamage (src, bl, skillid, heal, 1); heal_get_jobexp = battle_heal (NULL, bl, heal, 0, 0); // JOB経験値獲得 if (src->type == BL_PC && bl->type == BL_PC && heal > 0 && src != bl && battle_config.heal_exp > 0) { heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; if (heal_get_jobexp <= 0) heal_get_jobexp = 1; pc_gainexp ((struct map_session_data *) src, 0, heal_get_jobexp); } } break; case ALL_RESURRECTION: /* リザレクション */ if (bl->type == BL_PC) { int per = 0; struct map_session_data *tsd = (struct map_session_data *) bl; nullpo_retr (1, tsd); if ((map[bl->m].flag.pvp) && tsd->pvp_point < 0) break; /* PVPで復活不可能状態 */ if (pc_isdead (tsd)) { /* 死亡判定 */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); switch (skilllv) { case 1: per = 10; break; case 2: per = 30; break; case 3: per = 50; break; case 4: per = 80; break; } tsd->status.hp = tsd->status.max_hp * per / 100; if (tsd->status.hp <= 0) tsd->status.hp = 1; if (tsd->special_state.restart_full_recover) { /* オシリスカード */ tsd->status.hp = tsd->status.max_hp; tsd->status.sp = tsd->status.max_sp; } pc_setstand (tsd); if (battle_config.pc_invincible_time > 0) pc_setinvincibletimer (tsd, battle_config.pc_invincible_time); clif_updatestatus (tsd, SP_HP); clif_resurrection (&tsd->bl, 1); if (src != bl && sd && battle_config.resurrection_exp > 0) { int exp = 0, jexp = 0; int lv = tsd->status.base_level - sd->status.base_level, jlv = tsd->status.job_level - sd->status.job_level; if (lv > 0) { exp = (int) ((double) tsd->status.base_exp * (double) lv * (double) battle_config.resurrection_exp / 1000000.); if (exp < 1) exp = 1; } if (jlv > 0) { jexp = (int) ((double) tsd->status.job_exp * (double) lv * (double) battle_config.resurrection_exp / 1000000.); if (jexp < 1) jexp = 1; } if (exp > 0 || jexp > 0) pc_gainexp (sd, exp, jexp); } } } break; case AL_DECAGI: /* 速度減少 */ if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (MRAND (100) < (50 + skilllv * 3 + (battle_get_lv (src) + battle_get_int (src) / 5) - sc_def_mdef)) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } break; case AL_CRUCIS: if (flag & 1) { int race = battle_get_race (bl), ele = battle_get_elem_type (bl); if (battle_check_target (src, bl, BCT_ENEMY) && (race == 6 || battle_check_undead (race, ele))) { int slv = battle_get_lv (src), tlv = battle_get_lv (bl), rate; rate = 25 + skilllv * 2 + slv - tlv; if (MRAND (100) < rate) skill_status_change_start (bl, SkillStatusChangeTable [skillid], skilllv, 0, 0, 0, 0, 0); } } else { int range = 15; clif_skill_nodamage (src, bl, skillid, skilllv, 1); map_foreachinarea (skill_area_sub, src->m, src->x - range, src->y - range, src->x + range, src->y + range, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_nodamage_id); } break; case PR_LEXDIVINA: /* レックスディビーナ */ { struct status_change *sc_data = battle_get_sc_data (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (sc_data && sc_data[SC_DIVINA].timer != -1) skill_status_change_end (bl, SC_DIVINA, -1); else if (MRAND (100) < sc_def_vit) { skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } } break; case SA_ABRACADABRA: break; case SA_COMA: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (dstsd) { dstsd->status.hp = 1; dstsd->status.sp = 1; clif_updatestatus (dstsd, SP_HP); clif_updatestatus (dstsd, SP_SP); } if (dstmd) dstmd->hp = 1; break; case SA_FULLRECOVERY: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (dstsd) pc_heal (dstsd, dstsd->status.max_hp, dstsd->status.max_sp); if (dstmd) dstmd->hp = battle_get_max_hp (&dstmd->bl); break; case SA_SUMMONMONSTER: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) mob_once_spawn (sd, map[sd->bl.m].name, sd->bl.x, sd->bl.y, "--ja--", -1, 1, ""); break; case SA_LEVELUP: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd && pc_nextbaseexp (sd)) pc_gainexp (sd, pc_nextbaseexp (sd) * 10 / 100, 0); break; case SA_INSTANTDEATH: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) pc_damage (NULL, sd, sd->status.max_hp); break; case SA_QUESTION: case SA_GRAVITY: clif_skill_nodamage (src, bl, skillid, skilllv, 1); break; case SA_CLASSCHANGE: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (dstmd) mob_class_change (dstmd, changeclass); break; case SA_MONOCELL: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (dstmd) mob_class_change (dstmd, poringclass); break; case SA_DEATH: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (dstsd) pc_damage (NULL, dstsd, dstsd->status.max_hp); if (dstmd) mob_damage (NULL, dstmd, dstmd->hp, 1); break; case SA_REVERSEORCISH: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (dstsd) pc_setoption (dstsd, dstsd->status.option | 0x0800); break; case SA_FORTUNE: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) pc_getzeny (sd, battle_get_lv (bl) * 100); break; case AL_INCAGI: /* 速度増加 */ case AL_BLESSING: /* ブレッシング */ case PR_SLOWPOISON: case PR_IMPOSITIO: /* イムポシティオマヌス */ case PR_LEXAETERNA: /* レックスエーテルナ */ case PR_SUFFRAGIUM: /* サフラギウム */ case PR_BENEDICTIO: /* 聖体降福 */ case CR_PROVIDENCE: /* プロヴィデンス */ case CG_MARIONETTE: /* マリオネットコントロール */ if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); } else { skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); clif_skill_nodamage (src, bl, skillid, skilllv, 1); } break; case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] case SA_FROSTWEAPON: case SA_LIGHTNINGLOADER: case SA_SEISMICWEAPON: if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) { clif_skill_nodamage (src, bl, skillid, skilllv, 0); break; } if (bl->type == BL_PC) { struct map_session_data *sd2 = (struct map_session_data *) bl; if (sd2->status.weapon == 0 || sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 || sd2->sc_data[SC_FROSTWEAPON].timer != -1 || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 || sd2->sc_data[SC_ENCPOISON].timer != -1) { clif_skill_fail (sd, skillid, 0, 0); clif_skill_nodamage (src, bl, skillid, skilllv, 0); break; } } if (MRAND (100) > (75 + skilllv * 1) && (skilllv != 5)) { clif_skill_fail (sd, skillid, 0, 0); clif_skill_nodamage (src, bl, skillid, skilllv, 0); if (bl->type == BL_PC && battle_config.equipment_breaking) { struct map_session_data *sd2 = (struct map_session_data *) bl; if (sd != sd2) clif_displaymessage (sd->fd, "You broke target's weapon"); pc_breakweapon (sd2); } break; } else { skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); clif_skill_nodamage (src, bl, skillid, skilllv, 1); } break; case PR_ASPERSIO: /* アスペルシオ */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (bl->type == BL_MOB) break; skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case PR_KYRIE: /* キリエエレイソン */ clif_skill_nodamage (bl, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case KN_AUTOCOUNTER: /* オートカウンター */ case KN_TWOHANDQUICKEN: /* ツーハンドクイッケン */ case CR_SPEARQUICKEN: /* スピアクイッケン */ case CR_REFLECTSHIELD: case AS_POISONREACT: /* ポイズンリアクト */ case MC_LOUD: /* ラウドボイス */ case MG_ENERGYCOAT: /* エナジーコート */ case SM_ENDURE: /* インデュア */ case MG_SIGHT: /* サイト */ case AL_RUWACH: /* ルアフ */ case MO_EXPLOSIONSPIRITS: // 爆裂波動 case MO_STEELBODY: // 金剛 case LK_AURABLADE: /* オーラブレード */ case LK_PARRYING: /* パリイング */ case LK_CONCENTRATION: /* コンセントレーション */ case LK_BERSERK: /* バーサーク */ case HP_ASSUMPTIO: /* */ case WS_CARTBOOST: /* カートブースト */ case SN_SIGHT: /* トゥルーサイト */ case WS_MELTDOWN: /* メルトダウン */ case ST_REJECTSWORD: /* リジェクトソード */ case HW_MAGICPOWER: /* 魔法力増幅 */ case PF_MEMORIZE: /* メモライズ */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case AS_ENCHANTPOISON: // Prevent spamming [Valaris] if (bl->type == BL_PC) { struct map_session_data *sd2 = (struct map_session_data *) bl; if (sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 || sd2->sc_data[SC_FROSTWEAPON].timer != -1 || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 || sd2->sc_data[SC_ENCPOISON].timer != -1) { clif_skill_nodamage (src, bl, skillid, skilllv, 0); clif_skill_fail (sd, skillid, 0, 0); break; } } clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case LK_TENSIONRELAX: /* テンションリラックス */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); pc_setsit (sd); clif_sitting (sd->fd, sd); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case MC_CHANGECART: clif_skill_nodamage (src, bl, skillid, skilllv, 1); break; case AC_CONCENTRATION: /* 集中力向上 */ { int range = 1; clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); map_foreachinarea (skill_status_change_timer_sub, src->m, src->x - range, src->y - range, src->x + range, src->y + range, 0, src, SkillStatusChangeTable[skillid], tick); } break; case SM_PROVOKE: /* プロボック */ { struct status_change *sc_data = battle_get_sc_data (bl); /* MVPmobと不死には効かない */ if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない { map_freeblock_unlock (); return 1; } clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 skill_castcancel (bl, 0); if (dstsd && dstsd->skilltimer != -1 && (!dstsd->special_state.no_castcancel || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2) skill_castcancel (bl, 0); if (sc_data) { if (sc_data[SC_FREEZE].timer != -1) skill_status_change_end (bl, SC_FREEZE, -1); if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) skill_status_change_end (bl, SC_STONE, -1); if (sc_data[SC_SLEEP].timer != -1) skill_status_change_end (bl, SC_SLEEP, -1); } if (bl->type == BL_MOB) { int range = skill_get_range (skillid, skilllv); if (range < 0) range = battle_get_range (src) - (range + 1); mob_target ((struct mob_data *) bl, src, range); } } break; case CR_DEVOTION: /* ディボーション */ if (sd && dstsd) { //転生や養子の場合の元の職業を算出する int lv = sd->status.base_level - dstsd->status.base_level; lv = (lv < 0) ? -lv : lv; if ((dstsd->bl.type != BL_PC) // 相手はPCじゃないとだめ || (sd->bl.id == dstsd->bl.id) // 相手が自分はだめ || (lv > 10) // レベル差±10まで || (!sd->status.party_id && !sd->status.guild_id) // PTにもギルドにも所属無しはだめ || ((sd->status.party_id != dstsd->status.party_id) // 同じパーティーか、 || (sd->status.guild_id != dstsd->status.guild_id)) // 同じギルドじゃないとだめ || (dstsd->status.pc_class == 14 || dstsd->status.pc_class == 21 || dstsd->status.pc_class == 4015 || dstsd->status.pc_class == 4022)) { // クルセだめ clif_skill_fail (sd, skillid, 0, 0); map_freeblock_unlock (); return 1; } for (int i = 0; i < skilllv; i++) { if (!sd->dev.val1[i]) { // 空きがあったら入れる sd->dev.val1[i] = bl->id; sd->dev.val2[i] = bl->id; break; } else if (i == skilllv - 1) { // 空きがなかった clif_skill_fail (sd, skillid, 0, 0); map_freeblock_unlock (); return 1; } } clif_skill_nodamage (src, bl, skillid, skilllv, 1); clif_devotion (sd, bl->id); skill_status_change_start (bl, SkillStatusChangeTable[skillid], src->id, 1, 0, 0, 1000 * (15 + 15 * skilllv), 0); } else clif_skill_fail (sd, skillid, 0, 0); break; case MO_CALLSPIRITS: // 気功 if (sd) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); pc_addspiritball (sd, skill_get_time (skillid, skilllv), skilllv); } break; case CH_SOULCOLLECT: // 狂気功 if (sd) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); for (int i = 0; i < 5; i++) pc_addspiritball (sd, skill_get_time (skillid, skilllv), 5); } break; case MO_BLADESTOP: // 白刃取り clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (src, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case MO_ABSORBSPIRITS: // 気奪 { int i = 0; if (sd && dstsd) { if (sd == dstsd || map[sd->bl.m].flag.pvp || map[sd->bl.m].flag.gvg) { if (dstsd->spiritball > 0) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); i = dstsd->spiritball * 7; pc_delspiritball (dstsd, dstsd->spiritball, 0); if (i > 0x7FFF) i = 0x7FFF; if (sd->status.sp + i > sd->status.max_sp) i = sd->status.max_sp - sd->status.sp; } } } else if (sd && dstmd) { //対象がモンスターの場合 //20%の確率で対象のLv*2のSPを回復する。成功したときはターゲット(σ゚Д゚)σゲッツ!! if (MRAND (100) < 20) { i = 2 * mob_db[dstmd->mob_class].lv; mob_target (dstmd, src, 0); } } if (i) { sd->status.sp += i; clif_heal (sd->fd, SP_SP, i); } else clif_skill_nodamage (src, bl, skillid, skilllv, 0); break; } case AC_MAKINGARROW: /* 矢作成 */ /* if(sd) { clif_arrow_create_list(sd); clif_skill_nodamage(src,bl,skillid,skilllv,1); }*/ break; case AM_PHARMACY: /* ポーション作成 */ /* if(sd) { clif_skill_produce_mix_list(sd,32); clif_skill_nodamage(src,bl,skillid,skilllv,1); }*/ break; case WS_CREATECOIN: /* クリエイトコイン */ /* if(sd) { clif_skill_produce_mix_list(sd,64); clif_skill_nodamage(src,bl,skillid,skilllv,1); }*/ break; case WS_CREATENUGGET: /* 塊製造 */ /* if(sd) { clif_skill_produce_mix_list(sd,128); clif_skill_nodamage(src,bl,skillid,skilllv,1); }*/ break; case BS_HAMMERFALL: /* ハンマーフォール */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_weapon_damage) break; if (MRAND (100) < (20 + 10 * skilllv) * sc_def_vit / 100) { skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); } break; case RG_RAID: /* サプライズアタック */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); { int x = bl->x, y = bl->y; skill_area_temp[1] = bl->id; skill_area_temp[2] = x; skill_area_temp[3] = y; map_foreachinarea (skill_area_sub, bl->m, x - 1, y - 1, x + 1, y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 break; case KN_BRANDISHSPEAR: /*ブランディッシュスピア */ { int c, n = 4, ar; int dir = map_calc_dir (src, bl->x, bl->y); struct square tc; int x = bl->x, y = bl->y; ar = skilllv / 3; skill_brandishspear_first (&tc, dir, x, y); skill_brandishspear_dir (&tc, dir, 4); /* 範囲C */ if (skilllv == 10) { for (c = 1; c < 4; c++) { map_foreachinarea (skill_area_sub, bl->m, tc.val1[c], tc.val2[c], tc.val1[c], tc.val2[c], 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | n, skill_castend_damage_id); } } /* 範囲BA */ if (skilllv > 6) { skill_brandishspear_dir (&tc, dir, -1); n--; } else { skill_brandishspear_dir (&tc, dir, -2); n -= 2; } if (skilllv > 3) { for (c = 0; c < 5; c++) { map_foreachinarea (skill_area_sub, bl->m, tc.val1[c], tc.val2[c], tc.val1[c], tc.val2[c], 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | n, skill_castend_damage_id); if (skilllv > 6 && n == 3 && c == 4) { skill_brandishspear_dir (&tc, dir, -1); n--; c = -1; } } } /* 範囲@ */ for (c = 0; c < 10; c++) { if (c == 0 || c == 5) skill_brandishspear_dir (&tc, dir, -1); map_foreachinarea (skill_area_sub, bl->m, tc.val1[c % 5], tc.val2[c % 5], tc.val1[c % 5], tc.val2[c % 5], 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); } } break; /* パーティスキル */ case AL_ANGELUS: /* エンジェラス */ case PR_MAGNIFICAT: /* マグニフィカート */ case PR_GLORIA: /* グロリア */ case SN_WINDWALK: /* ウインドウォーク */ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { /* 個別の処理 */ clif_skill_nodamage (bl, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } else { /* パーティ全体への処理 */ party_foreachsamemap (skill_area_sub, sd, 1, src, skillid, skilllv, tick, flag | BCT_PARTY | 1, skill_castend_nodamage_id); } break; case BS_ADRENALINE: /* アドレナリンラッシュ */ case BS_WEAPONPERFECT: /* ウェポンパーフェクション */ case BS_OVERTHRUST: /* オーバートラスト */ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { /* 個別の処理 */ clif_skill_nodamage (bl, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, (src == bl) ? 1 : 0, 0, 0, skill_get_time (skillid, skilllv), 0); } else { /* パーティ全体への処理 */ party_foreachsamemap (skill_area_sub, sd, 1, src, skillid, skilllv, tick, flag | BCT_PARTY | 1, skill_castend_nodamage_id); } break; /*(付加と解除が必要) */ case BS_MAXIMIZE: /* マキシマイズパワー */ case NV_TRICKDEAD: /* 死んだふり */ case CR_DEFENDER: /* ディフェンダー */ case CR_AUTOGUARD: /* オートガード */ { struct status_change *tsc_data = battle_get_sc_data (bl); int sc = SkillStatusChangeTable[skillid]; clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (tsc_data) { if (tsc_data[sc].timer == -1) /* 付加する */ skill_status_change_start (bl, sc, skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); else /* 解除する */ skill_status_change_end (bl, sc, -1); } } break; case TF_HIDING: /* ハイディング */ { struct status_change *tsc_data = battle_get_sc_data (bl); int sc = SkillStatusChangeTable[skillid]; clif_skill_nodamage (src, bl, skillid, -1, 1); if (tsc_data) { if (tsc_data[sc].timer == -1) /* 付加する */ skill_status_change_start (bl, sc, skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); else /* 解除する */ skill_status_change_end (bl, sc, -1); } } break; case AS_CLOAKING: /* クローキング */ { struct status_change *tsc_data = battle_get_sc_data (bl); int sc = SkillStatusChangeTable[skillid]; clif_skill_nodamage (src, bl, skillid, -1, 1); if (tsc_data) { if (tsc_data[sc].timer == -1) /* 付加する */ skill_status_change_start (bl, sc, skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); else /* 解除する */ skill_status_change_end (bl, sc, -1); } skill_check_cloaking (bl); } break; case ST_CHASEWALK: /* ハイディング */ { struct status_change *tsc_data = battle_get_sc_data (bl); int sc = SkillStatusChangeTable[skillid]; clif_skill_nodamage (src, bl, skillid, -1, 1); if (tsc_data) { if (tsc_data[sc].timer == -1) /* 付加する */ skill_status_change_start (bl, sc, skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); else /* 解除する */ skill_status_change_end (bl, sc, -1); } } break; /* 対地スキル */ case BD_LULLABY: /* 子守唄 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BA_DISSONANCE: /* 不協和音 */ case BA_POEMBRAGI: /* ブラギの詩 */ case BA_WHISTLE: /* 口笛 */ case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ case BA_APPLEIDUN: /* イドゥンの林檎 */ case DC_UGLYDANCE: /* 自分勝手なダンス */ case DC_HUMMING: /* ハミング */ case DC_DONTFORGETME: /* 私を忘れないで… */ case DC_FORTUNEKISS: /* 幸運のキス */ case DC_SERVICEFORYOU: /* サービスフォーユー */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); break; case HP_BASILICA: /* バジリカ */ case PA_GOSPEL: /* ゴスペル */ skill_clear_unitgroup (src); clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); break; case BD_ADAPTATION: /* アドリブ */ { struct status_change *sc_data = battle_get_sc_data (src); if (sc_data && sc_data[SC_DANCING].timer != -1) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_stop_dancing (src, 0); } } break; case BA_FROSTJOKE: /* 寒いジョーク */ case DC_SCREAM: /* スクリーム */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_addtimerskill (src, tick + 3000, bl->id, 0, 0, skillid, skilllv, 0, flag); break; case TF_STEAL: // スティール if (sd) { if (pc_steal_item (sd, bl)) clif_skill_nodamage (src, bl, skillid, skilllv, 1); else clif_skill_nodamage (src, bl, skillid, skilllv, 0); } break; case RG_STEALCOIN: // スティールコイン if (sd) { if (pc_steal_coin (sd, bl)) { int range = skill_get_range (skillid, skilllv); if (range < 0) range = battle_get_range (src) - (range + 1); clif_skill_nodamage (src, bl, skillid, skilllv, 1); mob_target ((struct mob_data *) bl, src, range); } else clif_skill_nodamage (src, bl, skillid, skilllv, 0); } break; case MG_STONECURSE: /* ストーンカース */ if (bl->type == BL_MOB && battle_get_mode (bl) & 0x20) { clif_skill_fail (sd, sd->skillid, 0, 0); break; } clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (MRAND (100) < skilllv * 4 + 20 && !battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) skill_status_change_start (bl, SC_STONE, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); else if (sd) clif_skill_fail (sd, skillid, 0, 0); break; case NV_FIRSTAID: /* 応急手当 */ clif_skill_nodamage (src, bl, skillid, 5, 1); battle_heal (NULL, bl, 5, 0, 0); break; case AL_CURE: /* キュアー */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_end (bl, SC_SILENCE, -1); skill_status_change_end (bl, SC_BLIND, -1); skill_status_change_end (bl, SC_CONFUSION, -1); if (battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) { //アンデッドなら暗闇効果 skill_status_change_start (bl, SC_CONFUSION, 1, 0, 0, 0, 6000, 0); } break; case TF_DETOXIFY: /* 解毒 */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_end (bl, SC_POISON, -1); break; case PR_STRECOVERY: /* リカバリー */ { clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_end (bl, SC_FREEZE, -1); skill_status_change_end (bl, SC_STONE, -1); skill_status_change_end (bl, SC_SLEEP, -1); skill_status_change_end (bl, SC_STAN, -1); if (battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) { //アンデッドなら暗闇効果 int blind_time; //blind_time=30-battle_get_vit(bl)/10-battle_get_int/15; blind_time = 30 * (100 - (battle_get_int (bl) + battle_get_vit (bl)) / 2) / 100; if (MRAND (100) < (100 - (battle_get_int (bl) / 2 + battle_get_vit (bl) / 3 + battle_get_luk (bl) / 10))) skill_status_change_start (bl, SC_BLIND, 1, 0, 0, 0, blind_time, 0); } if (dstmd) { dstmd->attacked_id = 0; dstmd->target_id = 0; dstmd->state.targettype = NONE_ATTACKABLE; dstmd->state.skillstate = MSS_IDLE; dstmd->next_walktime = tick + MRAND (3000) + 3000; } } break; case WZ_ESTIMATION: /* モンスター情報 */ if (src->type == BL_PC) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); clif_skill_estimation ((struct map_session_data *) src, bl); } break; case MC_IDENTIFY: /* アイテム鑑定 */ if (sd) clif_item_identify_list (sd); break; case BS_REPAIRWEAPON: /* 武器修理 */ if (sd) //動作しないのでとりあえずコメントアウト // clif_item_repair_list(sd); break; case AL_TELEPORT: /* テレポート */ if (sd) { if (map[sd->bl.m].flag.noteleport) { /* テレポ禁止 */ clif_skill_teleportmessage (sd, 0); break; } clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd->skilllv == 1) clif_skill_warppoint (sd, sd->skillid, "Random", "", "", ""); else { clif_skill_warppoint (sd, sd->skillid, "Random", sd->status.save_point.map, "", ""); } } else if (bl->type == BL_MOB) mob_warp ((struct mob_data *) bl, -1, -1, -1, 3); break; case AL_HOLYWATER: /* アクアベネディクタ */ if (sd) { int eflag; struct item item_tmp; clif_skill_nodamage (src, bl, skillid, skilllv, 1); memset (&item_tmp, 0, sizeof (item_tmp)); item_tmp.nameid = 523; item_tmp.identify = 1; if (battle_config.holywater_name_input) { item_tmp.card[0] = 0xfe; item_tmp.card[1] = 0; *((unsigned long *) (&item_tmp.card[2])) = sd->char_id; /* キャラID */ } eflag = pc_additem (sd, &item_tmp, 1); if (eflag) { clif_additem (sd, 0, 0, eflag); map_addflooritem (&item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0); } } break; case TF_PICKSTONE: if (sd) { int eflag; struct item item_tmp; struct block_list tbl; clif_skill_nodamage (src, bl, skillid, skilllv, 1); memset (&item_tmp, 0, sizeof (item_tmp)); memset (&tbl, 0, sizeof (tbl)); // [MouseJstr] item_tmp.nameid = 7049; item_tmp.identify = 1; tbl.id = 0; clif_takeitem (&sd->bl, &tbl); eflag = pc_additem (sd, &item_tmp, 1); if (eflag) { clif_additem (sd, 0, 0, eflag); map_addflooritem (&item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0); } } break; case RG_STRIPWEAPON: /* ストリップウェポン */ { struct status_change *tsc_data = battle_get_sc_data (bl); if (tsc_data && tsc_data[SC_CP_WEAPON].timer != -1) break; strip_per = 5 + 2 * skilllv + strip_fix / 5; strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; if (MRAND (100) < strip_per) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, strip_time, 0); if (dstsd) { for (int i = 0; i < MAX_INVENTORY; i++) { if (dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0002) { pc_unequipitem (dstsd, i, 0); break; } } } } } break; case RG_STRIPSHIELD: /* ストリップシールド */ { struct status_change *tsc_data = battle_get_sc_data (bl); if (tsc_data && tsc_data[SC_CP_SHIELD].timer != -1) break; strip_per = 5 + 2 * skilllv + strip_fix / 5; strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; if (MRAND (100) < strip_per) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, strip_time, 0); if (dstsd) { for (int i = 0; i < MAX_INVENTORY; i++) { if (dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0020) { pc_unequipitem (dstsd, i, 0); break; } } } } } break; case RG_STRIPARMOR: /* ストリップアーマー */ { struct status_change *tsc_data = battle_get_sc_data (bl); if (tsc_data && tsc_data[SC_CP_ARMOR].timer != -1) break; strip_per = 5 + 2 * skilllv + strip_fix / 5; strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; if (MRAND (100) < strip_per) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, strip_time, 0); if (dstsd) { for (int i = 0; i < MAX_INVENTORY; i++) { if (dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0010) { pc_unequipitem (dstsd, i, 0); break; } } } } } break; case RG_STRIPHELM: /* ストリップヘルム */ { struct status_change *tsc_data = battle_get_sc_data (bl); if (tsc_data && tsc_data[SC_CP_HELM].timer != -1) break; strip_per = 5 + 2 * skilllv + strip_fix / 5; strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; if (MRAND (100) < strip_per) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, strip_time, 0); if (dstsd) { for (int i = 0; i < MAX_INVENTORY; i++) { if (dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0100) { pc_unequipitem (dstsd, i, 0); break; } } } } } break; /* PotionPitcher */ case AM_POTIONPITCHER: /* ポーションピッチャー */ { struct block_list tbl; int i, x, hp = 0, sp = 0; if (sd) { if (sd == dstsd) { // cancel use on oneself map_freeblock_unlock (); return 1; } x = skilllv % 11 - 1; i = pc_search_inventory (sd, skill_db[skillid].itemid[x]); if (i < 0 || skill_db[skillid].itemid[x] <= 0) { clif_skill_fail (sd, skillid, 0, 0); map_freeblock_unlock (); return 1; } if (sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) { clif_skill_fail (sd, skillid, 0, 0); map_freeblock_unlock (); return 1; } sd->state.potionpitcher_flag = 1; sd->potion_hp = sd->potion_sp = sd->potion_per_hp = sd->potion_per_sp = 0; sd->skilltarget = bl->id; run_script (sd->inventory_data[i]->use_script, 0, sd->bl.id, 0); pc_delitem (sd, i, skill_db[skillid].amount[x], 0); sd->state.potionpitcher_flag = 0; if (sd->potion_per_hp > 0 || sd->potion_per_sp > 0) { hp = battle_get_max_hp (bl) * sd->potion_per_hp / 100; hp = hp * (100 + pc_checkskill (sd, AM_POTIONPITCHER) * 10 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5) / 100; if (dstsd) { sp = dstsd->status.max_sp * sd->potion_per_sp / 100; sp = sp * (100 + pc_checkskill (sd, AM_POTIONPITCHER) + pc_checkskill (sd, AM_LEARNINGPOTION) * 5) / 100; } } else { if (sd->potion_hp > 0) { hp = sd->potion_hp * (100 + pc_checkskill (sd, AM_POTIONPITCHER) * 10 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5) / 100; hp = hp * (100 + (battle_get_vit (bl) << 1)) / 100; if (dstsd) hp = hp * (100 + pc_checkskill (dstsd, SM_RECOVERY) * 10) / 100; } if (sd->potion_sp > 0) { sp = sd->potion_sp * (100 + pc_checkskill (sd, AM_POTIONPITCHER) + pc_checkskill (sd, AM_LEARNINGPOTION) * 5) / 100; sp = sp * (100 + (battle_get_int (bl) << 1)) / 100; if (dstsd) sp = sp * (100 + pc_checkskill (dstsd, MG_SRECOVERY) * 10) / 100; } } } else { hp = (1 + MRAND (400)) * (100 + skilllv * 10) / 100; hp = hp * (100 + (battle_get_vit (bl) << 1)) / 100; if (dstsd) hp = hp * (100 + pc_checkskill (dstsd, SM_RECOVERY) * 10) / 100; } tbl.id = 0; tbl.m = src->m; tbl.x = src->x; tbl.y = src->y; clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (hp > 0 || (hp <= 0 && sp <= 0)) clif_skill_nodamage (&tbl, bl, AL_HEAL, hp, 1); if (sp > 0) clif_skill_nodamage (&tbl, bl, MG_SRECOVERY, sp, 1); battle_heal (src, bl, hp, sp, 0); } break; case AM_CP_WEAPON: { struct status_change *tsc_data = battle_get_sc_data (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (tsc_data && tsc_data[SC_STRIPWEAPON].timer != -1) skill_status_change_end (bl, SC_STRIPWEAPON, -1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } break; case AM_CP_SHIELD: { struct status_change *tsc_data = battle_get_sc_data (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (tsc_data && tsc_data[SC_STRIPSHIELD].timer != -1) skill_status_change_end (bl, SC_STRIPSHIELD, -1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } break; case AM_CP_ARMOR: { struct status_change *tsc_data = battle_get_sc_data (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (tsc_data && tsc_data[SC_STRIPARMOR].timer != -1) skill_status_change_end (bl, SC_STRIPARMOR, -1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } break; case AM_CP_HELM: { struct status_change *tsc_data = battle_get_sc_data (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (tsc_data && tsc_data[SC_STRIPHELM].timer != -1) skill_status_change_end (bl, SC_STRIPHELM, -1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); } break; case SA_DISPELL: /* ディスペル */ { int i; clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; for (i = 0; i < 136; i++) { if (i == SC_RIDING || i == SC_FALCON || i == SC_HALLUCINATION || i == SC_WEIGHT50 || i == SC_WEIGHT90 || i == SC_STRIPWEAPON || i == SC_STRIPSHIELD || i == SC_STRIPARMOR || i == SC_STRIPHELM || i == SC_CP_WEAPON || i == SC_CP_SHIELD || i == SC_CP_ARMOR || i == SC_CP_HELM || i == SC_COMBO) continue; skill_status_change_end (bl, i, -1); } } break; case TF_BACKSLIDING: /* バックステップ */ battle_stopwalking (src, 1); skill_blown (src, bl, skill_get_blewcount (skillid, skilllv) | 0x10000); if (src->type == BL_MOB) clif_fixmobpos ((struct mob_data *) src); else if (src->type == BL_PC) clif_fixpos (src); skill_addtimerskill (src, tick + 200, src->id, 0, 0, skillid, skilllv, 0, flag); break; case SA_CASTCANCEL: clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_castcancel (src, 1); if (sd) { int sp = skill_get_sp (sd->skillid_old, sd->skilllv_old); sp = sp * (90 - (skilllv - 1) * 20) / 100; if (sp < 0) sp = 0; pc_heal (sd, 0, -sp); } break; case SA_SPELLBREAKER: // スペルブレイカー { struct status_change *sc_data = battle_get_sc_data (bl); int sp; if (sc_data && sc_data[SC_MAGICROD].timer != -1) { if (dstsd) { sp = skill_get_sp (skillid, skilllv); sp = sp * sc_data[SC_MAGICROD].val2 / 100; if (sp > 0x7fff) sp = 0x7fff; else if (sp < 1) sp = 1; if (dstsd->status.sp + sp > dstsd->status.max_sp) { sp = dstsd->status.max_sp - dstsd->status.sp; dstsd->status.sp = dstsd->status.max_sp; } else dstsd->status.sp += sp; clif_heal (dstsd->fd, SP_SP, sp); } clif_skill_nodamage (bl, bl, SA_MAGICROD, sc_data[SC_MAGICROD].val1, 1); if (sd) { sp = sd->status.max_sp / 5; if (sp < 1) sp = 1; pc_heal (sd, 0, -sp); } } else { int bl_skillid = 0, bl_skilllv = 0; if (bl->type == BL_PC) { if (dstsd && dstsd->skilltimer != -1) { bl_skillid = dstsd->skillid; bl_skilllv = dstsd->skilllv; } } else if (bl->type == BL_MOB) { if (dstmd && dstmd->skilltimer != -1) { bl_skillid = dstmd->skillid; bl_skilllv = dstmd->skilllv; } } if (bl_skillid > 0 && skill_db[bl_skillid].skill_type == BF_MAGIC) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_castcancel (bl, 0); sp = skill_get_sp (bl_skillid, bl_skilllv); if (dstsd) pc_heal (dstsd, 0, -sp); if (sd) { sp = sp * (25 * (skilllv - 1)) / 100; if (skilllv > 1 && sp < 1) sp = 1; if (sp > 0x7fff) sp = 0x7fff; else if (sp < 1) sp = 1; if (sd->status.sp + sp > sd->status.max_sp) { sp = sd->status.max_sp - sd->status.sp; sd->status.sp = sd->status.max_sp; } else sd->status.sp += sp; clif_heal (sd->fd, SP_SP, sp); } } else if (sd) clif_skill_fail (sd, skillid, 0, 0); } } break; case SA_MAGICROD: if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case SA_AUTOSPELL: /* オートスペル */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) clif_autospell (sd, skilllv); else { int maxlv = 1, spellid = 0; static const int spellarray[3] = { MG_COLDBOLT, MG_FIREBOLT, MG_LIGHTNINGBOLT }; if (skilllv >= 10) { spellid = MG_FROSTDIVER; maxlv = skilllv - 9; } else if (skilllv >= 8) { spellid = MG_FIREBALL; maxlv = skilllv - 7; } else if (skilllv >= 5) { spellid = MG_SOULSTRIKE; maxlv = skilllv - 4; } else if (skilllv >= 2) { int i = MRAND (3); spellid = spellarray[i]; maxlv = skilllv - 1; } else if (skilllv > 0) { spellid = MG_NAPALMBEAT; maxlv = 3; } if (spellid > 0) skill_status_change_start (src, SC_AUTOSPELL, skilllv, spellid, maxlv, 0, skill_get_time (SA_AUTOSPELL, skilllv), 0); } break; /* ランダム属性変化、水属性変化、地、火、風 */ case NPC_ATTRICHANGE: case NPC_CHANGEWATER: case NPC_CHANGEGROUND: case NPC_CHANGEFIRE: case NPC_CHANGEWIND: /* 毒、聖、念、闇 */ case NPC_CHANGEPOISON: case NPC_CHANGEHOLY: case NPC_CHANGEDARKNESS: case NPC_CHANGETELEKINESIS: if (md) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); md->def_ele = skill_get_pl (skillid); if (md->def_ele == 0) /* ランダム変化、ただし、 */ md->def_ele = MRAND (10); /* 不死属性は除く */ md->def_ele += (1 + MRAND (4)) * 20; /* 属性レベルはランダム */ } break; case NPC_HALLUCINATION: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); break; case NPC_KEEPING: case NPC_BARRIER: { int skill_time = skill_get_time (skillid, skilllv); clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_time, 0); mob_changestate ((struct mob_data *) src, MS_DELAY, skill_time); } break; case NPC_DARKBLESSING: { int sc_def = 100 - battle_get_mdef (bl); clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (battle_get_elem_type (bl) == 7 || battle_get_race (bl) == 6) break; if (MRAND (100) < sc_def * (50 + skilllv * 5) / 100) { if (dstsd) { int hp = battle_get_hp (bl) - 1; pc_heal (dstsd, -hp, 0); } else if (dstmd) dstmd->hp = 1; } } break; case NPC_SELFDESTRUCTION: /* 自爆 */ case NPC_SELFDESTRUCTION2: /* 自爆2 */ skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, skillid, 0, 0, skill_get_time (skillid, skilllv), 0); break; case NPC_LICK: clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_weapon_damage) break; if (dstsd) pc_heal (dstsd, 0, -100); if (MRAND (100) < (skilllv * 5) * sc_def_vit / 100) skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, skill_get_time2 (skillid, skilllv), 0); break; case NPC_SUICIDE: /* 自決 */ if (src && bl && md) { clif_skill_nodamage (src, bl, skillid, skilllv, 1); mob_damage (NULL, md, md->hp, 0); } break; case NPC_SUMMONSLAVE: /* 手下召喚 */ case NPC_SUMMONMONSTER: /* MOB召喚 */ if (md && !md->master_id) { mob_summonslave (md, mob_db[md->mob_class].skill[md->skillidx].val, skilllv, (skillid == NPC_SUMMONSLAVE) ? 1 : 0); } break; case NPC_TRANSFORMATION: case NPC_METAMORPHOSIS: if (md) mob_class_change (md, mob_db[md->mob_class].skill[md->skillidx].val); break; case NPC_EMOTION: /* エモーション */ if (md) clif_emotion (&md->bl, mob_db[md->mob_class].skill[md->skillidx].val[0]); break; case NPC_DEFENDER: clif_skill_nodamage (src, bl, skillid, skilllv, 1); break; case WE_MALE: /* 君だけは護るよ */ if (sd && dstsd) { int hp_rate = (skilllv <= 0) ? 0 : skill_db[skillid].hp_rate[skilllv - 1]; int gain_hp = sd->status.max_hp * abs (hp_rate) / 100; // 15% clif_skill_nodamage (src, bl, skillid, gain_hp, 1); battle_heal (NULL, bl, gain_hp, 0, 0); } break; case WE_FEMALE: /* あなたの為に犠牲になります */ if (sd && dstsd) { int sp_rate = (skilllv <= 0) ? 0 : skill_db[skillid].sp_rate[skilllv - 1]; int gain_sp = sd->status.max_sp * abs (sp_rate) / 100; // 15% clif_skill_nodamage (src, bl, skillid, gain_sp, 1); battle_heal (NULL, bl, 0, gain_sp, 0); } break; case WE_CALLPARTNER: /* あなたに会いたい */ if (sd && dstsd) { if (map[sd->bl.m].flag.nomemo) { clif_skill_teleportmessage (sd, 1); return 0; } if ((dstsd = pc_get_partner (sd)) == NULL) { clif_skill_fail (sd, skillid, 0, 0); return 0; } skill_unitsetting (src, skillid, skilllv, sd->bl.x, sd->bl.y, 0); } break; case PF_HPCONVERSION: /* ライフ置き換え */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) { int conv_hp = 0, conv_sp = 0; conv_hp = sd->status.hp / 10; //基本はHPの10% sd->status.hp -= conv_hp; //HPを減らす conv_sp = conv_hp * 20 * skilllv / 100; conv_sp = (sd->status.sp + conv_sp > sd->status.max_sp) ? sd->status.max_sp - sd->status.sp : conv_sp; sd->status.sp += conv_sp; //SPを増やす pc_heal (sd, -conv_hp, conv_sp); clif_heal (sd->fd, SP_SP, conv_sp); } break; case HT_REMOVETRAP: /* リムーブトラップ */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); { struct skill_unit *su = NULL; struct item item_tmp; if ((bl->type == BL_SKILL) && (su = (struct skill_unit *) bl) && (su->group->src_id == src->id || map[bl->m].flag.pvp || map[bl->m].flag.gvg) && (su->group->unit_id >= 0x8f && su->group->unit_id <= 0x99) && (su->group->unit_id != 0x92)) { //罠を取り返す if (sd) { if (battle_config.skill_removetrap_type == 1) { for (int i = 0; i < 10; i++) { if (skill_db[su->group->skill_id].itemid[i] > 0) { memset (&item_tmp, 0, sizeof (item_tmp)); item_tmp.nameid = skill_db[su->group-> skill_id].itemid[i]; item_tmp.identify = 1; int item_flag; if (item_tmp.nameid && (item_flag = pc_additem (sd, &item_tmp, skill_db[su-> group->skill_id].amount [i]))) { clif_additem (sd, 0, 0, item_flag); map_addflooritem (&item_tmp, skill_db[su-> group->skill_id].amount [i], sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0); } } } } else { memset (&item_tmp, 0, sizeof (item_tmp)); item_tmp.nameid = 1065; item_tmp.identify = 1; int item_flag; if (item_tmp.nameid && (item_flag = pc_additem (sd, &item_tmp, 1))) { clif_additem (sd, 0, 0, item_flag); map_addflooritem (&item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0); } } } if (su->group->unit_id == 0x91 && su->group->val2) { struct block_list *target = map_id2bl (su->group->val2); if (target && (target->type == BL_PC || target->type == BL_MOB)) skill_status_change_end (target, SC_ANKLE, -1); } skill_delunit (su); } } break; case HT_SPRINGTRAP: /* スプリングトラップ */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); { struct skill_unit *su = NULL; if ((bl->type == BL_SKILL) && (su = (struct skill_unit *) bl) && (su->group)) { switch (su->group->unit_id) { case 0x8f: /* ブラストマイン */ case 0x90: /* スキッドトラップ */ case 0x93: /* ランドマイン */ case 0x94: /* ショックウェーブトラップ */ case 0x95: /* サンドマン */ case 0x96: /* フラッシャー */ case 0x97: /* フリージングトラップ */ case 0x98: /* クレイモアートラップ */ case 0x99: /* トーキーボックス */ su->group->unit_id = 0x8c; clif_changelook (bl, LOOK_BASE, su->group->unit_id); su->group->limit = DIFF_TICK (tick + 1500, su->group->tick); su->limit = DIFF_TICK (tick + 1500, su->group->tick); } } } break; case BD_ENCORE: /* アンコール */ clif_skill_nodamage (src, bl, skillid, skilllv, 1); if (sd) skill_use_id (sd, src->id, sd->skillid_dance, sd->skilllv_dance); break; case AS_SPLASHER: /* ベナムスプラッシャー */ if ((double) battle_get_max_hp (bl) * 2 / 3 < battle_get_hp (bl)) //HPが2/3以上残っていたら失敗 return 1; clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, skillid, src->id, 0, skill_get_time (skillid, skilllv), 0); break; case PF_MINDBREAKER: /* プロボック */ { struct status_change *sc_data = battle_get_sc_data (bl); /* MVPmobと不死には効かない */ if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない { map_freeblock_unlock (); return 1; } clif_skill_nodamage (src, bl, skillid, skilllv, 1); skill_status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, skill_get_time (skillid, skilllv), 0); if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 skill_castcancel (bl, 0); if (dstsd && dstsd->skilltimer != -1 && (!dstsd->special_state.no_castcancel || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2) skill_castcancel (bl, 0); if (sc_data) { if (sc_data[SC_FREEZE].timer != -1) skill_status_change_end (bl, SC_FREEZE, -1); if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) skill_status_change_end (bl, SC_STONE, -1); if (sc_data[SC_SLEEP].timer != -1) skill_status_change_end (bl, SC_SLEEP, -1); } if (bl->type == BL_MOB) { int range = skill_get_range (skillid, skilllv); if (range < 0) range = battle_get_range (src) - (range + 1); mob_target ((struct mob_data *) bl, src, range); } } break; case RG_CLEANER: //AppleGirl clif_skill_nodamage (src, bl, skillid, skilllv, 1); { struct skill_unit *su = NULL; if ((bl->type == BL_SKILL) && (su = (struct skill_unit *) bl) && (su->group->src_id == src->id || map[bl->m].flag.pvp || map[bl->m].flag.gvg) && (su->group->unit_id == 0xb0)) { //罠を取り返す if (sd) skill_delunit (su); } } break; default: printf ("Unknown skill used:%d\n", skillid); map_freeblock_unlock (); return 1; } map_freeblock_unlock (); return 0; } /*========================================== * スキル使用(詠唱完了、ID指定) *------------------------------------------ */ static void skill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; struct block_list *bl; int range, inf2; nullpo_retv ( sd); if (sd->bl.prev == NULL) //prevが無いのはありなの? return; if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid) /* タイマIDの確認 */ return; if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) > 0) { sd->speed = sd->prev_speed; clif_updatestatus (sd, SP_SPEED); } if (sd->skillid != SA_CASTCANCEL) sd->skilltimer = -1; if ((bl = map_id2bl (sd->skilltarget)) == NULL || bl->prev == NULL) { sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } if (sd->bl.m != bl->m || pc_isdead (sd)) { //マップが違うか自分が死んでいる sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } if (sd->skillid == PR_LEXAETERNA) { struct status_change *sc_data = battle_get_sc_data (bl); if (sc_data && (sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0))) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } else if (sd->skillid == RG_BACKSTAP) { int dir = map_calc_dir (&sd->bl, bl->x, bl->y), t_dir = battle_get_dir (bl); int dist = distance (sd->bl.x, sd->bl.y, bl->x, bl->y); if (bl->type != BL_SKILL && (dist == 0 || map_check_dir (dir, t_dir))) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } inf2 = skill_get_inf2 (sd->skillid); if (((skill_get_inf (sd->skillid) & 1) || inf2 & 4) && // 彼我敵対関係チェック battle_check_target (&sd->bl, bl, BCT_ENEMY) <= 0) { sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } if (inf2 & 0xC00 && sd->bl.id != bl->id) { int fail_flag = 1; if (inf2 & 0x400 && battle_check_target (&sd->bl, bl, BCT_PARTY) > 0) fail_flag = 0; if (inf2 & 0x800 && sd->status.guild_id > 0 && sd->status.guild_id == battle_get_guild_id (bl)) fail_flag = 0; if (fail_flag) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } range = skill_get_range (sd->skillid, sd->skilllv); if (range < 0) range = battle_get_range (&sd->bl) - (range + 1); range += battle_config.pc_skill_add_range; if ((sd->skillid == MO_EXTREMITYFIST && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || (sd->skillid == CH_TIGERFIST && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)) range += skill_get_blewcount (MO_COMBOFINISH, sd->sc_data[SC_COMBO].val2); if (battle_config.skill_out_range_consume) { // changed to allow casting when target walks out of range [Valaris] if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } if (!skill_check_condition (sd, 1)) { /* 使用条件チェック */ sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } sd->skillitem = sd->skillitemlv = -1; if (battle_config.skill_out_range_consume) { if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; return; } } if (battle_config.pc_skill_log) printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); pc_stop_walking (sd, 0); switch (skill_get_nk (sd->skillid)) { /* 攻撃系/吹き飛ばし系 */ case 0: case 2: skill_castend_damage_id (&sd->bl, bl, sd->skillid, sd->skilllv, tick, 0); break; case 1: /* 支援系 */ if ((sd->skillid == AL_HEAL || (sd->skillid == ALL_RESURRECTION && bl->type != BL_PC) || sd->skillid == PR_ASPERSIO) && battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) skill_castend_damage_id (&sd->bl, bl, sd->skillid, sd->skilllv, tick, 0); else skill_castend_nodamage_id (&sd->bl, bl, sd->skillid, sd->skilllv, tick, 0); break; } } /*========================================== * スキル使用(詠唱完了、場所指定の実際の処理) *------------------------------------------ */ int skill_castend_pos2 (struct block_list *src, int x, int y, int skillid, int skilllv, unsigned int tick, int flag) { struct map_session_data *sd = NULL; int i, tmpx = 0, tmpy = 0, x1 = 0, y1 = 0; nullpo_retr (0, src); if (src->type == BL_PC) { nullpo_retr (0, sd = (struct map_session_data *) src); } if (skillid != WZ_METEOR && skillid != WZ_SIGHTRASHER && skillid != AM_CANNIBALIZE && skillid != AM_SPHEREMINE) clif_skill_poseffect (src, skillid, skilllv, x, y, tick); if (skillnotok (skillid, sd)) // [MouseJstr] return 0; switch (skillid) { case PR_BENEDICTIO: /* 聖体降福 */ skill_area_temp[1] = src->id; map_foreachinarea (skill_area_sub, src->m, x - 1, y - 1, x + 1, y + 1, 0, src, skillid, skilllv, tick, flag | BCT_NOENEMY | 1, skill_castend_nodamage_id); map_foreachinarea (skill_area_sub, src->m, x - 1, y - 1, x + 1, y + 1, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); break; case BS_HAMMERFALL: /* ハンマーフォール */ skill_area_temp[1] = src->id; skill_area_temp[2] = x; skill_area_temp[3] = y; map_foreachinarea (skill_area_sub, src->m, x - 2, y - 2, x + 2, y + 2, 0, src, skillid, skilllv, tick, flag | BCT_ENEMY | 2, skill_castend_nodamage_id); break; case HT_DETECTING: /* ディテクティング */ { const int range = 7; map_foreachinarea (skill_status_change_timer_sub, src->m, src->x - range, src->y - range, src->x + range, src->y + range, 0, src, SC_SIGHT, tick); } break; case MG_SAFETYWALL: /* セイフティウォール */ case MG_FIREWALL: /* ファイヤーウォール */ case MG_THUNDERSTORM: /* サンダーストーム */ case AL_PNEUMA: /* ニューマ */ case WZ_ICEWALL: /* アイスウォール */ case WZ_FIREPILLAR: /* ファイアピラー */ case WZ_SIGHTRASHER: case WZ_QUAGMIRE: /* クァグマイア */ case WZ_VERMILION: /* ロードオブヴァーミリオン */ case WZ_FROSTNOVA: /* フロストノヴァ */ case WZ_STORMGUST: /* ストームガスト */ case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ case PR_SANCTUARY: /* サンクチュアリ */ case PR_MAGNUS: /* マグヌスエクソシズム */ case CR_GRANDCROSS: /* グランドクロス */ case HT_SKIDTRAP: /* スキッドトラップ */ case HT_LANDMINE: /* ランドマイン */ case HT_ANKLESNARE: /* アンクルスネア */ case HT_SHOCKWAVE: /* ショックウェーブトラップ */ case HT_SANDMAN: /* サンドマン */ case HT_FLASHER: /* フラッシャー */ case HT_FREEZINGTRAP: /* フリージングトラップ */ case HT_BLASTMINE: /* ブラストマイン */ case HT_CLAYMORETRAP: /* クレイモアートラップ */ case AS_VENOMDUST: /* ベノムダスト */ case AM_DEMONSTRATION: /* デモンストレーション */ case PF_SPIDERWEB: /* スパイダーウェッブ */ case PF_FOGWALL: /* フォグウォール */ case HT_TALKIEBOX: /* トーキーボックス */ skill_unitsetting (src, skillid, skilllv, x, y, 0); break; case RG_GRAFFITI: /* Graffiti [Valaris] */ skill_clear_unitgroup (src); skill_unitsetting (src, skillid, skilllv, x, y, 0); break; case SA_VOLCANO: /* ボルケーノ */ case SA_DELUGE: /* デリュージ */ case SA_VIOLENTGALE: /* バイオレントゲイル */ case SA_LANDPROTECTOR: /* ランドプロテクター */ skill_clear_element_field (src); //既に自分が発動している属性場をクリア skill_unitsetting (src, skillid, skilllv, x, y, 0); break; case WZ_METEOR: //メテオストーム { int flag_ = 0; for (i = 0; i < 2 + (skilllv >> 1); i++) { int j = 0, c; do { tmpx = x + (MRAND (7) - 3); tmpy = y + (MRAND (7) - 3); if (tmpx < 0) tmpx = 0; else if (tmpx >= map[src->m].xs) tmpx = map[src->m].xs - 1; if (tmpy < 0) tmpy = 0; else if (tmpy >= map[src->m].ys) tmpy = map[src->m].ys - 1; j++; } while (((c = map_getcell (src->m, tmpx, tmpy)) == 1 || c == 5) && j < 100); if (j >= 100) continue; if (flag_ == 0) { clif_skill_poseffect (src, skillid, skilllv, tmpx, tmpy, tick); flag_ = 1; } if (i > 0) skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, skillid, skilllv, (x1 << 16) | y1, flag_); x1 = tmpx; y1 = tmpy; } skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, skillid, skilllv, -1, flag_); } break; case AL_WARP: /* ワープポータル */ if (sd) { if (map[sd->bl.m].flag.noteleport) /* テレポ禁止 */ break; clif_skill_warppoint (sd, sd->skillid, sd->status.save_point.map, (sd->skilllv > 1) ? sd->status.memo_point[0].map : "", (sd->skilllv > 2) ? sd->status.memo_point[1].map : "", (sd->skilllv > 3) ? sd->status. memo_point[2].map : ""); } break; case MO_BODYRELOCATION: if (sd) { pc_movepos (sd, x, y); } else if (src->type == BL_MOB) mob_warp ((struct mob_data *) src, -1, x, y, 0); break; case AM_CANNIBALIZE: // バイオプラント if (sd) { int mx, my, id = 0; struct mob_data *md; mx = x; // + (rand()%10 - 5); my = y; // + (rand()%10 - 5); id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1118, 1, ""); if ((md = (struct mob_data *) map_id2bl (id)) != NULL) { md->master_id = sd->bl.id; md->hp = 2210 + skilllv * 200; md->state.special_mob_ai = 1; md->deletetimer = add_timer (gettick () + skill_get_time (skillid, skilllv), mob_timer_delete, id, 0); } clif_skill_poseffect (src, skillid, skilllv, x, y, tick); } break; case AM_SPHEREMINE: // スフィアーマイン if (sd) { int mx, my, id = 0; struct mob_data *md; mx = x; // + (rand()%10 - 5); my = y; // + (rand()%10 - 5); id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1142, 1, ""); if ((md = (struct mob_data *) map_id2bl (id)) != NULL) { md->master_id = sd->bl.id; md->hp = 1000 + skilllv * 200; md->state.special_mob_ai = 2; md->deletetimer = add_timer (gettick () + skill_get_time (skillid, skilllv), mob_timer_delete, id, 0); } clif_skill_poseffect (src, skillid, skilllv, x, y, tick); } break; } return 0; } /*========================================== * スキル使用(詠唱完了、map指定) *------------------------------------------ */ int skill_castend_map (struct map_session_data *sd, int skill_num, const char *mapname) { int x = 0, y = 0; nullpo_retr (0, sd); if (sd->bl.prev == NULL || pc_isdead (sd)) return 0; if (sd->opt1 > 0 || sd->status.option & 2) return 0; //スキルが使えない状態異常中 if (sd->sc_data) { if (sd->sc_data[SC_DIVINA].timer != -1 || sd->sc_data[SC_ROKISWEIL].timer != -1 || sd->sc_data[SC_AUTOCOUNTER].timer != -1 || sd->sc_data[SC_STEELBODY].timer != -1 || sd->sc_data[SC_DANCING].timer != -1 || sd->sc_data[SC_BERSERK].timer != -1) return 0; } if (skill_num != sd->skillid) /* 不正パケットらしい */ return 0; pc_stopattack (sd); if (battle_config.pc_skill_log) printf ("PC %d skill castend skill =%d map=%s\n", sd->bl.id, skill_num, mapname); pc_stop_walking (sd, 0); if (strcmp (mapname, "cancel") == 0) return 0; switch (skill_num) { case AL_TELEPORT: /* テレポート */ if (strcmp (mapname, "Random") == 0) pc_randomwarp (sd, 3); else pc_setpos (sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3); break; case AL_WARP: /* ワープポータル */ { const struct point *p[] = { &sd->status.save_point, &sd->status.memo_point[0], &sd->status.memo_point[1], &sd->status.memo_point[2], }; struct skill_unit_group *group; int i; int maxcount = 0; if ((maxcount = skill_get_maxcount (sd->skillid)) > 0) { int c; for (i = c = 0; i < MAX_SKILLUNITGROUP; i++) { if (sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == sd->skillid) c++; } if (c >= maxcount) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = gettick (); sd->canmove_tick = gettick (); sd->skillitem = sd->skillitemlv = -1; return 0; } } for (i = 0; i < sd->skilllv; i++) { if (strcmp (mapname, p[i]->map) == 0) { x = p[i]->x; y = p[i]->y; break; } } if (x == 0 || y == 0) /* 不正パケット? */ return 0; if (!skill_check_condition (sd, 3)) return 0; if ((group = skill_unitsetting (&sd->bl, sd->skillid, sd->skilllv, sd->skillx, sd->skilly, 0)) == NULL) return 0; CREATE (group->valstr, char, 24); memcpy (group->valstr, map, 24); group->val2 = (x << 16) | y; } break; } return 0; } /*========================================== * スキルユニット設定処理 *------------------------------------------ */ struct skill_unit_group *skill_unitsetting (struct block_list *src, int skillid, int skilllv, int x, int y, int flag) { struct skill_unit_group *group; int i, count = 1, limit_ = 10000, val1_ = 0, val2_ = 0; int target = BCT_ENEMY, interval = 1000, range_ = 0; int dir = 0, aoe_diameter = 0; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills nullpo_retr (0, src); switch (skillid) { /* 設定 */ case MG_SAFETYWALL: /* セイフティウォール */ limit_ = skill_get_time (skillid, skilllv); val2_ = skilllv + 1; interval = -1; target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; break; case MG_FIREWALL: /* ファイヤーウォール */ if (src->x == x && src->y == y) dir = 2; else dir = map_calc_dir (src, x, y); if (dir & 1) count = 5; else count = 3; limit_ = skill_get_time (skillid, skilllv); val2_ = 4 + skilllv; interval = 1; break; case AL_PNEUMA: /* ニューマ */ limit_ = skill_get_time (skillid, skilllv); interval = -1; target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; count = 9; break; case AL_WARP: /* ワープポータル */ target = BCT_ALL; val1_ = skilllv + 6; if (flag == 0) limit_ = 2000; else limit_ = skill_get_time (skillid, skilllv); break; case PR_SANCTUARY: /* サンクチュアリ */ count = 21; limit_ = skill_get_time (skillid, skilllv); val1_ = skilllv + 3; val2_ = (skilllv > 6) ? 777 : skilllv * 100; target = BCT_ALL; range_ = 1; break; case PR_MAGNUS: /* マグヌスエクソシズム */ count = 33; limit_ = skill_get_time (skillid, skilllv); interval = 3000; break; case WZ_FIREPILLAR: /* ファイアーピラー */ if (flag == 0) limit_ = skill_get_time (skillid, skilllv); else limit_ = 1000; interval = 2000; val1_ = skilllv + 2; range_ = 1; break; case MG_THUNDERSTORM: /* サンダーストーム */ limit_ = 500; range_ = 1; break; case WZ_FROSTNOVA: /* フロストノヴァ */ limit_ = 500; range_ = 5; break; case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ limit_ = 500; range_ = 2; break; case WZ_METEOR: /* メテオストーム */ limit_ = 500; range_ = 3; break; case WZ_SIGHTRASHER: limit_ = 500; count = 41; break; case WZ_VERMILION: /* ロードオブヴァーミリオン */ limit_ = 4100; interval = 1000; range_ = 6; break; case WZ_ICEWALL: /* アイスウォール */ limit_ = skill_get_time (skillid, skilllv); count = 5; break; case WZ_STORMGUST: /* ストームガスト */ limit_ = 4600; interval = 450; range_ = 5; break; case WZ_QUAGMIRE: /* クァグマイア */ limit_ = skill_get_time (skillid, skilllv); interval = 200; count = 25; break; case HT_SKIDTRAP: /* スキッドトラップ */ case HT_LANDMINE: /* ランドマイン */ case HT_ANKLESNARE: /* アンクルスネア */ case HT_SANDMAN: /* サンドマン */ case PF_SPIDERWEB: /* スパイダーウェッブ */ case HT_FLASHER: /* フラッシャー */ case HT_FREEZINGTRAP: /* フリージングトラップ */ case HT_BLASTMINE: /* ブラストマイン */ case HT_CLAYMORETRAP: /* クレイモアートラップ */ limit_ = skill_get_time (skillid, skilllv); range_ = 1; break; case HT_TALKIEBOX: /* トーキーボックス */ limit_ = skill_get_time (skillid, skilllv); range_ = 1; target = BCT_ALL; break; case HT_SHOCKWAVE: /* ショックウェーブトラップ */ limit_ = skill_get_time (skillid, skilllv); range_ = 1; val1_ = skilllv * 15 + 10; break; case AS_VENOMDUST: /* ベノムダスト */ limit_ = skill_get_time (skillid, skilllv); interval = 1000; count = 5; break; case CR_GRANDCROSS: /* グランドクロス */ count = 29; limit_ = 1000; interval = 300; break; case SA_VOLCANO: /* ボルケーノ */ case SA_DELUGE: /* デリュージ */ case SA_VIOLENTGALE: /* バイオレントゲイル */ limit_ = skill_get_time (skillid, skilllv); count = skilllv <= 2 ? 25 : (skilllv <= 4 ? 49 : 81); target = BCT_ALL; break; case SA_LANDPROTECTOR: /* グランドクロス */ limit_ = skill_get_time (skillid, skilllv); // changed to get duration from cast_db (moonsoul) val1_ = skilllv * 15 + 10; aoe_diameter = skilllv + skilllv % 2 + 5; target = BCT_ALL; count = aoe_diameter * aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) break; case BD_LULLABY: /* 子守唄 */ case BD_ETERNALCHAOS: /* エターナルカオス */ case BD_ROKISWEIL: /* ロキの叫び */ count = 81; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_ALL; break; case BD_RICHMANKIM: case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ count = 81; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_PARTY; break; case BA_WHISTLE: /* 口笛 */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, BA_MUSICALLESSON) + 1) >> 1; val2_ = ((battle_get_agi (src) / 10) & 0xffff) << 16; val2_ |= (battle_get_luk (src) / 10) & 0xffff; break; case DC_HUMMING: /* ハミング */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, DC_DANCINGLESSON) + 1) >> 1; val2_ = battle_get_dex (src) / 10; break; case BA_DISSONANCE: /* 不協和音 */ case DC_UGLYDANCE: /* 自分勝手なダンス */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_ENEMY; break; case DC_DONTFORGETME: /* 私を忘れないで… */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_ENEMY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, DC_DANCINGLESSON) + 1) >> 1; val2_ = ((battle_get_str (src) / 20) & 0xffff) << 16; val2_ |= (battle_get_agi (src) / 10) & 0xffff; break; case BA_POEMBRAGI: /* ブラギの詩 */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = pc_checkskill ((struct map_session_data *) src, BA_MUSICALLESSON); val2_ = ((battle_get_dex (src) / 10) & 0xffff) << 16; val2_ |= (battle_get_int (src) / 5) & 0xffff; break; case BA_APPLEIDUN: /* イドゥンの林檎 */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = ((pc_checkskill ((struct map_session_data *) src, BA_MUSICALLESSON)) & 0xffff) << 16; else val1_ = 0; val1_ |= (battle_get_vit (src)) & 0xffff; val2_ = 0; //回復用タイムカウンタ(6秒毎に1増加) break; case DC_SERVICEFORYOU: /* サービスフォーユー */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_PARTY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, DC_DANCINGLESSON) + 1) >> 1; val2_ = battle_get_int (src) / 10; break; case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, BA_MUSICALLESSON) + 1) >> 1; val2_ = battle_get_agi (src) / 20; break; case DC_FORTUNEKISS: /* 幸運のキス */ count = 49; limit_ = skill_get_time (skillid, skilllv); range_ = 5; target = BCT_NOENEMY; if (src->type == BL_PC) val1_ = (pc_checkskill ((struct map_session_data *) src, DC_DANCINGLESSON) + 1) >> 1; val2_ = battle_get_luk (src) / 10; break; case AM_DEMONSTRATION: /* デモンストレーション */ limit_ = skill_get_time (skillid, skilllv); interval = 1000; range_ = 1; target = BCT_ENEMY; break; case WE_CALLPARTNER: /* あなたに逢いたい */ limit_ = skill_get_time (skillid, skilllv); range_ = -1; break; case HP_BASILICA: /* バジリカ */ limit_ = skill_get_time (skillid, skilllv); target = BCT_ALL; range_ = 3; //Fix to prevent the priest from walking while Basilica is up. battle_stopwalking (src, 1); skill_status_change_start (src, SC_ANKLE, skilllv, 0, 0, 0, limit_, 0); break; case PA_GOSPEL: /* ゴスペル */ count = 49; target = BCT_PARTY; limit_ = skill_get_time (skillid, skilllv); break; case PF_FOGWALL: /* フォグウォール */ count = 15; limit_ = skill_get_time (skillid, skilllv); break; case RG_GRAFFITI: /* Graffiti */ count = 1; // Leave this at 1 [Valaris] limit_ = 600000; // Time length [Valaris] break; }; nullpo_retr (NULL, group = skill_initunitgroup (src, count, skillid, skilllv, skill_get_unit_id (skillid, flag & 1))); group->limit = limit_; group->val1 = val1_; group->val2 = val2_; group->target_flag = target; group->interval = interval; group->range = range_; if (skillid == HT_TALKIEBOX || skillid == RG_GRAFFITI) { CREATE (group->valstr, char, 80); memcpy (group->valstr, talkie_mes, 80); } for (i = 0; i < count; i++) { struct skill_unit *unit; int ux = x, uy = y, val1 = skilllv, val2 = 0, limit = group->limit, alive = 1; int range = group->range; switch (skillid) { /* 設定 */ case AL_PNEUMA: /* ニューマ */ { static const int dx[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 }; static const int dy[9] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 }; ux += dx[i]; uy += dy[i]; } break; case MG_FIREWALL: /* ファイヤーウォール */ { if (dir & 1) { /* 斜め配置 */ static const int dx[][5] = { {1, 1, 0, 0, -1}, {-1, -1, 0, 0, 1}, }, dy[][5] = { { 1, 0, 0, -1, -1}, { 1, 0, 0, -1, -1},}; ux += dx[(dir >> 1) & 1][i]; uy += dy[(dir >> 1) & 1][i]; } else { /* 上下配置 */ if (dir % 4 == 0) /* 上下 */ ux += i - 1; else /* 左右 */ uy += i - 1; } val2 = group->val2; } break; case PR_SANCTUARY: /* サンクチュアリ */ { static const int dx[] = { -1, 0, 1, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -1, 0, 1 }; static const int dy[] = { -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, }; ux += dx[i]; uy += dy[i]; } break; case PR_MAGNUS: /* マグヌスエクソシズム */ { static const int dx[] = { -1, 0, 1, -1, 0, 1, -3, -2, -1, 0, 1, 2, 3, -3, -2, -1, 0, 1, 2, 3, -3, -2, -1, 0, 1, 2, 3, -1, 0, 1, -1, 0, 1, }; static const int dy[] = { -3, -3, -3, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; ux += dx[i]; uy += dy[i]; } break; case WZ_SIGHTRASHER: { static const int dx[] = { -5, 0, 5, -4, 0, 4, -3, 0, 3, -2, 0, 2, -1, 0, 1, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -1, 0, 1, -2, 0, 2, -3, 0, 3, -4, 0, 4, -5, 0, 5 }; static const int dy[] = { -5, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; ux += dx[i]; uy += dy[i]; } break; case WZ_ICEWALL: /* アイスウォール */ { if (skilllv <= 1) val1 = 500; else val1 = 200 + 200 * skilllv; if (src->x == x && src->y == y) dir = 2; else dir = map_calc_dir (src, x, y); ux += (2 - i) * diry[dir]; uy += (i - 2) * dirx[dir]; } break; case WZ_QUAGMIRE: /* クァグマイア */ ux += (i % 5 - 2); uy += (i / 5 - 2); if (i == 12) range = 2; else range = -1; break; case AS_VENOMDUST: /* ベノムダスト */ { static const int dx[] = { -1, 0, 0, 0, 1 }; static const int dy[] = { 0, -1, 0, 1, 0 }; ux += dx[i]; uy += dy[i]; } break; case CR_GRANDCROSS: /* グランドクロス */ { static const int dx[] = { 0, 0, -1, 0, 1, -2, -1, 0, 1, 2, -4, -3, -2, -1, 0, 1, 2, 3, 4, -2, -1, 0, 1, 2, -1, 0, 1, 0, 0, }; static const int dy[] = { -4, -3, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, }; ux += dx[i]; uy += dy[i]; } break; case SA_VOLCANO: /* ボルケーノ */ case SA_DELUGE: /* デリュージ */ case SA_VIOLENTGALE: /* バイオレントゲイル */ { int u_range = 0, central = 0; if (skilllv <= 2) { u_range = 2; central = 12; } else if (skilllv <= 4) { u_range = 3; central = 24; } else if (skilllv >= 5) { u_range = 4; central = 40; } ux += (i % (u_range * 2 + 1) - u_range); uy += (i / (u_range * 2 + 1) - u_range); if (i == central) range = u_range; //中央のユニットの効果範囲は全範囲 else range = -1; //中央以外のユニットは飾り } break; case SA_LANDPROTECTOR: /* ランドプロテクター */ { int u_range = 0; if (skilllv <= 2) u_range = 3; else if (skilllv <= 4) u_range = 4; else if (skilllv >= 5) u_range = 5; ux += (i % (u_range * 2 + 1) - u_range); uy += (i / (u_range * 2 + 1) - u_range); range = 0; } break; /* ダンスなど */ case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ ux += (i % 9 - 4); uy += (i / 9 - 4); if (i == 40) range = 4; /* 中心の場合は範囲を4にオーバーライド */ else range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ break; case BA_DISSONANCE: /* 不協和音 */ case BA_WHISTLE: /* 口笛 */ case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ case BA_POEMBRAGI: /* ブラギの詩 */ case BA_APPLEIDUN: /* イドゥンの林檎 */ case DC_UGLYDANCE: /* 自分勝手なダンス */ case DC_HUMMING: /* ハミング */ case DC_DONTFORGETME: /* 私を忘れないで… */ case DC_FORTUNEKISS: /* 幸運のキス */ case DC_SERVICEFORYOU: /* サービスフォーユー */ ux += (i % 7 - 3); uy += (i / 7 - 3); if (i == 40) range = 4; /* 中心の場合は範囲を4にオーバーライド */ else range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ break; case PA_GOSPEL: /* ゴスペル */ ux += (i % 7 - 3); uy += (i / 7 - 3); break; case PF_FOGWALL: /* フォグウォール */ ux += (i % 5 - 2); uy += (i / 5 - 1); break; case RG_GRAFFITI: /* Graffiti [Valaris] */ ux += (i % 5 - 2); uy += (i / 5 - 2); break; } //直上スキルの場合設置座標上にランドプロテクターがないかチェック if (range <= 0) map_foreachinarea (skill_landprotector, src->m, ux, uy, ux, uy, BL_SKILL, skillid, &alive); if (skillid == WZ_ICEWALL && alive) { val2 = map_getcell (src->m, ux, uy); if (val2 == 5 || val2 == 1) alive = 0; else { map_setcell (src->m, ux, uy, 5); clif_changemapcell (src->m, ux, uy, 5, 0); } } if (alive) { nullpo_retr (NULL, unit = skill_initunit (group, i, ux, uy)); unit->val1 = val1; unit->val2 = val2; unit->limit = limit; unit->range = range; } } return group; } /*========================================== * スキルユニットの発動イベント *------------------------------------------ */ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, unsigned int tick) { struct skill_unit_group *sg; struct block_list *ss; struct skill_unit_group_tickset *ts; struct map_session_data *srcsd = NULL; int diff, goflag, splash_count = 0; nullpo_retr (0, src); nullpo_retr (0, bl); if (bl->prev == NULL || !src->alive || (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl))) return 0; nullpo_retr (0, sg = src->group); nullpo_retr (0, ss = map_id2bl (sg->src_id)); if (ss->type == BL_PC) nullpo_retr (0, srcsd = (struct map_session_data *) ss); if (srcsd && srcsd->chatID) return 0; if (bl->type != BL_PC && bl->type != BL_MOB) return 0; nullpo_retr (0, ts = skill_unitgrouptickset_search (bl, sg->group_id)); diff = DIFF_TICK (tick, ts->tick); goflag = (diff > sg->interval || diff < 0); if (sg->skill_id == CR_GRANDCROSS && !battle_config.gx_allhit) // 重なっていたら3HITしない goflag = (diff > sg->interval * map_count_oncell (bl->m, bl->x, bl->y) || diff < 0); //対象がLP上に居る場合は無効 map_foreachinarea (skill_landprotector, bl->m, bl->x, bl->y, bl->x, bl->y, BL_SKILL, 0, &goflag); if (!goflag) return 0; ts->tick = tick; ts->group_id = sg->group_id; switch (sg->unit_id) { case 0x83: /* サンクチュアリ */ { int race = battle_get_race (bl); int damage_flag = (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) ? 1 : 0; if (battle_get_hp (bl) >= battle_get_max_hp (bl) && !damage_flag) break; if ((sg->val1--) <= 0) { skill_delunitgroup (sg); return 0; } if (!damage_flag) { int heal = sg->val2; if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) heal = 0; /* 黄金蟲カード(ヒール量0) */ clif_skill_nodamage (&src->bl, bl, AL_HEAL, heal, 1); battle_heal (NULL, bl, heal, 0, 0); } else skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); } break; case 0x84: /* マグヌスエクソシズム */ { int race = battle_get_race (bl); int damage_flag = (battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) ? 1 : 0; if (!damage_flag) return 0; skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); } break; case 0x85: /* ニューマ */ { struct skill_unit *unit2; struct status_change *sc_data = battle_get_sc_data (bl); int type = SC_PNEUMA; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, 0, 0); else if ((unit2 = (struct skill_unit *) sc_data[type].val2) && unit2 != src) { if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, 0, 0); ts->tick -= sg->interval; } } break; case 0x7e: /* セイフティウォール */ { struct skill_unit *unit2; struct status_change *sc_data = battle_get_sc_data (bl); int type = SC_SAFETYWALL; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, 0, 0); else if ((unit2 = (struct skill_unit *) sc_data[type].val2) && unit2 != src) { if (sg->val1 < unit2->group->val1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, 0, 0); ts->tick -= sg->interval; } } break; case 0x86: /* ロードオブヴァーミリオン(&ストームガスト &グランドクロス) */ skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case 0x7f: /* ファイヤーウォール */ if ((src->val2--) > 0) skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); if (src->val2 <= 0) skill_delunit (src); break; case 0x87: /* ファイアーピラー(発動前) */ skill_delunit (src); skill_unitsetting (ss, sg->skill_id, sg->skill_lv, src->bl.x, src->bl.y, 1); break; case 0x88: /* ファイアーピラー(発動後) */ if (DIFF_TICK (tick, sg->tick) < 150) skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case 0x90: /* スキッドトラップ */ { int i, c = skill_get_blewcount (sg->skill_id, sg->skill_lv); if (map[bl->m].flag.gvg) c = 0; for (i = 0; i < c; i++) skill_blown (&src->bl, bl, 1 | 0x30000); sg->unit_id = 0x8c; clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); sg->limit = DIFF_TICK (tick, sg->tick) + 1500; } break; case 0x93: /* ランドマイン */ skill_attack (BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); sg->unit_id = 0x8c; clif_changelook (&src->bl, LOOK_BASE, 0x88); sg->limit = DIFF_TICK (tick, sg->tick) + 1500; break; case 0x8f: /* ブラストマイン */ case 0x94: /* ショックウェーブトラップ */ case 0x95: /* サンドマン */ case 0x96: /* フラッシャー */ case 0x97: /* フリージングトラップ */ case 0x98: /* クレイモアートラップ */ map_foreachinarea (skill_count_target, src->bl.m, src->bl.x - src->range, src->bl.y - src->range, src->bl.x + src->range, src->bl.y + src->range, 0, &src->bl, &splash_count); map_foreachinarea (skill_trap_splash, src->bl.m, src->bl.x - src->range, src->bl.y - src->range, src->bl.x + src->range, src->bl.y + src->range, 0, &src->bl, tick, splash_count); sg->unit_id = 0x8c; clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); sg->limit = DIFF_TICK (tick, sg->tick) + 1500; break; case 0x91: /* アンクルスネア */ { struct status_change *sc_data = battle_get_sc_data (bl); if (sg->val2 == 0 && sc_data && sc_data[SC_ANKLE].timer == -1) { int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE || bl->y / BLOCK_SIZE != src->bl.y / BLOCK_SIZE); int sec = skill_get_time2 (sg->skill_id, sg->skill_lv) - (double) battle_get_agi (bl) * 0.1; if (battle_get_mode (bl) & 0x20) sec = sec / 5; battle_stopwalking (bl, 1); skill_status_change_start (bl, SC_ANKLE, sg->skill_lv, 0, 0, 0, sec, 0); if (moveblock) map_delblock (bl); bl->x = src->bl.x; bl->y = src->bl.y; if (moveblock) map_addblock (bl); if (bl->type == BL_MOB) clif_fixmobpos ((struct mob_data *) bl); else clif_fixpos (bl); clif_01ac (&src->bl); sg->limit = DIFF_TICK (tick, sg->tick) + sec; sg->val2 = bl->id; } } break; case 0x80: /* ワープポータル(発動後) */ if (bl->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) bl; if (sd && src->bl.m == bl->m && src->bl.x == bl->x && src->bl.y == bl->y && src->bl.x == sd->to_x && src->bl.y == sd->to_y) { if (battle_config.chat_warpportal || !sd->chatID) { if ((sg->val1--) > 0) { pc_setpos (sd, sg->valstr, sg->val2 >> 16, sg->val2 & 0xffff, 3); if (sg->src_id == bl->id || (strcmp (map[src->bl.m].name, sg->valstr) == 0 && src->bl.x == (sg->val2 >> 16) && src->bl.y == (sg->val2 & 0xffff))) skill_delunitgroup (sg); } else skill_delunitgroup (sg); } } } else if (bl->type == BL_MOB && battle_config.mob_warpportal) { int m = map_mapname2mapid (sg->valstr); struct mob_data *md; md = (struct mob_data *) bl; mob_warp ((struct mob_data *) bl, m, sg->val2 >> 16, sg->val2 & 0xffff, 3); } break; case 0x8e: /* クァグマイア */ { int type = SkillStatusChangeTable[sg->skill_id]; if (bl->type == BL_PC && ((struct map_session_data *) bl)-> special_state.no_magic_damage) break; if (battle_get_sc_data (bl)[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); } break; case 0x92: /* ベノムダスト */ { struct status_change *sc_data = battle_get_sc_data (bl); int type = SkillStatusChangeTable[sg->skill_id]; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); } break; case 0x9a: /* ボルケーノ */ case 0x9b: /* デリュージ */ case 0x9c: /* バイオレントゲイル */ { struct skill_unit *unit2; struct status_change *sc_data = battle_get_sc_data (bl); int type = SkillStatusChangeTable[sg->skill_id]; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); else if ((unit2 = (struct skill_unit *) sc_data[type].val2) && unit2 != src) { if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) skill_status_change_start (bl, type, sg->skill_lv, (int) src, 0, 0, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); ts->tick -= sg->interval; } } break; case 0x9e: /* 子守唄 */ case 0x9f: /* ニヨルドの宴 */ case 0xa0: /* 永遠の混沌 */ case 0xa1: /* 戦太鼓の響き */ case 0xa2: /* ニーベルングの指輪 */ case 0xa3: /* ロキの叫び */ case 0xa4: /* 深淵の中に */ case 0xa5: /* 不死身のジークフリード */ case 0xa6: /* 不協和音 */ case 0xa7: /* 口笛 */ case 0xa8: /* 夕陽のアサシンクロス */ case 0xa9: /* ブラギの詩 */ case 0xab: /* 自分勝手なダンス */ case 0xac: /* ハミング */ case 0xad: /* 私を忘れないで… */ case 0xae: /* 幸運のキス */ case 0xaf: /* サービスフォーユー */ case 0xb4: { struct skill_unit *unit2; struct status_change *sc_data = battle_get_sc_data (bl); int type = SkillStatusChangeTable[sg->skill_id]; if (sg->src_id == bl->id) break; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, sg->val1, sg->val2, (int) src, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); else if ((unit2 = (struct skill_unit *) sc_data[type].val4) && unit2 != src) { if (unit2->group && DIFF_TICK (sg->tick, unit2->group->tick) > 0) skill_status_change_start (bl, type, sg->skill_lv, sg->val1, sg->val2, (int) src, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); ts->tick -= sg->interval; } } break; case 0xaa: /* イドゥンの林檎 */ { struct skill_unit *unit2; struct status_change *sc_data = battle_get_sc_data (bl); int type = SkillStatusChangeTable[sg->skill_id]; if (sg->src_id == bl->id) break; if (sc_data && sc_data[type].timer == -1) skill_status_change_start (bl, type, sg->skill_lv, (sg->val1) >> 16, (sg->val1) & 0xffff, (int) src, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); else if ((unit2 = (struct skill_unit *) sc_data[type].val4) && unit2 != src) { if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) skill_status_change_start (bl, type, sg->skill_lv, (sg->val1) >> 16, (sg->val1) & 0xffff, (int) src, skill_get_time2 (sg->skill_id, sg->skill_lv), 0); ts->tick -= sg->interval; } } break; case 0xb1: /* デモンストレーション */ skill_attack (BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); if (bl->type == BL_PC && MRAND (100) < sg->skill_lv && battle_config.equipment_breaking) pc_breakweapon ((struct map_session_data *) bl); break; case 0x99: /* トーキーボックス */ if (sg->src_id == bl->id) //自分が踏んでも発動しない break; if (sg->val2 == 0) { clif_talkiebox (&src->bl, sg->valstr); sg->unit_id = 0x8c; clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); sg->limit = DIFF_TICK (tick, sg->tick) + 5000; sg->val2 = -1; //踏んだ } break; case 0xb2: /* あなたを_会いたいです */ case 0xb3: /* ゴスペル */ case 0xb6: /* フォグウォール */ //とりあえず何もしない break; case 0xb7: /* スパイダーウェッブ */ if (sg->val2 == 0) { int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE || bl->y / BLOCK_SIZE != src->bl.y / BLOCK_SIZE); skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick); if (moveblock) map_delblock (bl); bl->x = (&src->bl)->x; bl->y = (&src->bl)->y; if (moveblock) map_addblock (bl); if (bl->type == BL_MOB) clif_fixmobpos ((struct mob_data *) bl); else clif_fixpos (bl); clif_01ac (&src->bl); sg->limit = DIFF_TICK (tick, sg->tick) + skill_get_time2 (sg->skill_id, sg->skill_lv); sg->val2 = bl->id; } break; /* default: if(battle_config.error_log) printf("skill_unit_onplace: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); break;*/ } if (bl->type == BL_MOB && ss != bl) /* スキル使用条件のMOBスキル */ { if (battle_config.mob_changetarget_byskill == 1) { int target = ((struct mob_data *) bl)->target_id; if (ss->type == BL_PC) ((struct mob_data *) bl)->target_id = ss->id; mobskill_use ((struct mob_data *) bl, tick, MSC_SKILLUSED | (sg->skill_id << 16)); ((struct mob_data *) bl)->target_id = target; } else mobskill_use ((struct mob_data *) bl, tick, MSC_SKILLUSED | (sg->skill_id << 16)); } return 0; } /*========================================== * スキルユニットから離脱する(もしくはしている)場合 *------------------------------------------ */ static int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned int tick) { struct skill_unit_group *sg; nullpo_retr (0, src); nullpo_retr (0, bl); nullpo_retr (0, sg = src->group); if (bl->prev == NULL || !src->alive) return 0; if (bl->type != BL_PC && bl->type != BL_MOB) return 0; switch (sg->unit_id) { case 0x7e: /* セイフティウォール */ case 0x85: /* ニューマ */ case 0x8e: /* クァグマイア */ { struct status_change *sc_data = battle_get_sc_data (bl); int type = (sg->unit_id == 0x85) ? SC_PNEUMA : ((sg->unit_id == 0x7e) ? SC_SAFETYWALL : SC_QUAGMIRE); if ((type != SC_QUAGMIRE || bl->type != BL_MOB) && sc_data && sc_data[type].timer != -1 && ((struct skill_unit *) sc_data[type].val2) == src) { skill_status_change_end (bl, type, -1); } } break; case 0x91: /* アンクルスネア */ { struct block_list *target = map_id2bl (sg->val2); if (target && target == bl) { skill_status_change_end (bl, SC_ANKLE, -1); sg->limit = DIFF_TICK (tick, sg->tick) + 1000; } } break; case 0xb5: case 0xb8: { struct block_list *target = map_id2bl (sg->val2); if (target == bl) skill_status_change_end (bl, SC_SPIDERWEB, -1); sg->limit = DIFF_TICK (tick, sg->tick) + 1000; } break; case 0xb6: { struct block_list *target = map_id2bl (sg->val2); if (target == bl) skill_status_change_end (bl, SC_FOGWALL, -1); sg->limit = DIFF_TICK (tick, sg->tick) + 1000; } break; case 0x9a: /* ボルケーノ */ case 0x9b: /* デリュージ */ case 0x9c: /* バイオレントゲイル */ { struct status_change *sc_data = battle_get_sc_data (bl); struct skill_unit *su; int type = SkillStatusChangeTable[sg->skill_id]; if (sc_data && sc_data[type].timer != -1 && (su = ((struct skill_unit *) sc_data[type].val2)) && su == src) { skill_status_change_end (bl, type, -1); } } break; case 0x9e: /* 子守唄 */ case 0x9f: /* ニヨルドの宴 */ case 0xa0: /* 永遠の混沌 */ case 0xa1: /* 戦太鼓の響き */ case 0xa2: /* ニーベルングの指輪 */ case 0xa3: /* ロキの叫び */ case 0xa4: /* 深淵の中に */ case 0xa5: /* 不死身のジークフリード */ case 0xa6: /* 不協和音 */ case 0xa7: /* 口笛 */ case 0xa8: /* 夕陽のアサシンクロス */ case 0xa9: /* ブラギの詩 */ case 0xaa: /* イドゥンの林檎 */ case 0xab: /* 自分勝手なダンス */ case 0xac: /* ハミング */ case 0xad: /* 私を忘れないで… */ case 0xae: /* 幸運のキス */ case 0xaf: /* サービスフォーユー */ case 0xb4: { struct status_change *sc_data = battle_get_sc_data (bl); struct skill_unit *su; int type = SkillStatusChangeTable[sg->skill_id]; if (sc_data && sc_data[type].timer != -1 && (su = ((struct skill_unit *) sc_data[type].val4)) && su == src) { skill_status_change_end (bl, type, -1); } } break; case 0xb7: /* スパイダーウェッブ */ { struct block_list *target = map_id2bl (sg->val2); if (target && target == bl) skill_status_change_end (bl, SC_SPIDERWEB, -1); sg->limit = DIFF_TICK (tick, sg->tick) + 1000; } break; /* default: if(battle_config.error_log) printf("skill_unit_onout: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); break;*/ } skill_unitgrouptickset_delete (bl, sg->group_id); return 0; } /*========================================== * スキルユニットの削除イベント *------------------------------------------ */ static int skill_unit_ondelete (struct skill_unit *src, struct block_list *bl, unsigned int tick) { struct skill_unit_group *sg; nullpo_retr (0, src); nullpo_retr (0, bl); nullpo_retr (0, sg = src->group); if (bl->prev == NULL || !src->alive) return 0; if (bl->type != BL_PC && bl->type != BL_MOB) return 0; switch (sg->unit_id) { case 0x85: /* ニューマ */ case 0x7e: /* セイフティウォール */ case 0x8e: /* クァグマイヤ */ case 0x9a: /* ボルケーノ */ case 0x9b: /* デリュージ */ case 0x9c: /* バイオレントゲイル */ case 0x9e: /* 子守唄 */ case 0x9f: /* ニヨルドの宴 */ case 0xa0: /* 永遠の混沌 */ case 0xa1: /* 戦太鼓の響き */ case 0xa2: /* ニーベルングの指輪 */ case 0xa3: /* ロキの叫び */ case 0xa4: /* 深淵の中に */ case 0xa5: /* 不死身のジークフリード */ case 0xa6: /* 不協和音 */ case 0xa7: /* 口笛 */ case 0xa8: /* 夕陽のアサシンクロス */ case 0xa9: /* ブラギの詩 */ case 0xaa: /* イドゥンの林檎 */ case 0xab: /* 自分勝手なダンス */ case 0xac: /* ハミング */ case 0xad: /* 私を忘れないで… */ case 0xae: /* 幸運のキス */ case 0xaf: /* サービスフォーユー */ case 0xb4: return skill_unit_onout (src, bl, tick); /* default: if(battle_config.error_log) printf("skill_unit_ondelete: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); break;*/ } skill_unitgrouptickset_delete (bl, sg->group_id); return 0; } /*========================================== * スキルユニットの限界イベント *------------------------------------------ */ static int skill_unit_onlimit (struct skill_unit *src, unsigned int tick) { struct skill_unit_group *sg; nullpo_retr (0, src); nullpo_retr (0, sg = src->group); switch (sg->unit_id) { case 0x81: /* ワープポータル(発動前) */ { struct skill_unit_group *group = skill_unitsetting (map_id2bl (sg->src_id), sg->skill_id, sg->skill_lv, src->bl.x, src->bl.y, 1); if (group == NULL) return 0; CREATE (group->valstr, char, 24); memcpy (group->valstr, sg->valstr, 24); group->val2 = sg->val2; } break; case 0x8d: /* アイスウォール */ map_setcell (src->bl.m, src->bl.x, src->bl.y, src->val2); clif_changemapcell (src->bl.m, src->bl.x, src->bl.y, src->val2, 1); break; case 0xb2: /* あなたに会いたい */ { struct map_session_data *sd = NULL; struct map_session_data *p_sd = NULL; if ((sd = (struct map_session_data *) (map_id2bl (sg->src_id))) == NULL) return 0; if ((p_sd = pc_get_partner (sd)) == NULL) return 0; pc_setpos (p_sd, map[src->bl.m].name, src->bl.x, src->bl.y, 3); } break; } return 0; } /*========================================== * スキルユニットのダメージイベント *------------------------------------------ */ int skill_unit_ondamaged (struct skill_unit *src, struct block_list *bl, int damage, unsigned int tick) { struct skill_unit_group *sg; nullpo_retr (0, src); nullpo_retr (0, sg = src->group); switch (sg->unit_id) { case 0x8d: /* アイスウォール */ src->val1 -= damage; break; case 0x8f: /* ブラストマイン */ case 0x98: /* クレイモアートラップ */ skill_blown (bl, &src->bl, 2); //吹き飛ばしてみる break; default: damage = 0; break; } return damage; } /*---------------------------------------------------------------------------- */ /*========================================== * スキル使用(詠唱完了、場所指定) *------------------------------------------ */ static void skill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; int range, maxcount; nullpo_retv (sd); if (sd->bl.prev == NULL) return; if (sd->skilltimer != tid) /* タイマIDの確認 */ return; if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) > 0) { sd->speed = sd->prev_speed; clif_updatestatus (sd, SP_SPEED); } sd->skilltimer = -1; if (pc_isdead (sd)) { sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } if (battle_config.pc_skill_reiteration == 0) { range = -1; switch (sd->skillid) { case MG_SAFETYWALL: case WZ_FIREPILLAR: case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_SHOCKWAVE: case HT_SANDMAN: case HT_FLASHER: case HT_FREEZINGTRAP: case HT_BLASTMINE: case HT_CLAYMORETRAP: case HT_TALKIEBOX: case AL_WARP: case PF_SPIDERWEB: /* スパイダーウェッブ */ case RG_GRAFFITI: /* グラフィティ */ range = 0; break; case AL_PNEUMA: range = 1; break; } if (range >= 0) { if (skill_check_unit_range (sd->bl.m, sd->skillx, sd->skilly, range, sd->skillid) > 0) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } } if (battle_config.pc_skill_nofootset) { range = -1; switch (sd->skillid) { case WZ_FIREPILLAR: case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_SHOCKWAVE: case HT_SANDMAN: case HT_FLASHER: case HT_FREEZINGTRAP: case HT_BLASTMINE: case HT_CLAYMORETRAP: case HT_TALKIEBOX: case PF_SPIDERWEB: /* スパイダーウェッブ */ case WZ_ICEWALL: range = 1; break; case AL_WARP: range = 0; break; } if (range >= 0) { if (skill_check_unit_range2 (sd->bl.m, sd->skillx, sd->skilly, range) > 0) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } } if (battle_config.pc_land_skill_limit) { maxcount = skill_get_maxcount (sd->skillid); if (maxcount > 0) { int i, c; for (i = c = 0; i < MAX_SKILLUNITGROUP; i++) { if (sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == sd->skillid) c++; } if (c >= maxcount) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } } range = skill_get_range (sd->skillid, sd->skilllv); if (range < 0) range = battle_get_range (&sd->bl) - (range + 1); range += battle_config.pc_skill_add_range; if (battle_config.skill_out_range_consume) { // changed to allow casting when target walks out of range [Valaris] if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } } if (!skill_check_condition (sd, 1)) { /* 使用条件チェック */ sd->canact_tick = tick; sd->canmove_tick = tick; sd->skillitem = sd->skillitemlv = -1; return; } sd->skillitem = sd->skillitemlv = -1; if (battle_config.skill_out_range_consume) { if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->canact_tick = tick; sd->canmove_tick = tick; return; } } if (battle_config.pc_skill_log) printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); pc_stop_walking (sd, 0); skill_castend_pos2 (&sd->bl, sd->skillx, sd->skilly, sd->skillid, sd->skilllv, tick, 0); } /*========================================== * 範囲内キャラ存在確認判定処理(foreachinarea) *------------------------------------------ */ static void skill_check_condition_char_sub (struct block_list *bl, va_list ap) { int *c; struct block_list *src; struct map_session_data *sd; struct map_session_data *ssd; struct pc_base_job s_class; struct pc_base_job ss_class; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (sd = (struct map_session_data *) bl); nullpo_retv (src = va_arg (ap, struct block_list *)); nullpo_retv (c = va_arg (ap, int *)); nullpo_retv (ssd = (struct map_session_data *) src); s_class = pc_calc_base_job (sd->status.pc_class); //チェックしない設定ならcにありえない大きな数字を返して終了 if (!battle_config.player_skill_partner_check) { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ (*c) = 99; return; } ; ss_class = pc_calc_base_job (ssd->status.pc_class); switch (ssd->skillid) { case PR_BENEDICTIO: /* 聖体降福 */ if (sd != ssd && (sd->status.pc_class == 4 || sd->status.pc_class == 8 || sd->status.pc_class == 15 || sd->status.pc_class == 4005 || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) && sd->status.sp >= 10) (*c)++; break; case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BD_RAGNAROK: /* 神々の黄昏 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ if (sd != ssd && ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && pc_checkskill (sd, ssd->skillid) > 0 && (*c) == 0 && sd->status.party_id == ssd->status.party_id && !pc_issit (sd) && sd->sc_data[SC_DANCING].timer == -1) (*c) = pc_checkskill (sd, ssd->skillid); break; } return; } /*========================================== * 範囲内キャラ存在確認判定後スキル使用処理(foreachinarea) *------------------------------------------ */ static void skill_check_condition_use_sub (struct block_list *bl, va_list ap) { int *c; struct block_list *src; struct map_session_data *sd; struct map_session_data *ssd; struct pc_base_job s_class; struct pc_base_job ss_class; int skillid, skilllv; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (sd = (struct map_session_data *) bl); nullpo_retv (src = va_arg (ap, struct block_list *)); nullpo_retv (c = va_arg (ap, int *)); nullpo_retv (ssd = (struct map_session_data *) src); s_class = pc_calc_base_job (sd->status.pc_class); //チェックしない設定ならcにありえない大きな数字を返して終了 if (!battle_config.player_skill_partner_check) { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ (*c) = 99; return; } ss_class = pc_calc_base_job (ssd->status.pc_class); skillid = ssd->skillid; skilllv = ssd->skilllv; switch (skillid) { case PR_BENEDICTIO: /* 聖体降福 */ if (sd != ssd && (sd->status.pc_class == 4 || sd->status.pc_class == 8 || sd->status.pc_class == 15 || sd->status.pc_class == 4005 || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) && sd->status.sp >= 10) { sd->status.sp -= 10; pc_calcstatus (sd, 0); (*c)++; } break; case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BD_RAGNAROK: /* 神々の黄昏 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ if (sd != ssd && //本人以外で ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && //自分がダンサーならバードで pc_checkskill (sd, skillid) > 0 && //スキルを持っていて (*c) == 0 && //最初の一人で sd->status.party_id == ssd->status.party_id && //パーティーが同じで !pc_issit (sd) && //座ってない sd->sc_data[SC_DANCING].timer == -1 //ダンス中じゃない ) { ssd->sc_data[SC_DANCING].val4 = bl->id; clif_skill_nodamage (bl, src, skillid, skilllv, 1); skill_status_change_start (bl, SC_DANCING, skillid, ssd->sc_data[SC_DANCING].val2, 0, src->id, skill_get_time (skillid, skilllv) + 1000, 0); sd->skillid_dance = sd->skillid = skillid; sd->skilllv_dance = sd->skilllv = skilllv; (*c)++; } break; } } /*========================================== * 範囲内バイオプラント、スフィアマイン用Mob存在確認判定処理(foreachinarea) *------------------------------------------ */ static void skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap) { int *c, src_id = 0, mob_class = 0; struct mob_data *md; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (md = (struct mob_data *) bl); if (!(src_id = va_arg (ap, int))) return; if (!(mob_class = va_arg (ap, int))) return; nullpo_retv (c = va_arg (ap, int *)); if (md->mob_class == mob_class && md->master_id == src_id) (*c)++; } /*========================================== * スキル使用条件(偽で使用失敗) *------------------------------------------ */ int skill_check_condition (struct map_session_data *sd, int type) { int hp, sp, hp_rate, sp_rate, zeny, weapon, state, spiritball, skill, lv, mhp; int index[10], itemid[10], amount[10]; nullpo_retr (0, sd); if (battle_config.gm_skilluncond > 0 && pc_isGM (sd) >= battle_config.gm_skilluncond) { sd->skillitem = sd->skillitemlv = -1; return 1; } if (sd->opt1 > 0) { clif_skill_fail (sd, sd->skillid, 0, 0); sd->skillitem = sd->skillitemlv = -1; return 0; } if (pc_is90overweight (sd)) { clif_skill_fail (sd, sd->skillid, 9, 0); sd->skillitem = sd->skillitemlv = -1; return 0; } if (sd->skillid == AC_MAKINGARROW && sd->state.make_arrow_flag == 1) { sd->skillitem = sd->skillitemlv = -1; return 0; } /*if(sd->skillid == AM_PHARMACY && sd->state.produce_flag == 1) { * sd->skillitem = sd->skillitemlv = -1; * return 0; * } */ if (sd->skillitem == sd->skillid) { /* アイテムの場合無条件成功 */ if (type & 1) sd->skillitem = sd->skillitemlv = -1; return 1; } if (sd->opt1 > 0) { clif_skill_fail (sd, sd->skillid, 0, 0); return 0; } if (sd->sc_data) { if (sd->sc_data[SC_DIVINA].timer != -1 || sd->sc_data[SC_ROKISWEIL].timer != -1 || (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) || sd->sc_data[SC_STEELBODY].timer != -1 || sd->sc_data[SC_BERSERK].timer != -1) { clif_skill_fail (sd, sd->skillid, 0, 0); return 0; /* 状態異常や沈黙など */ } } skill = sd->skillid; lv = sd->skilllv; hp = skill_get_hp (skill, lv); /* 消費HP */ sp = skill_get_sp (skill, lv); /* 消費SP */ if ((sd->skillid_old == BD_ENCORE) && skill == sd->skillid_dance) sp = sp / 2; //アンコール時はSP消費が半分 hp_rate = (lv <= 0) ? 0 : skill_db[skill].hp_rate[lv - 1]; sp_rate = (lv <= 0) ? 0 : skill_db[skill].sp_rate[lv - 1]; zeny = skill_get_zeny (skill, lv); weapon = skill_db[skill].weapon; state = skill_db[skill].state; spiritball = (lv <= 0) ? 0 : skill_db[skill].spiritball[lv - 1]; mhp = skill_get_mhp (skill, lv); /* 消費HP */ for (int i = 0; i < 10; i++) { itemid[i] = skill_db[skill].itemid[i]; amount[i] = skill_db[skill].amount[i]; } if (mhp > 0) hp += (sd->status.max_hp * mhp) / 100; if (hp_rate > 0) hp += (sd->status.hp * hp_rate) / 100; else hp += (sd->status.max_hp * abs (hp_rate)) / 100; if (sp_rate > 0) sp += (sd->status.sp * sp_rate) / 100; else sp += (sd->status.max_sp * abs (sp_rate)) / 100; if (sd->dsprate != 100) sp = sp * sd->dsprate / 100; /* 消費SP修正 */ switch (skill) { case SA_CASTCANCEL: if (sd->skilltimer == -1) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case BS_MAXIMIZE: /* マキシマイズパワー */ case NV_TRICKDEAD: /* 死んだふり */ case TF_HIDING: /* ハイディング */ case AS_CLOAKING: /* クローキング */ case CR_AUTOGUARD: /* オートガード */ case CR_DEFENDER: /* ディフェンダー */ case ST_CHASEWALK: if (sd->sc_data[SkillStatusChangeTable[skill]].timer != -1) return 1; /* 解除する場合はSP消費しない */ break; case AL_TELEPORT: case AL_WARP: if (map[sd->bl.m].flag.noteleport) { clif_skill_teleportmessage (sd, 0); return 0; } break; case MO_CALLSPIRITS: /* 気功 */ if (sd->spiritball >= lv) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case CH_SOULCOLLECT: /* 狂気功 */ if (sd->spiritball >= 5) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case MO_FINGEROFFENSIVE: //指弾 if (sd->spiritball > 0 && sd->spiritball < spiritball) { spiritball = sd->spiritball; sd->spiritball_old = sd->spiritball; } else sd->spiritball_old = lv; break; case MO_CHAINCOMBO: //連打掌 if (sd->sc_data[SC_BLADESTOP].timer == -1) { if (sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_TRIPLEATTACK) return 0; } break; case MO_COMBOFINISH: //猛龍拳 if (sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_CHAINCOMBO) return 0; break; case CH_TIGERFIST: //伏虎拳 if (sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH) return 0; break; case CH_CHAINCRUSH: //連柱崩撃 if (sd->sc_data[SC_COMBO].timer == -1) return 0; if (sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST) return 0; break; case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 if ((sd->sc_data[SC_COMBO].timer != -1 && (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH || sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) || sd->sc_data[SC_BLADESTOP].timer != -1) spiritball--; break; case BD_ADAPTATION: /* アドリブ */ { struct skill_unit_group *group = NULL; if (sd->sc_data[SC_DANCING].timer == -1 || ((group = (struct skill_unit_group *) sd->sc_data[SC_DANCING].val2) && (skill_get_time (sd->sc_data[SC_DANCING].val1, group->skill_lv) - sd->sc_data[SC_DANCING].val3 * 1000) <= skill_get_time2 (skill, lv))) { //ダンス中で使用後5秒以上のみ? clif_skill_fail (sd, skill, 0, 0); return 0; } } break; case PR_BENEDICTIO: /* 聖体降福 */ { int range = 1; int c = 0; if (!(type & 1)) { map_foreachinarea (skill_check_condition_char_sub, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &sd->bl, &c); if (c < 2) { clif_skill_fail (sd, skill, 0, 0); return 0; } } else { map_foreachinarea (skill_check_condition_use_sub, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &sd->bl, &c); } } break; case WE_CALLPARTNER: /* あなたに逢いたい */ if (!sd->status.partner_id) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case AM_CANNIBALIZE: /* バイオプラント */ case AM_SPHEREMINE: /* スフィアーマイン */ if (type & 1) { int c = 0; int maxcount = skill_get_maxcount (skill); int mob_class = (skill == AM_CANNIBALIZE) ? 1118 : 1142; if (battle_config.pc_land_skill_limit && maxcount > 0) { map_foreachinarea (skill_check_condition_mob_master_sub, sd->bl.m, 0, 0, map[sd->bl.m].xs, map[sd->bl.m].ys, BL_MOB, sd->bl.id, mob_class, &c); if (c >= maxcount) { clif_skill_fail (sd, skill, 0, 0); return 0; } } } break; case MG_FIREWALL: /* ファイアーウォール */ /* 数制限 */ if (battle_config.pc_land_skill_limit) { int maxcount = skill_get_maxcount (skill); if (maxcount > 0) { int i, c; for (i = c = 0; i < MAX_SKILLUNITGROUP; i++) { if (sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill) c++; } if (c >= maxcount) { clif_skill_fail (sd, skill, 0, 0); return 0; } } } break; } if (!(type & 2)) { if (hp > 0 && sd->status.hp < hp) { /* HPチェック */ clif_skill_fail (sd, skill, 2, 0); /* HP不足:失敗通知 */ return 0; } if (sp > 0 && sd->status.sp < sp) { /* SPチェック */ clif_skill_fail (sd, skill, 1, 0); /* SP不足:失敗通知 */ return 0; } if (zeny > 0 && sd->status.zeny < zeny) { clif_skill_fail (sd, skill, 5, 0); return 0; } if (!(weapon & (1 << sd->status.weapon))) { clif_skill_fail (sd, skill, 6, 0); return 0; } if (spiritball > 0 && sd->spiritball < spiritball) { clif_skill_fail (sd, skill, 0, 0); // 氣球不足 return 0; } } switch (state) { case ST_HIDING: if (!(sd->status.option & 2)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_CLOAKING: if (!(sd->status.option & 4)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_HIDDEN: if (!pc_ishiding (sd)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_RIDING: if (!pc_isriding (sd)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_FALCON: if (!pc_isfalcon (sd)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_CART: if (!pc_iscarton (sd)) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_SHIELD: if (sd->status.shield <= 0) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_SIGHT: if (sd->sc_data[SC_SIGHT].timer == -1 && type & 1) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_EXPLOSIONSPIRITS: if (sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_RECOV_WEIGHT_RATE: if (battle_config.natural_heal_weight_rate <= 100 && sd->weight * 100 / sd->max_weight >= battle_config.natural_heal_weight_rate) { clif_skill_fail (sd, skill, 0, 0); return 0; } break; case ST_MOVE_ENABLE: { struct walkpath_data wpd; if (path_search (&wpd, sd->bl.m, sd->bl.x, sd->bl.y, sd->skillx, sd->skilly, 1) == -1) { clif_skill_fail (sd, skill, 0, 0); return 0; } } break; case ST_WATER: if (map_getcell (sd->bl.m, sd->bl.x, sd->bl.y) != 3 && (sd->sc_data[SC_DELUGE].timer == -1)) { //水場判定 clif_skill_fail (sd, skill, 0, 0); return 0; } break; } for (int i = 0; i < 10; i++) { int x = lv % 11 - 1; index[i] = -1; if (itemid[i] <= 0) continue; if (itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone) continue; if (((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) && sd->sc_data[SC_INTOABYSS].timer != -1) continue; if (skill == AM_POTIONPITCHER && i != x) continue; index[i] = pc_search_inventory (sd, itemid[i]); if (index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) { if (itemid[i] == 716 || itemid[i] == 717) clif_skill_fail (sd, skill, (7 + (itemid[i] - 716)), 0); else clif_skill_fail (sd, skill, 0, 0); return 0; } } if (!(type & 1)) return 1; if (skill != AM_POTIONPITCHER) { if (skill == AL_WARP && !(type & 2)) return 1; for (int i = 0; i < 10; i++) { if (index[i] >= 0) pc_delitem (sd, index[i], amount[i], 0); // アイテム消費 } } if (type & 2) return 1; pc_heal (sd, -sp, -hp); // [Fate] This might suppress some dupe messages /* if(sp > 0) { // SP消費 */ /* sd->status.sp-=sp; */ /* clif_updatestatus(sd,SP_SP); */ /* } */ /* if(hp > 0) { // HP消費 */ /* sd->status.hp-=hp; */ /* clif_updatestatus(sd,SP_HP); */ /* } */ if (zeny > 0) // Zeny消費 pc_payzeny (sd, zeny); if (spiritball > 0) // 氣球消費 pc_delspiritball (sd, spiritball, 0); return 1; } /*========================================== * 詠唱時間計算 *------------------------------------------ */ int skill_castfix (struct block_list *bl, int time) { struct map_session_data *sd; struct mob_data *md; // [Valaris] struct status_change *sc_data; int dex; int castrate = 100; int skill, lv, castnodex; nullpo_retr (0, bl); if (bl->type == BL_MOB) { // Crash fix [Valaris] md = (struct mob_data *) bl; skill = md->skillid; lv = md->skilllv; } else { sd = (struct map_session_data *) bl; skill = sd->skillid; lv = sd->skilllv; } sc_data = battle_get_sc_data (bl); dex = battle_get_dex (bl); if (skill > MAX_SKILL_DB || skill < 0) return 0; castnodex = skill_get_castnodex (skill, lv); if (time == 0) return 0; if (castnodex > 0 && bl->type == BL_PC) castrate = ((struct map_session_data *) bl)->castrate; else if (castnodex <= 0 && bl->type == BL_PC) { castrate = ((struct map_session_data *) bl)->castrate; time = time * castrate * (battle_config.castrate_dex_scale - dex) / (battle_config.castrate_dex_scale * 100); time = time * battle_config.cast_rate / 100; } /* サフラギウム */ if (sc_data && sc_data[SC_SUFFRAGIUM].timer != -1) { time = time * (100 - sc_data[SC_SUFFRAGIUM].val1 * 15) / 100; skill_status_change_end (bl, SC_SUFFRAGIUM, -1); } /* ブラギの詩 */ if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) time = time * (100 - (sc_data[SC_POEMBRAGI].val1 * 3 + sc_data[SC_POEMBRAGI].val2 + (sc_data[SC_POEMBRAGI].val3 >> 16))) / 100; return (time > 0) ? time : 0; } /*========================================== * ディレイ計算 *------------------------------------------ */ int skill_delayfix (struct block_list *bl, int time) { struct status_change *sc_data; nullpo_retr (0, bl); sc_data = battle_get_sc_data (bl); if (time <= 0) return 0; if (bl->type == BL_PC) { if (battle_config.delay_dependon_dex) /* dexの影響を計算する */ time = time * (battle_config.castrate_dex_scale - battle_get_dex (bl)) / battle_config.castrate_dex_scale; time = time * battle_config.delay_rate / 100; } /* ブラギの詩 */ if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) time = time * (100 - (sc_data[SC_POEMBRAGI].val1 * 3 + sc_data[SC_POEMBRAGI].val2 + (sc_data[SC_POEMBRAGI].val3 & 0xffff))) / 100; return (time > 0) ? time : 0; } /*========================================== * スキル使用(ID指定) *------------------------------------------ */ int skill_use_id (struct map_session_data *sd, int target_id, int skill_num, int skill_lv) { unsigned int tick; int casttime = 0, delay = 0, skill, range_; struct map_session_data *target_sd = NULL; int forcecast = 0; struct block_list *bl; struct status_change *sc_data; tick = gettick (); nullpo_retr (0, sd); if ((bl = map_id2bl (target_id)) == NULL) { /* if(battle_config.error_log) printf("skill target not found %d\n",target_id); */ return 0; } if (sd->bl.m != bl->m || pc_isdead (sd)) return 0; if (skillnotok (skill_num, sd)) // [MouseJstr] return 0; if (sd->skillid == WZ_ICEWALL && map[sd->bl.m].flag.noicewall && !map[sd->bl.m].flag.pvp) { // noicewall flag [Valaris] clif_skill_fail (sd, sd->skillid, 0, 0); return 0; } sc_data = sd->sc_data; /* 沈黙や異常(ただし、グリムなどの判定をする) */ if (sd->opt1 > 0) return 0; if (sd->sc_data) { if (sc_data[SC_CHASEWALK].timer != -1) return 0; if (sc_data[SC_VOLCANO].timer != -1) { if (skill_num == WZ_ICEWALL) return 0; } if (sc_data[SC_ROKISWEIL].timer != -1) { if (skill_num == BD_ADAPTATION) return 0; } if (sd->sc_data[SC_DIVINA].timer != -1 || sd->sc_data[SC_ROKISWEIL].timer != -1 || (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) || sd->sc_data[SC_STEELBODY].timer != -1 || sd->sc_data[SC_BERSERK].timer != -1) { return 0; /* 状態異常や沈黙など */ } if (sc_data[SC_BLADESTOP].timer != -1) { int lv = sc_data[SC_BLADESTOP].val1; if (sc_data[SC_BLADESTOP].val2 == 1) return 0; //白羽された側なのでダメ if (lv == 1) return 0; if (lv == 2 && skill_num != MO_FINGEROFFENSIVE) return 0; if (lv == 3 && skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE) return 0; if (lv == 4 && skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) return 0; if (lv == 5 && skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO && skill_num != MO_EXTREMITYFIST) return 0; } } if (sd->status.option & 4 && skill_num == TF_HIDING) return 0; if (sd->status.option & 2 && skill_num != TF_HIDING && skill_num != AS_GRIMTOOTH && skill_num != RG_BACKSTAP && skill_num != RG_RAID) return 0; if (map[sd->bl.m].flag.gvg) { //GvGで使用できないスキル switch (skill_num) { case SM_ENDURE: case AL_TELEPORT: case AL_WARP: case WZ_ICEWALL: case TF_BACKSLIDING: case LK_BERSERK: case HP_BASILICA: case ST_CHASEWALK: return 0; } } /* 演奏/ダンス中 */ if (sc_data && sc_data[SC_DANCING].timer != -1) { // if(battle_config.pc_skill_log) // printf("dancing! %d\n",skill_num); if (sc_data[SC_DANCING].val4 && skill_num != BD_ADAPTATION) //合奏中はアドリブ以外不可 return 0; if (skill_num != BD_ADAPTATION && skill_num != BA_MUSICALSTRIKE && skill_num != DC_THROWARROW) { return 0; } } if (skill_get_inf2 (skill_num) & 0x200 && sd->bl.id == target_id) return 0; //直前のスキルが何か覚える必要のあるスキル switch (skill_num) { case SA_CASTCANCEL: if (sd->skillid != skill_num) { //キャストキャンセル自体は覚えない sd->skillid_old = sd->skillid; sd->skilllv_old = sd->skilllv; break; } case BD_ENCORE: /* アンコール */ if (!sd->skillid_dance) { //前回使用した踊りがないとだめ clif_skill_fail (sd, skill_num, 0, 0); return 0; } else { sd->skillid_old = skill_num; } break; } sd->skillid = skill_num; sd->skilllv = skill_lv; switch (skill_num) { //事前にレベルが変わったりするスキル case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BD_RAGNAROK: /* 神々の黄昏 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ { int range = 1; int c = 0; map_foreachinarea (skill_check_condition_char_sub, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &sd->bl, &c); if (c < 1) { clif_skill_fail (sd, skill_num, 0, 0); return 0; } else if (c == 99) { //相方不要設定だった ; } else { sd->skilllv = (c + skill_lv) / 2; } } break; } if (!skill_check_condition (sd, 0)) return 0; /* 射程と障害物チェック */ range_ = skill_get_range (skill_num, skill_lv); if (range_ < 0) range_ = battle_get_range (&sd->bl) - (range_ + 1); if (!battle_check_range (&sd->bl, bl, range_)) return 0; if (bl->type == BL_PC) { target_sd = (struct map_session_data *) bl; if (target_sd && skill_num == ALL_RESURRECTION && !pc_isdead (target_sd)) return 0; } if ((skill_num != MO_CHAINCOMBO && skill_num != MO_COMBOFINISH && skill_num != MO_EXTREMITYFIST && skill_num != CH_TIGERFIST && skill_num != CH_CHAINCRUSH) || (skill_num == MO_EXTREMITYFIST && sd->state.skill_flag)) pc_stopattack (sd); casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); if (skill_num != SA_MAGICROD) delay = skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); sd->state.skillcastcancel = skill_db[skill_num].castcancel; switch (skill_num) { /* 何か特殊な処理が必要 */ // case AL_HEAL: /* ヒール */ // if(battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) // forcecast=1; /* ヒールアタックなら詠唱エフェクト有り */ // break; case ALL_RESURRECTION: /* リザレクション */ if (bl->type != BL_PC && battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) { /* 敵がアンデッドなら */ forcecast = 1; /* ターンアンデットと同じ詠唱時間 */ casttime = skill_castfix (&sd->bl, skill_get_cast (PR_TURNUNDEAD, skill_lv)); } break; case MO_FINGEROFFENSIVE: /* 指弾 */ casttime += casttime * ((skill_lv > sd->spiritball) ? sd->spiritball : skill_lv); break; case MO_CHAINCOMBO: /*連打掌 */ target_id = sd->attacktarget; if (sc_data && sc_data[SC_BLADESTOP].timer != -1) { struct block_list *tbl; if ((tbl = (struct block_list *) sc_data[SC_BLADESTOP].val4) == NULL) //ターゲットがいない? return 0; target_id = tbl->id; } break; case MO_COMBOFINISH: /*猛龍拳 */ case CH_TIGERFIST: /* 伏虎拳 */ case CH_CHAINCRUSH: /* 連柱崩撃 */ target_id = sd->attacktarget; break; // -- moonsoul (altered to allow proper usage of extremity from new champion combos) // case MO_EXTREMITYFIST: /*阿修羅覇鳳拳 */ if (sc_data && sc_data[SC_COMBO].timer != -1 && (sc_data[SC_COMBO].val1 == MO_COMBOFINISH || sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) { casttime = 0; target_id = sd->attacktarget; } forcecast = 1; break; case SA_MAGICROD: case SA_SPELLBREAKER: forcecast = 1; break; case WE_MALE: case WE_FEMALE: { struct map_session_data *p_sd = NULL; if ((p_sd = pc_get_partner (sd)) == NULL) return 0; target_id = p_sd->bl.id; //rangeをもう1回検査 range_ = skill_get_range (skill_num, skill_lv); if (range_ < 0) range_ = battle_get_range (&sd->bl) - (range_ + 1); if (!battle_check_range (&sd->bl, &p_sd->bl, range_)) { return 0; } } break; case AS_SPLASHER: /* ベナムスプラッシャー */ { struct status_change *t_sc_data = battle_get_sc_data (bl); if (t_sc_data && t_sc_data[SC_POISON].timer == -1) { clif_skill_fail (sd, skill_num, 0, 10); return 0; } } break; case PF_MEMORIZE: /* メモライズ */ casttime = 12000; break; } //メモライズ状態ならキャストタイムが1/3 if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) { casttime = casttime / 3; if ((--sc_data[SC_MEMORIZE].val2) <= 0) skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); } if (battle_config.pc_skill_log) printf ("PC %d skill use target_id=%d skill=%d lv=%d cast=%d\n", sd->bl.id, target_id, skill_num, skill_lv, casttime); // if(sd->skillitem == skill_num) // casttime = delay = 0; if (casttime > 0 || forcecast) { /* 詠唱が必要 */ struct mob_data *md; clif_skillcasting (&sd->bl, sd->bl.id, target_id, 0, 0, skill_num, casttime); /* 詠唱反応モンスター */ if (bl->type == BL_MOB && (md = (struct mob_data *) bl) && mob_db[md->mob_class].mode & 0x10 && md->state.state != MS_ATTACK && sd->invincible_timer == -1) { md->target_id = sd->bl.id; md->state.targettype = ATTACKABLE; md->min_chase = 13; } } if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ sd->state.skillcastcancel = 0; sd->skilltarget = target_id; /* sd->cast_target_bl = bl; */ sd->skillx = 0; sd->skilly = 0; sd->canact_tick = tick + casttime + delay; sd->canmove_tick = tick; if (!(battle_config.pc_cloak_check_type & 2) && sc_data && sc_data[SC_CLOAKING].timer != -1 && sd->skillid != AS_CLOAKING) skill_status_change_end (&sd->bl, SC_CLOAKING, -1); if (casttime > 0) { sd->skilltimer = add_timer (tick + casttime, skill_castend_id, sd->bl.id, 0); if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) { sd->prev_speed = sd->speed; sd->speed = sd->speed * (175 - skill * 5) / 100; clif_updatestatus (sd, SP_SPEED); } else pc_stop_walking (sd, 0); } else { if (skill_num != SA_CASTCANCEL) sd->skilltimer = -1; skill_castend_id (sd->skilltimer, tick, sd->bl.id, 0); } //マジックパワーの効果終了 if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 && skill_num != HW_MAGICPOWER) skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); return 0; } /*========================================== * スキル使用(場所指定) *------------------------------------------ */ int skill_use_pos (struct map_session_data *sd, int skill_x, int skill_y, int skill_num, int skill_lv) { struct block_list bl; struct status_change *sc_data; unsigned int tick; int casttime = 0, delay = 0, skill, range; nullpo_retr (0, sd); if (pc_isdead (sd)) return 0; if (skillnotok (skill_num, sd)) // [MoueJstr] return 0; sc_data = sd->sc_data; if (sd->opt1 > 0) return 0; if (sc_data) { if (sc_data[SC_DIVINA].timer != -1 || sc_data[SC_ROKISWEIL].timer != -1 || sc_data[SC_AUTOCOUNTER].timer != -1 || sc_data[SC_STEELBODY].timer != -1 || sc_data[SC_DANCING].timer != -1 || sc_data[SC_BERSERK].timer != -1) return 0; /* 状態異常や沈黙など */ } if (sd->status.option & 2) return 0; if (map[sd->bl.m].flag.gvg && (skill_num == SM_ENDURE || skill_num == AL_TELEPORT || skill_num == AL_WARP || skill_num == WZ_ICEWALL || skill_num == TF_BACKSLIDING)) return 0; sd->skillid = skill_num; sd->skilllv = skill_lv; sd->skillx = skill_x; sd->skilly = skill_y; if (!skill_check_condition (sd, 0)) return 0; /* 射程と障害物チェック */ bl.type = BL_NUL; bl.m = sd->bl.m; bl.x = skill_x; bl.y = skill_y; range = skill_get_range (skill_num, skill_lv); if (range < 0) range = battle_get_range (&sd->bl) - (range + 1); if (!battle_check_range (&sd->bl, &bl, range)) return 0; pc_stopattack (sd); casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); delay = skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); sd->state.skillcastcancel = skill_db[skill_num].castcancel; if (battle_config.pc_skill_log) printf ("PC %d skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d\n", sd->bl.id, skill_x, skill_y, skill_num, skill_lv, casttime); // if(sd->skillitem == skill_num) // casttime = delay = 0; //メモライズ状態ならキャストタイムが1/3 if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) { casttime = casttime / 3; if ((--sc_data[SC_MEMORIZE].val2) <= 0) skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); } if (casttime > 0) /* 詠唱が必要 */ clif_skillcasting (&sd->bl, sd->bl.id, 0, skill_x, skill_y, skill_num, casttime); if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ sd->state.skillcastcancel = 0; sd->skilltarget = 0; /* sd->cast_target_bl = NULL; */ tick = gettick (); sd->canact_tick = tick + casttime + delay; sd->canmove_tick = tick; if (!(battle_config.pc_cloak_check_type & 2) && sc_data && sc_data[SC_CLOAKING].timer != -1) skill_status_change_end (&sd->bl, SC_CLOAKING, -1); if (casttime > 0) { sd->skilltimer = add_timer (tick + casttime, skill_castend_pos, sd->bl.id, 0); if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) { sd->prev_speed = sd->speed; sd->speed = sd->speed * (175 - skill * 5) / 100; clif_updatestatus (sd, SP_SPEED); } else pc_stop_walking (sd, 0); } else { sd->skilltimer = -1; skill_castend_pos (sd->skilltimer, tick, sd->bl.id, 0); } //マジックパワーの効果終了 if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 && skill_num != HW_MAGICPOWER) skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); return 0; } /*========================================== * スキル詠唱キャンセル *------------------------------------------ */ int skill_castcancel (struct block_list *bl, int type) { int inf; nullpo_retr (0, bl); if (bl->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *) bl; unsigned long tick = gettick (); nullpo_retr (0, sd); sd->canact_tick = tick; sd->canmove_tick = tick; if (sd->skilltimer != -1) { if (pc_checkskill (sd, SA_FREECAST) > 0) { sd->speed = sd->prev_speed; clif_updatestatus (sd, SP_SPEED); } if (!type) { if ((inf = skill_get_inf (sd->skillid)) == 2 || inf == 32) delete_timer (sd->skilltimer, skill_castend_pos); else delete_timer (sd->skilltimer, skill_castend_id); } else { if ((inf = skill_get_inf (sd->skillid_old)) == 2 || inf == 32) delete_timer (sd->skilltimer, skill_castend_pos); else delete_timer (sd->skilltimer, skill_castend_id); } sd->skilltimer = -1; clif_skillcastcancel (bl); } return 0; } else if (bl->type == BL_MOB) { struct mob_data *md = (struct mob_data *) bl; nullpo_retr (0, md); if (md->skilltimer != -1) { if ((inf = skill_get_inf (md->skillid)) == 2 || inf == 32) delete_timer (md->skilltimer, mobskill_castend_pos); else delete_timer (md->skilltimer, mobskill_castend_id); md->skilltimer = -1; clif_skillcastcancel (bl); } return 0; } return 1; } /*========================================= * ブランディッシュスピア 初期範囲決定 *---------------------------------------- */ void skill_brandishspear_first (struct square *tc, int dir, int x, int y) { nullpo_retv (tc); if (dir == 0) { tc->val1[0] = x - 2; tc->val1[1] = x - 1; tc->val1[2] = x; tc->val1[3] = x + 1; tc->val1[4] = x + 2; tc->val2[0] = tc->val2[1] = tc->val2[2] = tc->val2[3] = tc->val2[4] = y - 1; } else if (dir == 2) { tc->val1[0] = tc->val1[1] = tc->val1[2] = tc->val1[3] = tc->val1[4] = x + 1; tc->val2[0] = y + 2; tc->val2[1] = y + 1; tc->val2[2] = y; tc->val2[3] = y - 1; tc->val2[4] = y - 2; } else if (dir == 4) { tc->val1[0] = x - 2; tc->val1[1] = x - 1; tc->val1[2] = x; tc->val1[3] = x + 1; tc->val1[4] = x + 2; tc->val2[0] = tc->val2[1] = tc->val2[2] = tc->val2[3] = tc->val2[4] = y + 1; } else if (dir == 6) { tc->val1[0] = tc->val1[1] = tc->val1[2] = tc->val1[3] = tc->val1[4] = x - 1; tc->val2[0] = y + 2; tc->val2[1] = y + 1; tc->val2[2] = y; tc->val2[3] = y - 1; tc->val2[4] = y - 2; } else if (dir == 1) { tc->val1[0] = x - 1; tc->val1[1] = x; tc->val1[2] = x + 1; tc->val1[3] = x + 2; tc->val1[4] = x + 3; tc->val2[0] = y - 4; tc->val2[1] = y - 3; tc->val2[2] = y - 1; tc->val2[3] = y; tc->val2[4] = y + 1; } else if (dir == 3) { tc->val1[0] = x + 3; tc->val1[1] = x + 2; tc->val1[2] = x + 1; tc->val1[3] = x; tc->val1[4] = x - 1; tc->val2[0] = y - 1; tc->val2[1] = y; tc->val2[2] = y + 1; tc->val2[3] = y + 2; tc->val2[4] = y + 3; } else if (dir == 5) { tc->val1[0] = x + 1; tc->val1[1] = x; tc->val1[2] = x - 1; tc->val1[3] = x - 2; tc->val1[4] = x - 3; tc->val2[0] = y + 3; tc->val2[1] = y + 2; tc->val2[2] = y + 1; tc->val2[3] = y; tc->val2[4] = y - 1; } else if (dir == 7) { tc->val1[0] = x - 3; tc->val1[1] = x - 2; tc->val1[2] = x - 1; tc->val1[3] = x; tc->val1[4] = x + 1; tc->val2[1] = y; tc->val2[0] = y + 1; tc->val2[2] = y - 1; tc->val2[3] = y - 2; tc->val2[4] = y - 3; } } /*========================================= * ブランディッシュスピア 方向判定 範囲拡張 *----------------------------------------- */ void skill_brandishspear_dir (struct square *tc, int dir, int are) { int c; nullpo_retv (tc); for (c = 0; c < 5; c++) { if (dir == 0) { tc->val2[c] += are; } else if (dir == 1) { tc->val1[c] -= are; tc->val2[c] += are; } else if (dir == 2) { tc->val1[c] -= are; } else if (dir == 3) { tc->val1[c] -= are; tc->val2[c] -= are; } else if (dir == 4) { tc->val2[c] -= are; } else if (dir == 5) { tc->val1[c] += are; tc->val2[c] -= are; } else if (dir == 6) { tc->val1[c] += are; } else if (dir == 7) { tc->val1[c] += are; tc->val2[c] += are; } } } /*========================================== * ディボーション 有効確認 *------------------------------------------ */ void skill_devotion (struct map_session_data *md, int target) { // 総確認 int n; nullpo_retv (md); for (n = 0; n < 5; n++) { if (md->dev.val1[n]) { struct map_session_data *sd = map_id2sd (md->dev.val1[n]); // 相手が見つからない // 相手をディボしてるのが自分じゃない // 距離が離れてる if (sd == NULL || (sd->sc_data && (md->bl.id != sd->sc_data[SC_DEVOTION].val1)) || skill_devotion3 (&md->bl, md->dev.val1[n])) { skill_devotion_end (md, sd, n); } } } } void skill_devotion2 (struct block_list *bl, int crusader) { // 被ディボーションが歩いた時の距離チェック struct map_session_data *sd = map_id2sd (crusader); nullpo_retv (bl); if (sd) skill_devotion3 (&sd->bl, bl->id); } int skill_devotion3 (struct block_list *bl, int target) { // クルセが歩いた時の距離チェック struct map_session_data *md; struct map_session_data *sd; int n, r = 0; nullpo_retr (1, bl); if ((md = (struct map_session_data *) bl) == NULL || (sd = map_id2sd (target)) == NULL) return 1; else r = distance (bl->x, bl->y, sd->bl.x, sd->bl.y); if (pc_checkskill (sd, CR_DEVOTION) + 6 < r) { // 許容範囲を超えてた for (n = 0; n < 5; n++) if (md->dev.val1[n] == target) md->dev.val2[n] = 0; // 離れた時は、糸を切るだけ clif_devotion (md, sd->bl.id); return 1; } return 0; } void skill_devotion_end (struct map_session_data *md, struct map_session_data *sd, int target) { // クルセと被ディボキャラのリセット nullpo_retv (md); nullpo_retv (sd); md->dev.val1[target] = md->dev.val2[target] = 0; if (sd && sd->sc_data) { // skill_status_change_end(sd->bl,SC_DEVOTION,-1); sd->sc_data[SC_DEVOTION].val1 = 0; sd->sc_data[SC_DEVOTION].val2 = 0; clif_status_change (&sd->bl, SC_DEVOTION, 0); clif_devotion (md, sd->bl.id); } } /*========================================== * オートスペル *------------------------------------------ */ int skill_autospell (struct map_session_data *sd, int skillid) { int skilllv; int maxlv = 1, lv; nullpo_retr (0, sd); skilllv = pc_checkskill (sd, SA_AUTOSPELL); if (skillid == MG_NAPALMBEAT) maxlv = 3; else if (skillid == MG_COLDBOLT || skillid == MG_FIREBOLT || skillid == MG_LIGHTNINGBOLT) { if (skilllv == 2) maxlv = 1; else if (skilllv == 3) maxlv = 2; else if (skilllv >= 4) maxlv = 3; } else if (skillid == MG_SOULSTRIKE) { if (skilllv == 5) maxlv = 1; else if (skilllv == 6) maxlv = 2; else if (skilllv >= 7) maxlv = 3; } else if (skillid == MG_FIREBALL) { if (skilllv == 8) maxlv = 1; else if (skilllv >= 9) maxlv = 2; } else if (skillid == MG_FROSTDIVER) maxlv = 1; else return 0; if (maxlv > (lv = pc_checkskill (sd, skillid))) maxlv = lv; skill_status_change_start (&sd->bl, SC_AUTOSPELL, skilllv, skillid, maxlv, 0, // val1:スキルID val2:使用最大Lv skill_get_time (SA_AUTOSPELL, skilllv), 0); // にしてみたけどbscriptが書き易い・・・? return 0; } /*========================================== * ギャングスターパラダイス判定処理(foreachinarea) *------------------------------------------ */ static void skill_gangster_count (struct block_list *bl, va_list ap) { int *c; struct map_session_data *sd; nullpo_retv (bl); nullpo_retv (ap); sd = (struct map_session_data *) bl; c = va_arg (ap, int *); if (sd && c && pc_issit (sd) && pc_checkskill (sd, RG_GANGSTER) > 0) (*c)++; } static void skill_gangster_in (struct block_list *bl, va_list ap) { struct map_session_data *sd; nullpo_retv (bl); nullpo_retv (ap); sd = (struct map_session_data *) bl; if (sd && pc_issit (sd) && pc_checkskill (sd, RG_GANGSTER) > 0) sd->state.gangsterparadise = 1; } static void skill_gangster_out (struct block_list *bl, va_list ap) { struct map_session_data *sd; nullpo_retv (bl); nullpo_retv (ap); sd = (struct map_session_data *) bl; if (sd && sd->state.gangsterparadise) sd->state.gangsterparadise = 0; } int skill_gangsterparadise (struct map_session_data *sd, int type) { int range = 1; int c = 0; nullpo_retr (0, sd); if (pc_checkskill (sd, RG_GANGSTER) <= 0) return 0; if (type == 1) { /* 座った時の処理 */ map_foreachinarea (skill_gangster_count, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &c); if (c > 0) { /*ギャングスター成功したら自分にもギャングスター属性付与 */ map_foreachinarea (skill_gangster_in, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC); sd->state.gangsterparadise = 1; } return 0; } else if (type == 0) { /* 立ち上がったときの処理 */ map_foreachinarea (skill_gangster_count, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &c); if (c < 1) map_foreachinarea (skill_gangster_out, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC); sd->state.gangsterparadise = 0; return 0; } return 0; } /*========================================== * 寒いジョーク・スクリーム判定処理(foreachinarea) *------------------------------------------ */ void skill_frostjoke_scream (struct block_list *bl, va_list ap) { struct block_list *src; int skillnum, skilllv; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (src = va_arg (ap, struct block_list *)); skillnum = va_arg (ap, int); skilllv = va_arg (ap, int); tick = va_arg (ap, unsigned int); if (src == bl) //自分には効かない return; if (battle_check_target (src, bl, BCT_ENEMY) > 0) skill_additional_effect (src, bl, skillnum, skilllv, BF_MISC, tick); else if (battle_check_target (src, bl, BCT_PARTY) > 0) { if (MRAND (100) < 10) //PTメンバにも低確率でかかる(とりあえず10%) skill_additional_effect (src, bl, skillnum, skilllv, BF_MISC, tick); } } /*========================================== * *------------------------------------------ */ void skill_attack_area (struct block_list *bl, va_list ap) { struct block_list *src, *dsrc; int atk_type, skillid, skilllv, flag, type; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); atk_type = va_arg (ap, int); if ((src = va_arg (ap, struct block_list *)) == NULL) return; if ((dsrc = va_arg (ap, struct block_list *)) == NULL) return; skillid = va_arg (ap, int); skilllv = va_arg (ap, int); tick = va_arg (ap, unsigned int); flag = va_arg (ap, int); type = va_arg (ap, int); if (battle_check_target (dsrc, bl, type) > 0) skill_attack (atk_type, src, dsrc, bl, skillid, skilllv, tick, flag); } /*========================================== * *------------------------------------------ */ void skill_clear_element_field (struct block_list *bl) { struct mob_data *md = NULL; struct map_session_data *sd = NULL; int i, skillid; nullpo_retv (bl); if (bl->type == BL_MOB) md = (struct mob_data *) bl; if (bl->type == BL_PC) sd = (struct map_session_data *) bl; for (i = 0; i < MAX_MOBSKILLUNITGROUP; i++) { if (sd) { skillid = sd->skillunit[i].skill_id; if (skillid == SA_DELUGE || skillid == SA_VOLCANO || skillid == SA_VIOLENTGALE || skillid == SA_LANDPROTECTOR) skill_delunitgroup (&sd->skillunit[i]); } else if (md) { skillid = md->skillunit[i].skill_id; if (skillid == SA_DELUGE || skillid == SA_VOLCANO || skillid == SA_VIOLENTGALE || skillid == SA_LANDPROTECTOR) skill_delunitgroup (&md->skillunit[i]); } } } /*========================================== * ランドプロテクターチェック(foreachinarea) *------------------------------------------ */ void skill_landprotector (struct block_list *bl, va_list ap) { int skillid; int *alive; struct skill_unit *unit; nullpo_retv (bl); nullpo_retv (ap); skillid = va_arg (ap, int); alive = va_arg (ap, int *); if ((unit = (struct skill_unit *) bl) == NULL) return; if (skillid == SA_LANDPROTECTOR) { skill_delunit (unit); } else { if (alive && unit->group->skill_id == SA_LANDPROTECTOR) (*alive) = 0; } } /*========================================== * イドゥンの林檎の回復処理(foreachinarea) *------------------------------------------ */ static void skill_idun_heal (struct block_list *bl, va_list ap) { struct skill_unit *unit; struct skill_unit_group *sg; int heal; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (unit = va_arg (ap, struct skill_unit *)); nullpo_retv (sg = unit->group); heal = 30 + sg->skill_lv * 5 + ((sg->val1) >> 16) * 5 + ((sg->val1) & 0xfff) / 2; if (bl->type == BL_SKILL || bl->id == sg->src_id) return; if (bl->type == BL_PC || bl->type == BL_MOB) { clif_skill_nodamage (&unit->bl, bl, AL_HEAL, heal, 1); battle_heal (NULL, bl, heal, 0, 0); } } /*========================================== * 指定範囲内でsrcに対して有効なターゲットのblの数を数える(foreachinarea) *------------------------------------------ */ void skill_count_target (struct block_list *bl, va_list ap) { struct block_list *src; int *c; nullpo_retv (bl); nullpo_retv (ap); if ((src = va_arg (ap, struct block_list *)) == NULL) return; if ((c = va_arg (ap, int *)) == NULL) return; if (battle_check_target (src, bl, BCT_ENEMY) > 0) (*c)++; } /*========================================== * トラップ範囲処理(foreachinarea) *------------------------------------------ */ void skill_trap_splash (struct block_list *bl, va_list ap) { struct block_list *src; int tick; int splash_count; struct skill_unit *unit; struct skill_unit_group *sg; struct block_list *ss; int i; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (src = va_arg (ap, struct block_list *)); nullpo_retv (unit = (struct skill_unit *) src); nullpo_retv (sg = unit->group); nullpo_retv (ss = map_id2bl (sg->src_id)); tick = va_arg (ap, int); splash_count = va_arg (ap, int); if (battle_check_target (src, bl, BCT_ENEMY) > 0) { switch (sg->unit_id) { case 0x95: /* サンドマン */ case 0x96: /* フラッシャー */ case 0x94: /* ショックウェーブトラップ */ skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick); break; case 0x8f: /* ブラストマイン */ case 0x98: /* クレイモアートラップ */ for (i = 0; i < splash_count; i++) { skill_attack (BF_MISC, ss, src, bl, sg->skill_id, sg->skill_lv, tick, (sg->val2) ? 0x0500 : 0); } case 0x97: /* フリージングトラップ */ skill_attack (BF_WEAPON, ss, src, bl, sg->skill_id, sg->skill_lv, tick, (sg->val2) ? 0x0500 : 0); break; default: break; } } } /*---------------------------------------------------------------------------- * ステータス異常 *---------------------------------------------------------------------------- */ /*========================================== * ステータス異常タイマー範囲処理 *------------------------------------------ */ void skill_status_change_timer_sub (struct block_list *bl, va_list ap) { struct block_list *src; int type; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (src = va_arg (ap, struct block_list *)); type = va_arg (ap, int); tick = va_arg (ap, unsigned int); if (bl->type != BL_PC && bl->type != BL_MOB) return; switch (type) { case SC_SIGHT: /* サイト */ case SC_CONCENTRATE: if ((*battle_get_option (bl)) & 6) { skill_status_change_end (bl, SC_HIDING, -1); skill_status_change_end (bl, SC_CLOAKING, -1); } break; case SC_RUWACH: /* ルアフ */ if ((*battle_get_option (bl)) & 6) { skill_status_change_end (bl, SC_HIDING, -1); skill_status_change_end (bl, SC_CLOAKING, -1); if (battle_check_target (src, bl, BCT_ENEMY) > 0) { struct status_change *sc_data = battle_get_sc_data (bl); skill_attack (BF_MAGIC, src, src, bl, AL_RUWACH, sc_data[type].val1, tick, 0); } } break; } } /*========================================== * ステータス異常終了 *------------------------------------------ */ int skill_status_change_active (struct block_list *bl, int type) { struct status_change *sc_data; nullpo_retr (0, bl); if (bl->type != BL_PC && bl->type != BL_MOB) { if (battle_config.error_log) printf ("skill_status_change_active: neither MOB nor PC !\n"); return 0; } nullpo_retr (0, sc_data = battle_get_sc_data (bl)); return sc_data[type].timer != -1; } int skill_status_change_end (struct block_list *bl, int type, int tid) { struct status_change *sc_data; int opt_flag = 0, calc_flag = 0; short *sc_count, *option, *opt1, *opt2, *opt3; nullpo_retr (0, bl); if (bl->type != BL_PC && bl->type != BL_MOB) { if (battle_config.error_log) printf ("skill_status_change_end: neither MOB nor PC !\n"); return 0; } nullpo_retr (0, sc_data = battle_get_sc_data (bl)); nullpo_retr (0, sc_count = battle_get_sc_count (bl)); nullpo_retr (0, option = battle_get_option (bl)); nullpo_retr (0, opt1 = battle_get_opt1 (bl)); nullpo_retr (0, opt2 = battle_get_opt2 (bl)); nullpo_retr (0, opt3 = battle_get_opt3 (bl)); if ((*sc_count) > 0 && sc_data[type].timer != -1 && (sc_data[type].timer == tid || tid == -1)) { if (tid == -1) // タイマから呼ばれていないならタイマ削除をする delete_timer (sc_data[type].timer, skill_status_change_timer); /* 該当の異常を正常に戻す */ sc_data[type].timer = -1; (*sc_count)--; switch (type) { /* 異常の種類ごとの処理 */ case SC_PROVOKE: /* プロボック */ case SC_CONCENTRATE: /* 集中力向上 */ case SC_BLESSING: /* ブレッシング */ case SC_ANGELUS: /* アンゼルス */ case SC_INCREASEAGI: /* 速度上昇 */ case SC_DECREASEAGI: /* 速度減少 */ case SC_SIGNUMCRUCIS: /* シグナムクルシス */ case SC_HIDING: case SC_TWOHANDQUICKEN: /* 2HQ */ case SC_ADRENALINE: /* アドレナリンラッシュ */ case SC_ENCPOISON: /* エンチャントポイズン */ case SC_IMPOSITIO: /* インポシティオマヌス */ case SC_GLORIA: /* グロリア */ case SC_LOUD: /* ラウドボイス */ case SC_QUAGMIRE: /* クァグマイア */ case SC_PROVIDENCE: /* プロヴィデンス */ case SC_SPEARSQUICKEN: /* スピアクイッケン */ case SC_VOLCANO: case SC_DELUGE: case SC_VIOLENTGALE: case SC_ETERNALCHAOS: /* エターナルカオス */ case SC_DRUMBATTLE: /* 戦太鼓の響き */ case SC_NIBELUNGEN: /* ニーベルングの指輪 */ case SC_SIEGFRIED: /* 不死身のジークフリード */ case SC_WHISTLE: /* 口笛 */ case SC_ASSNCROS: /* 夕陽のアサシンクロス */ case SC_HUMMING: /* ハミング */ case SC_DONTFORGETME: /* 私を忘れないで */ case SC_FORTUNE: /* 幸運のキス */ case SC_SERVICE4U: /* サービスフォーユー */ case SC_EXPLOSIONSPIRITS: // 爆裂波動 case SC_STEELBODY: // 金剛 case SC_DEFENDER: case SC_SPEEDPOTION0: /* 増速ポーション */ case SC_SPEEDPOTION1: case SC_SPEEDPOTION2: case SC_APPLEIDUN: /* イドゥンの林檎 */ case SC_RIDING: case SC_BLADESTOP_WAIT: case SC_AURABLADE: /* オーラブレード */ case SC_PARRYING: /* パリイング */ case SC_CONCENTRATION: /* コンセントレーション */ case SC_TENSIONRELAX: /* テンションリラックス */ case SC_ASSUMPTIO: /* アシャンプティオ */ case SC_WINDWALK: /* ウインドウォーク */ case SC_TRUESIGHT: /* トゥルーサイト */ case SC_SPIDERWEB: /* スパイダーウェッブ */ case SC_MAGICPOWER: /* 魔法力増幅 */ case SC_CHASEWALK: case SC_ATKPOT: /* attack potion [Valaris] */ case SC_MATKPOT: /* magic attack potion [Valaris] */ case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) case SC_MELTDOWN: /* メルトダウン */ case SC_PHYS_SHIELD: case SC_HASTE: calc_flag = 1; break; case SC_BERSERK: /* バーサーク */ calc_flag = 1; clif_status_change (bl, SC_INCREASEAGI, 0); /* アイコン消去 */ break; case SC_DEVOTION: /* ディボーション */ { struct map_session_data *md = map_id2sd (sc_data[type].val1); sc_data[type].val1 = sc_data[type].val2 = 0; skill_devotion (md, bl->id); calc_flag = 1; } break; case SC_BLADESTOP: { struct status_change *t_sc_data = battle_get_sc_data ((struct block_list *) sc_data[type].val4); //片方が切れたので相手の白刃状態が切れてないのなら解除 if (t_sc_data && t_sc_data[SC_BLADESTOP].timer != -1) skill_status_change_end ((struct block_list *) sc_data[type].val4, SC_BLADESTOP, -1); if (sc_data[type].val2 == 2) clif_bladestop ((struct block_list *) sc_data[type].val3, (struct block_list *) sc_data[type].val4, 0); } break; case SC_DANCING: { struct map_session_data *dsd; struct status_change *d_sc_data; if (sc_data[type].val4 && (dsd = map_id2sd (sc_data[type].val4))) { d_sc_data = dsd->sc_data; //合奏で相手がいる場合相手のval4を0にする if (d_sc_data && d_sc_data[type].timer != -1) d_sc_data[type].val4 = 0; } } calc_flag = 1; break; case SC_GRAFFITI: { struct skill_unit_group *sg = (struct skill_unit_group *) sc_data[type].val4; //val4がグラフィティのgroup_id if (sg) skill_delunitgroup (sg); } break; case SC_NOCHAT: //チャット禁止状態 { struct map_session_data *sd = NULL; if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) { sd->status.manner = 0; clif_updatestatus (sd, SP_MANNER); } } break; case SC_SPLASHER: /* ベナムスプラッシャー */ { struct block_list *src = map_id2bl (sc_data[type].val3); if (src && tid != -1) { //自分にダメージ&周囲3*3にダメージ skill_castend_damage_id (src, bl, sc_data[type].val2, sc_data[type].val1, gettick (), 0); } } break; case SC_SELFDESTRUCTION: /* 自爆 */ { //自分のダメージは0にして struct mob_data *md = NULL; if (bl->type == BL_MOB && (md = (struct mob_data *) bl)) skill_castend_damage_id (bl, bl, sc_data[type].val2, sc_data[type].val1, gettick (), 0); } break; /* option1 */ case SC_FREEZE: sc_data[type].val3 = 0; break; /* option2 */ case SC_POISON: /* 毒 */ case SC_BLIND: /* 暗黒 */ case SC_CURSE: calc_flag = 1; break; } if (bl->type == BL_PC && type < SC_SENDMAX) clif_status_change (bl, type, 0); /* アイコン消去 */ switch (type) { /* 正常に戻るときなにか処理が必要 */ case SC_STONE: case SC_FREEZE: case SC_STAN: case SC_SLEEP: *opt1 = 0; opt_flag = 1; break; case SC_POISON: case SC_CURSE: case SC_SILENCE: case SC_BLIND: *opt2 &= ~(1 << (type - SC_POISON)); opt_flag = 1; break; case SC_SLOWPOISON: if (sc_data[SC_POISON].timer != -1) *opt2 |= 0x1; *opt2 &= ~0x200; opt_flag = 1; break; case SC_SIGNUMCRUCIS: *opt2 &= ~0x40; opt_flag = 1; break; case SC_SPEEDPOTION0: *opt2 &= ~0x20; opt_flag = 1; break; case SC_ATKPOT: *opt2 &= ~0x80; opt_flag = 1; break; case SC_HIDING: case SC_CLOAKING: *option &= ~((type == SC_HIDING) ? 2 : 4); opt_flag = 1; break; case SC_CHASEWALK: *option &= ~16388; opt_flag = 1; break; case SC_SIGHT: *option &= ~1; opt_flag = 1; break; case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) *option &= ~4096; opt_flag = 1; break; case SC_RUWACH: *option &= ~8192; opt_flag = 1; break; //opt3 case SC_TWOHANDQUICKEN: /* 2HQ */ case SC_SPEARSQUICKEN: /* スピアクイッケン */ case SC_CONCENTRATION: /* コンセントレーション */ *opt3 &= ~1; break; case SC_OVERTHRUST: /* オーバースラスト */ *opt3 &= ~2; break; case SC_ENERGYCOAT: /* エナジーコート */ *opt3 &= ~4; break; case SC_EXPLOSIONSPIRITS: // 爆裂波動 *opt3 &= ~8; break; case SC_STEELBODY: // 金剛 *opt3 &= ~16; break; case SC_BLADESTOP: /* 白刃取り */ *opt3 &= ~32; break; case SC_BERSERK: /* バーサーク */ *opt3 &= ~128; break; case SC_MARIONETTE: /* マリオネットコントロール */ *opt3 &= ~1024; break; case SC_ASSUMPTIO: /* アスムプティオ */ *opt3 &= ~2048; break; } if (night_flag == 1 && (*opt2 & STATE_BLIND) == 0 && bl->type == BL_PC) { // by [Yor] *opt2 |= STATE_BLIND; opt_flag = 1; } if (opt_flag) /* optionの変更を伝える */ clif_changeoption (bl); if (bl->type == BL_PC && calc_flag) pc_calcstatus ((struct map_session_data *) bl, 0); /* ステータス再計算 */ } return 0; } int skill_update_heal_animation (struct map_session_data *sd) { const int mask = 0x100; int was_active; int is_active; nullpo_retr (0, sd); was_active = sd->opt2 & mask; is_active = sd->quick_regeneration_hp.amount > 0; if ((was_active && is_active) || (!was_active && !is_active)) return 0; // no update if (is_active) sd->opt2 |= mask; else sd->opt2 &= ~mask; return clif_changeoption (&sd->bl); } /*========================================== * ステータス異常終了タイマー *------------------------------------------ */ void skill_status_change_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { int type = data; struct block_list *bl; struct map_session_data *sd = NULL; struct status_change *sc_data; //short *sc_count; //使ってない? if ((bl = map_id2bl (id)) == NULL) return; //該当IDがすでに消滅しているというのはいかにもありそうなのでスルーしてみる nullpo_retv (sc_data = battle_get_sc_data (bl)); if (bl->type == BL_PC) sd = (struct map_session_data *) bl; //sc_count=battle_get_sc_count(bl); //使ってない? if (sc_data[type].timer != tid) { if (battle_config.error_log) printf ("skill_status_change_timer %d != %d\n", tid, sc_data[type].timer); } if (sc_data[type].spell_invocation) { // Must report termination spell_effect_report_termination (sc_data[type].spell_invocation, bl->id, type, 0); sc_data[type].spell_invocation = 0; } switch (type) { /* 特殊な処理になる場合 */ case SC_MAXIMIZEPOWER: /* マキシマイズパワー */ case SC_CLOAKING: /* クローキング */ case SC_CHASEWALK: if (sd) { if (sd->status.sp > 0) { /* SP切れるまで持続 */ sd->status.sp--; clif_updatestatus (sd, SP_SP); sc_data[type].timer = add_timer ( /* タイマー再設定 */ sc_data[type].val2 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_HIDING: /* ハイディング */ if (sd) { /* SPがあって、時間制限の間は持続 */ if (sd->status.sp > 0 && (--sc_data[type].val2) > 0) { if (sc_data[type].val2 % (sc_data[type].val1 + 3) == 0) { sd->status.sp--; clif_updatestatus (sd, SP_SP); } sc_data[type].timer = add_timer ( /* タイマー再設定 */ 1000 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_SIGHT: /* サイト */ { const int range = 7; map_foreachinarea (skill_status_change_timer_sub, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, 0, bl, type, tick); if ((--sc_data[type].val2) > 0) { sc_data[type].timer = add_timer ( /* タイマー再設定 */ 250 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_RUWACH: /* ルアフ */ { const int range = 5; map_foreachinarea (skill_status_change_timer_sub, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, 0, bl, type, tick); if ((--sc_data[type].val2) > 0) { sc_data[type].timer = add_timer ( /* タイマー再設定 */ 250 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_SIGNUMCRUCIS: /* シグナムクルシス */ { int race = battle_get_race (bl); if (race == 6 || battle_check_undead (race, battle_get_elem_type (bl))) { sc_data[type].timer = add_timer (1000 * 600 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_PROVOKE: /* プロボック/オートバーサーク */ if (sc_data[type].val2 != 0) { /* オートバーサーク(1秒ごとにHPチェック) */ if (sd && sd->status.hp > sd->status.max_hp >> 2) /* 停止 */ break; sc_data[type].timer = add_timer (1000 + tick, skill_status_change_timer, bl->id, data); return; } break; case SC_WATERBALL: /* ウォーターボール */ { struct block_list *target = map_id2bl (sc_data[type].val2); if (target == NULL || target->prev == NULL) break; skill_attack (BF_MAGIC, bl, bl, target, WZ_WATERBALL, sc_data[type].val1, tick, 0); if ((--sc_data[type].val3) > 0) { sc_data[type].timer = add_timer (150 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_ENDURE: /* インデュア */ if (sd && sd->special_state.infinite_endure) { sc_data[type].timer = add_timer (1000 * 600 + tick, skill_status_change_timer, bl->id, data); sc_data[type].val2 = 1; return; } break; case SC_DISSONANCE: /* 不協和音 */ if ((--sc_data[type].val2) > 0) { struct skill_unit *unit = (struct skill_unit *) sc_data[type].val4; struct block_list *src; if (!unit || !unit->group) break; src = map_id2bl (unit->group->src_id); if (!src) break; skill_attack (BF_MISC, src, &unit->bl, bl, unit->group->skill_id, sc_data[type].val1, tick, 0); sc_data[type].timer = add_timer (skill_get_time2 (unit->group->skill_id, unit->group->skill_lv) + tick, skill_status_change_timer, bl->id, data); return; } break; case SC_LULLABY: /* 子守唄 */ if ((--sc_data[type].val2) > 0) { struct skill_unit *unit = (struct skill_unit *) sc_data[type].val4; if (!unit || !unit->group || unit->group->src_id == bl->id) break; skill_additional_effect (bl, bl, unit->group->skill_id, sc_data[type].val1, BF_LONG | BF_SKILL | BF_MISC, tick); sc_data[type].timer = add_timer (skill_get_time (unit->group->skill_id, unit->group->skill_lv) / 10 + tick, skill_status_change_timer, bl->id, data); return; } break; case SC_STONE: if (sc_data[type].val2 != 0) { short *opt1 = battle_get_opt1 (bl); sc_data[type].val2 = 0; sc_data[type].val4 = 0; battle_stopwalking (bl, 1); if (opt1) { *opt1 = 1; clif_changeoption (bl); } sc_data[type].timer = add_timer (1000 + tick, skill_status_change_timer, bl->id, data); return; } else if ((--sc_data[type].val3) > 0) { int hp = battle_get_max_hp (bl); if ((++sc_data[type].val4) % 5 == 0 && battle_get_hp (bl) > hp >> 2) { hp = hp / 100; if (hp < 1) hp = 1; if (bl->type == BL_PC) pc_heal ((struct map_session_data *) bl, -hp, 0); else if (bl->type == BL_MOB) { struct mob_data *md; if ((md = ((struct mob_data *) bl)) == NULL) break; md->hp -= hp; } } sc_data[type].timer = add_timer (1000 + tick, skill_status_change_timer, bl->id, data); return; } break; case SC_POISON: if (sc_data[SC_SLOWPOISON].timer == -1) { const int resist_poison = skill_power_bl (bl, TMW_RESIST_POISON) >> 3; if (resist_poison) sc_data[type].val1 -= MRAND (resist_poison + 1); if ((--sc_data[type].val1) > 0) { int hp = battle_get_max_hp (bl); if (battle_get_hp (bl) > hp >> 4) { if (bl->type == BL_PC) { hp = 3 + hp * 3 / 200; pc_heal ((struct map_session_data *) bl, -hp, 0); } else if (bl->type == BL_MOB) { struct mob_data *md; if ((md = ((struct mob_data *) bl)) == NULL) break; hp = 3 + hp / 200; md->hp -= hp; } } sc_data[type].timer = add_timer (1000 + tick, skill_status_change_timer, bl->id, data); } } else sc_data[type].timer = add_timer (2000 + tick, skill_status_change_timer, bl->id, data); break; case SC_TENSIONRELAX: /* テンションリラックス */ if (sd) { /* SPがあって、HPが満タンでなければ継続 */ if (sd->status.sp > 12 && sd->status.max_hp > sd->status.hp) { if (sc_data[type].val2 % (sc_data[type].val1 + 3) == 0) { sd->status.sp -= 12; clif_updatestatus (sd, SP_SP); } sc_data[type].timer = add_timer ( /* タイマー再設定 */ 10000 + tick, skill_status_change_timer, bl->id, data); return; } if (sd->status.max_hp <= sd->status.hp) skill_status_change_end (&sd->bl, SC_TENSIONRELAX, -1); } break; /* 時間切れ無し?? */ case SC_AETERNA: case SC_TRICKDEAD: case SC_RIDING: case SC_FALCON: case SC_WEIGHT50: case SC_WEIGHT90: case SC_MAGICPOWER: /* 魔法力増幅 */ case SC_REJECTSWORD: /* リジェクトソード */ case SC_MEMORIZE: /* メモライズ */ case SC_BROKNWEAPON: case SC_BROKNARMOR: if (sc_data[type].timer == tid) sc_data[type].timer = add_timer (1000 * 600 + tick, skill_status_change_timer, bl->id, data); return; case SC_DANCING: //ダンススキルの時間SP消費 { int s = 0; if (sd) { if (sd->status.sp > 0 && (--sc_data[type].val3) > 0) { switch (sc_data[type].val1) { case BD_RICHMANKIM: /* ニヨルドの宴 3秒にSP1 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き 3秒にSP1 */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 3秒にSP1 */ case BD_SIEGFRIED: /* 不死身のジークフリード 3秒にSP1 */ case BA_DISSONANCE: /* 不協和音 3秒でSP1 */ case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス 3秒でSP1 */ case DC_UGLYDANCE: /* 自分勝手なダンス 3秒でSP1 */ s = 3; break; case BD_LULLABY: /* 子守歌 4秒にSP1 */ case BD_ETERNALCHAOS: /* 永遠の混沌 4秒にSP1 */ case BD_ROKISWEIL: /* ロキの叫び 4秒にSP1 */ case DC_FORTUNEKISS: /* 幸運のキス 4秒でSP1 */ s = 4; break; case BD_INTOABYSS: /* 深淵の中に 5秒にSP1 */ case BA_WHISTLE: /* 口笛 5秒でSP1 */ case DC_HUMMING: /* ハミング 5秒でSP1 */ case BA_POEMBRAGI: /* ブラギの詩 5秒でSP1 */ case DC_SERVICEFORYOU: /* サービスフォーユー 5秒でSP1 */ s = 5; break; case BA_APPLEIDUN: /* イドゥンの林檎 6秒でSP1 */ s = 6; break; case DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */ s = 10; break; } if (s && ((sc_data[type].val3 % s) == 0)) { sd->status.sp--; clif_updatestatus (sd, SP_SP); } sc_data[type].timer = add_timer ( /* タイマー再設定 */ 1000 + tick, skill_status_change_timer, bl->id, data); return; } } } break; case SC_BERSERK: /* バーサーク */ if (sd) { /* HPが100以上なら継続 */ if ((sd->status.hp - sd->status.hp / 100) > 100) { sd->status.hp -= sd->status.hp / 100; clif_updatestatus (sd, SP_HP); sc_data[type].timer = add_timer ( /* タイマー再設定 */ 15000 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) if (sd) { time_t timer; if (time (&timer) < ((sc_data[type].val2) + 3600)) { //1時間たっていないので継続 sc_data[type].timer = add_timer ( /* タイマー再設定 */ 10000 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_NOCHAT: //チャット禁止状態 if (sd && battle_config.muting_players) { time_t timer; if ((++sd->status.manner) && time (&timer) < ((sc_data[type].val2) + 60 * (0 - sd->status.manner))) { //開始からstatus.manner分経ってないので継続 clif_updatestatus (sd, SP_MANNER); sc_data[type].timer = add_timer ( /* タイマー再設定(60秒) */ 60000 + tick, skill_status_change_timer, bl->id, data); return; } } break; case SC_SELFDESTRUCTION: /* 自爆 */ if (--sc_data[type].val3 > 0) { struct mob_data *md; if (bl->type == BL_MOB && (md = (struct mob_data *) bl) && md->stats[MOB_SPEED] > 250) { md->stats[MOB_SPEED] -= 250; md->next_walktime = tick; } sc_data[type].timer = add_timer ( /* タイマー再設定 */ 1000 + tick, skill_status_change_timer, bl->id, data); return; } break; case SC_FLYING_BACKPACK: clif_updatestatus (sd, SP_WEIGHT); break; } skill_status_change_end (bl, type, tid); } /*========================================== * ステータス異常終了 *------------------------------------------ */ int skill_encchant_eremental_end (struct block_list *bl, int type) { struct status_change *sc_data; nullpo_retr (0, bl); nullpo_retr (0, sc_data = battle_get_sc_data (bl)); if (type != SC_ENCPOISON && sc_data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解除 */ skill_status_change_end (bl, SC_ENCPOISON, -1); if (type != SC_ASPERSIO && sc_data[SC_ASPERSIO].timer != -1) /* アスペルシオ解除 */ skill_status_change_end (bl, SC_ASPERSIO, -1); if (type != SC_FLAMELAUNCHER && sc_data[SC_FLAMELAUNCHER].timer != -1) /* フレイムランチャ解除 */ skill_status_change_end (bl, SC_FLAMELAUNCHER, -1); if (type != SC_FROSTWEAPON && sc_data[SC_FROSTWEAPON].timer != -1) /* フロストウェポン解除 */ skill_status_change_end (bl, SC_FROSTWEAPON, -1); if (type != SC_LIGHTNINGLOADER && sc_data[SC_LIGHTNINGLOADER].timer != -1) /* ライトニングローダー解除 */ skill_status_change_end (bl, SC_LIGHTNINGLOADER, -1); if (type != SC_SEISMICWEAPON && sc_data[SC_SEISMICWEAPON].timer != -1) /* サイスミックウェポン解除 */ skill_status_change_end (bl, SC_SEISMICWEAPON, -1); return 0; } /*========================================== * ステータス異常開始 *------------------------------------------ */ int skill_status_change_start (struct block_list *bl, int type, int val1, int val2, int val3, int val4, int tick, int flag) { return skill_status_effect (bl, type, val1, val2, val3, val4, tick, flag, 0); } int skill_status_effect (struct block_list *bl, int type, int val1, int val2, int val3, int val4, int tick, int flag, int spell_invocation) { struct map_session_data *sd = NULL; struct status_change *sc_data; short *sc_count, *option, *opt1, *opt2, *opt3; int opt_flag = 0, calc_flag = 0, updateflag = 0, race, mode, elem, undead_flag; int scdef = 0; nullpo_retr (0, bl); if (bl->type == BL_SKILL) return 0; nullpo_retr (0, sc_data = battle_get_sc_data (bl)); nullpo_retr (0, sc_count = battle_get_sc_count (bl)); nullpo_retr (0, option = battle_get_option (bl)); nullpo_retr (0, opt1 = battle_get_opt1 (bl)); nullpo_retr (0, opt2 = battle_get_opt2 (bl)); nullpo_retr (0, opt3 = battle_get_opt3 (bl)); race = battle_get_race (bl); mode = battle_get_mode (bl); elem = battle_get_elem_type (bl); undead_flag = battle_check_undead (race, elem); if (type == SC_AETERNA && (sc_data[SC_STONE].timer != -1 || sc_data[SC_FREEZE].timer != -1)) return 0; switch (type) { case SC_STONE: case SC_FREEZE: scdef = 3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3; break; case SC_STAN: case SC_SILENCE: case SC_POISON: scdef = 3 + battle_get_vit (bl) + battle_get_luk (bl) / 3; break; case SC_SLEEP: case SC_BLIND: scdef = 3 + battle_get_int (bl) + battle_get_luk (bl) / 3; break; case SC_CURSE: scdef = 3 + battle_get_luk (bl); break; // case SC_CONFUSION: default: scdef = 0; } if (scdef >= 100) return 0; if (bl->type == BL_PC) { sd = (struct map_session_data *) bl; if (sd && type == SC_ADRENALINE && !(skill_get_weapontype (BS_ADRENALINE) & (1 << sd->status.weapon))) return 0; if (SC_STONE <= type && type <= SC_BLIND) { /* カードによる耐性 */ if (sd && sd->reseff[type - SC_STONE] > 0 && MRAND (10000) < sd->reseff[type - SC_STONE]) { if (battle_config.battle_log) printf ("PC %d skill_sc_start: cardによる異常耐性発動\n", sd->bl.id); return 0; } } } else if (bl->type == BL_MOB) { } else { if (battle_config.error_log) printf ("skill_status_change_start: neither MOB nor PC !\n"); return 0; } if (type == SC_FREEZE && undead_flag && !(flag & 1)) return 0; if ((type == SC_ADRENALINE || type == SC_WEAPONPERFECTION || type == SC_OVERTHRUST) && sc_data[type].timer != -1 && sc_data[type].val2 && !val2) return 0; if (mode & 0x20 && (type == SC_STONE || type == SC_FREEZE || type == SC_STAN || type == SC_SLEEP || type == SC_SILENCE || type == SC_QUAGMIRE || type == SC_DECREASEAGI || type == SC_SIGNUMCRUCIS || type == SC_PROVOKE || (type == SC_BLESSING && (undead_flag || race == 6))) && !(flag & 1)) { /* ボスには効かない(ただしカードによる効果は適用される) */ return 0; } if (type == SC_FREEZE || type == SC_STAN || type == SC_SLEEP) battle_stopwalking (bl, 1); if (sc_data[type].timer != -1) { /* すでに同じ異常になっている場合タイマ解除 */ if (sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION && type != SC_SPEEDPOTION0 && type != SC_SPEEDPOTION1 && type != SC_SPEEDPOTION2 && type != SC_ATKPOT && type != SC_MATKPOT) // added atk and matk potions [Valaris] return 0; if (type >= SC_STAN && type <= SC_BLIND) return 0; /* 継ぎ足しができない状態異常である時は状態異常を行わない */ if (type == SC_GRAFFITI) { //異常中にもう一度状態異常になった時に解除してから再度かかる skill_status_change_end (bl, type, -1); } else { (*sc_count)--; delete_timer (sc_data[type].timer, skill_status_change_timer); sc_data[type].timer = -1; } } switch (type) { /* 異常の種類ごとの処理 */ case SC_PROVOKE: /* プロボック */ calc_flag = 1; if (tick <= 0) tick = 1000; /* (オートバーサーク) */ break; case SC_ENDURE: /* インデュア */ if (tick <= 0) tick = 1000 * 60; break; case SC_CONCENTRATE: /* 集中力向上 */ calc_flag = 1; break; case SC_BLESSING: /* ブレッシング */ { if (bl->type == BL_PC || (!undead_flag && race != 6)) { if (sc_data[SC_CURSE].timer != -1) skill_status_change_end (bl, SC_CURSE, -1); if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) skill_status_change_end (bl, SC_STONE, -1); } calc_flag = 1; } break; case SC_ANGELUS: /* アンゼルス */ calc_flag = 1; break; case SC_INCREASEAGI: /* 速度上昇 */ calc_flag = 1; if (sc_data[SC_DECREASEAGI].timer != -1) skill_status_change_end (bl, SC_DECREASEAGI, -1); if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ skill_status_change_end (bl, SC_WINDWALK, -1); break; case SC_DECREASEAGI: /* 速度減少 */ calc_flag = 1; if (sc_data[SC_INCREASEAGI].timer != -1) skill_status_change_end (bl, SC_INCREASEAGI, -1); break; case SC_SIGNUMCRUCIS: /* シグナムクルシス */ calc_flag = 1; // val2 = 14 + val1; val2 = 10 + val1 * 2; tick = 600 * 1000; clif_emotion (bl, 4); break; case SC_SLOWPOISON: if (sc_data[SC_POISON].timer == -1) return 0; break; case SC_TWOHANDQUICKEN: /* 2HQ */ *opt3 |= 1; calc_flag = 1; break; case SC_ADRENALINE: /* アドレナリンラッシュ */ calc_flag = 1; break; case SC_WEAPONPERFECTION: /* ウェポンパーフェクション */ if (battle_config.party_skill_penaly && !val2) tick /= 5; break; case SC_OVERTHRUST: /* オーバースラスト */ *opt3 |= 2; if (battle_config.party_skill_penaly && !val2) tick /= 10; break; case SC_MAXIMIZEPOWER: /* マキシマイズパワー(SPが1減る時間,val2にも) */ if (bl->type == BL_PC) val2 = tick; else tick = 5000 * val1; break; case SC_ENCPOISON: /* エンチャントポイズン */ calc_flag = 1; val2 = (((val1 - 1) / 2) + 3) * 100; /* 毒付与確率 */ skill_encchant_eremental_end (bl, SC_ENCPOISON); break; case SC_POISONREACT: /* ポイズンリアクト */ break; case SC_IMPOSITIO: /* インポシティオマヌス */ calc_flag = 1; break; case SC_ASPERSIO: /* アスペルシオ */ skill_encchant_eremental_end (bl, SC_ASPERSIO); break; case SC_SUFFRAGIUM: /* サフラギム */ case SC_BENEDICTIO: /* 聖体 */ case SC_MAGNIFICAT: /* マグニフィカート */ case SC_AETERNA: /* エーテルナ */ break; case SC_ENERGYCOAT: /* エナジーコート */ *opt3 |= 4; break; case SC_MAGICROD: val2 = val1 * 20; break; case SC_KYRIE: /* キリエエレイソン */ val2 = battle_get_max_hp (bl) * (val1 * 2 + 10) / 100; /* 耐久度 */ val3 = (val1 / 2 + 5); /* 回数 */ // -- moonsoul (added to undo assumptio status if target has it) if (sc_data[SC_ASSUMPTIO].timer != -1) skill_status_change_end (bl, SC_ASSUMPTIO, -1); break; case SC_MINDBREAKER: calc_flag = 1; if (tick <= 0) tick = 1000; /* (オートバーサーク) */ case SC_GLORIA: /* グロリア */ calc_flag = 1; break; case SC_LOUD: /* ラウドボイス */ calc_flag = 1; break; case SC_TRICKDEAD: /* 死んだふり */ break; case SC_QUAGMIRE: /* クァグマイア */ calc_flag = 1; if (sc_data[SC_CONCENTRATE].timer != -1) /* 集中力向上解除 */ skill_status_change_end (bl, SC_CONCENTRATE, -1); if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ skill_status_change_end (bl, SC_INCREASEAGI, -1); if (sc_data[SC_TWOHANDQUICKEN].timer != -1) skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); if (sc_data[SC_SPEARSQUICKEN].timer != -1) skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); if (sc_data[SC_ADRENALINE].timer != -1) skill_status_change_end (bl, SC_ADRENALINE, -1); if (sc_data[SC_LOUD].timer != -1) skill_status_change_end (bl, SC_LOUD, -1); if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ skill_status_change_end (bl, SC_TRUESIGHT, -1); if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ skill_status_change_end (bl, SC_WINDWALK, -1); if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ skill_status_change_end (bl, SC_CARTBOOST, -1); break; case SC_FLAMELAUNCHER: /* フレームランチャー */ skill_encchant_eremental_end (bl, SC_FLAMELAUNCHER); break; case SC_FROSTWEAPON: /* フロストウェポン */ skill_encchant_eremental_end (bl, SC_FROSTWEAPON); break; case SC_LIGHTNINGLOADER: /* ライトニングローダー */ skill_encchant_eremental_end (bl, SC_LIGHTNINGLOADER); break; case SC_SEISMICWEAPON: /* サイズミックウェポン */ skill_encchant_eremental_end (bl, SC_SEISMICWEAPON); break; case SC_DEVOTION: /* ディボーション */ calc_flag = 1; break; case SC_PROVIDENCE: /* プロヴィデンス */ calc_flag = 1; val2 = val1 * 5; break; case SC_REFLECTSHIELD: val2 = 10 + val1 * 3; break; case SC_STRIPWEAPON: case SC_STRIPSHIELD: case SC_STRIPARMOR: case SC_STRIPHELM: case SC_CP_WEAPON: case SC_CP_SHIELD: case SC_CP_ARMOR: case SC_CP_HELM: break; case SC_AUTOSPELL: /* オートスペル */ val4 = 5 + val1 * 2; break; case SC_VOLCANO: calc_flag = 1; val3 = val1 * 10; val4 = val1 >= 5 ? 20 : (val1 == 4 ? 19 : (val1 == 3 ? 17 : (val1 == 2 ? 14 : 10))); break; case SC_DELUGE: calc_flag = 1; val3 = val1 >= 5 ? 15 : (val1 == 4 ? 14 : (val1 == 3 ? 12 : (val1 == 2 ? 9 : 5))); val4 = val1 >= 5 ? 20 : (val1 == 4 ? 19 : (val1 == 3 ? 17 : (val1 == 2 ? 14 : 10))); break; case SC_VIOLENTGALE: calc_flag = 1; val3 = val1 * 3; val4 = val1 >= 5 ? 20 : (val1 == 4 ? 19 : (val1 == 3 ? 17 : (val1 == 2 ? 14 : 10))); break; case SC_SPEARSQUICKEN: /* スピアクイッケン */ calc_flag = 1; val2 = 20 + val1; *opt3 |= 1; break; case SC_COMBO: break; case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */ break; case SC_BLADESTOP: /* 白刃取り */ if (val2 == 2) clif_bladestop ((struct block_list *) val3, (struct block_list *) val4, 1); *opt3 |= 32; break; case SC_LULLABY: /* 子守唄 */ val2 = 11; break; case SC_RICHMANKIM: break; case SC_ETERNALCHAOS: /* エターナルカオス */ calc_flag = 1; break; case SC_DRUMBATTLE: /* 戦太鼓の響き */ calc_flag = 1; val2 = (val1 + 1) * 25; val3 = (val1 + 1) * 2; break; case SC_NIBELUNGEN: /* ニーベルングの指輪 */ calc_flag = 1; val2 = (val1 + 2) * 50; val3 = (val1 + 2) * 25; break; case SC_ROKISWEIL: /* ロキの叫び */ break; case SC_INTOABYSS: /* 深淵の中に */ break; case SC_SIEGFRIED: /* 不死身のジークフリード */ calc_flag = 1; val2 = 40 + val1 * 5; val3 = val1 * 10; break; case SC_DISSONANCE: /* 不協和音 */ val2 = 10; break; case SC_WHISTLE: /* 口笛 */ calc_flag = 1; break; case SC_ASSNCROS: /* 夕陽のアサシンクロス */ calc_flag = 1; break; case SC_POEMBRAGI: /* ブラギの詩 */ break; case SC_APPLEIDUN: /* イドゥンの林檎 */ calc_flag = 1; break; case SC_UGLYDANCE: /* 自分勝手なダンス */ val2 = 10; break; case SC_HUMMING: /* ハミング */ calc_flag = 1; break; case SC_DONTFORGETME: /* 私を忘れないで */ calc_flag = 1; if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ skill_status_change_end (bl, SC_INCREASEAGI, -1); if (sc_data[SC_TWOHANDQUICKEN].timer != -1) skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); if (sc_data[SC_SPEARSQUICKEN].timer != -1) skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); if (sc_data[SC_ADRENALINE].timer != -1) skill_status_change_end (bl, SC_ADRENALINE, -1); if (sc_data[SC_ASSNCROS].timer != -1) skill_status_change_end (bl, SC_ASSNCROS, -1); if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ skill_status_change_end (bl, SC_TRUESIGHT, -1); if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ skill_status_change_end (bl, SC_WINDWALK, -1); if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ skill_status_change_end (bl, SC_CARTBOOST, -1); break; case SC_FORTUNE: /* 幸運のキス */ calc_flag = 1; break; case SC_SERVICE4U: /* サービスフォーユー */ calc_flag = 1; break; case SC_DANCING: /* ダンス/演奏中 */ calc_flag = 1; val3 = tick / 1000; tick = 1000; break; case SC_EXPLOSIONSPIRITS: // 爆裂波動 calc_flag = 1; val2 = 75 + 25 * val1; *opt3 |= 8; break; case SC_STEELBODY: // 金剛 calc_flag = 1; *opt3 |= 16; break; case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */ break; case SC_AUTOCOUNTER: val3 = val4 = 0; break; case SC_SPEEDPOTION0: /* 増速ポーション */ *opt2 |= 0x20; case SC_SPEEDPOTION1: case SC_SPEEDPOTION2: calc_flag = 1; tick = 1000 * tick; // val2 = 5*(2+type-SC_SPEEDPOTION0); break; /* atk & matk potions [Valaris] */ case SC_ATKPOT: *opt2 |= 0x80; case SC_MATKPOT: calc_flag = 1; tick = 1000 * tick; break; case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) { time_t timer; calc_flag = 1; tick = 10000; if (!val2) val2 = time (&timer); } break; case SC_NOCHAT: //チャット禁止状態 { time_t timer; if (!battle_config.muting_players) break; tick = 60000; if (!val2) val2 = time (&timer); updateflag = SP_MANNER; } break; case SC_SELFDESTRUCTION: //自爆 clif_skillcasting (bl, bl->id, bl->id, 0, 0, 331, skill_get_time (val2, val1)); val3 = tick / 1000; tick = 1000; break; /* option1 */ case SC_STONE: /* 石化 */ if (!(flag & 2)) { int sc_def = battle_get_mdef (bl) * 200; tick = tick - sc_def; } val3 = tick / 1000; if (val3 < 1) val3 = 1; tick = 5000; val2 = 1; break; case SC_SLEEP: /* 睡眠 */ if (!(flag & 2)) { // int sc_def = 100 - (battle_get_int(bl) + battle_get_luk(bl)/3); // tick = tick * sc_def / 100; // if(tick < 1000) tick = 1000; tick = 30000; //睡眠はステータス耐性に関わらず30秒 } break; case SC_FREEZE: /* 凍結 */ if (!(flag & 2)) { int sc_def = 100 - battle_get_mdef (bl); tick = tick * sc_def / 100; } break; case SC_STAN: /* スタン(val2にミリ秒セット) */ if (!(flag & 2)) { int sc_def = 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 3); tick = tick * sc_def / 100; } break; /* option2 */ case SC_POISON: /* 毒 */ calc_flag = 1; if (!(flag & 2)) { int sc_def = 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 5); tick = tick * sc_def / 100; } val3 = tick / 1000; if (val3 < 1) val3 = 1; tick = 1000; break; case SC_SILENCE: /* 沈黙(レックスデビーナ) */ if (!(flag & 2)) { int sc_def = 100 - battle_get_vit (bl); tick = tick * sc_def / 100; } break; case SC_BLIND: /* 暗黒 */ calc_flag = 1; if (!(flag & 2)) { int sc_def = battle_get_lv (bl) / 10 + battle_get_int (bl) / 15; tick = 30000 - sc_def; } break; case SC_CURSE: calc_flag = 1; if (!(flag & 2)) { int sc_def = 100 - battle_get_vit (bl); tick = tick * sc_def / 100; } break; /* option */ case SC_HIDING: /* ハイディング */ calc_flag = 1; if (bl->type == BL_PC) { val2 = tick / 1000; /* 持続時間 */ tick = 1000; } break; case SC_CHASEWALK: case SC_CLOAKING: /* クローキング */ if (bl->type == BL_PC) val2 = tick; else tick = 5000 * val1; break; case SC_SIGHT: /* サイト/ルアフ */ case SC_RUWACH: val2 = tick / 250; tick = 10; break; /* セーフティウォール、ニューマ */ case SC_SAFETYWALL: case SC_PNEUMA: tick = ((struct skill_unit *) val2)->group->limit; break; /* アンクル */ case SC_ANKLE: break; /* ウォーターボール */ case SC_WATERBALL: tick = 150; if (val1 > 5) //レベルが5以上の場合は25発に制限(1発目はすでに打ってるので-1) val3 = 5 * 5 - 1; else val3 = (val1 | 1) * (val1 | 1) - 1; break; /* スキルじゃない/時間に関係しない */ case SC_RIDING: calc_flag = 1; tick = 600 * 1000; break; case SC_FALCON: case SC_WEIGHT50: case SC_WEIGHT90: case SC_BROKNWEAPON: case SC_BROKNARMOR: tick = 600 * 1000; break; case SC_AUTOGUARD: { int i, t; for (i = val2 = 0; i < val1; i++) { t = 5 - (i >> 1); val2 += (t < 0) ? 1 : t; } } break; case SC_DEFENDER: calc_flag = 1; val2 = 5 + val1 * 15; break; case SC_KEEPING: case SC_BARRIER: case SC_HALLUCINATION: break; case SC_CONCENTRATION: /* コンセントレーション */ *opt3 |= 1; calc_flag = 1; break; case SC_TENSIONRELAX: /* テンションリラックス */ calc_flag = 1; if (bl->type == BL_PC) { tick = 10000; } break; case SC_AURABLADE: /* オーラブレード */ case SC_PARRYING: /* パリイング */ // case SC_ASSUMPTIO: /* */ case SC_HEADCRUSH: /* ヘッドクラッシュ */ case SC_JOINTBEAT: /* ジョイントビート */ // case SC_MARIONETTE: /* マリオネットコントロール */ //とりあえず手抜き break; // -- moonsoul (for new upper class related skill status effects) /* case SC_AURABLADE: val2 = val1*10; break; case SC_PARRYING: val2=val1*3; break; case SC_CONCENTRATION: calc_flag=1; val2=val1*10; val3=val1*5; break; case SC_TENSIONRELAX: // val2 = 10; // val3 = 15; break; case SC_BERSERK: calc_flag=1; break; case SC_ASSUMPTIO: if(sc_data[SC_KYRIE].timer!=-1 ) skill_status_change_end(bl,SC_KYRIE,-1); break; */ case SC_WINDWALK: /* ウインドウォーク */ calc_flag = 1; val2 = (val1 / 2); //Flee上昇率 break; case SC_BERSERK: /* バーサーク */ if (sd) { sd->status.sp = 0; clif_updatestatus (sd, SP_SP); clif_status_change (bl, SC_INCREASEAGI, 1); /* アイコン表示 */ } *opt3 |= 128; tick = 1000; calc_flag = 1; break; case SC_ASSUMPTIO: /* アスムプティオ */ *opt3 |= 2048; break; case SC_MARIONETTE: /* マリオネットコントロール */ *opt3 |= 1024; break; case SC_MELTDOWN: /* メルトダウン */ case SC_CARTBOOST: /* カートブースト */ case SC_TRUESIGHT: /* トゥルーサイト */ case SC_SPIDERWEB: /* スパイダーウェッブ */ case SC_MAGICPOWER: /* 魔法力増幅 */ calc_flag = 1; break; case SC_REJECTSWORD: /* リジェクトソード */ val2 = 3; //3回攻撃を跳ね返す break; case SC_MEMORIZE: /* メモライズ */ val2 = 3; //3回詠唱を1/3にする break; case SC_GRAFFITI: /* グラフィティ */ { struct skill_unit_group *sg = skill_unitsetting (bl, RG_GRAFFITI, val1, val2, val3, 0); if (sg) val4 = (int) sg; } break; case SC_HASTE: calc_flag = 1; case SC_SPLASHER: /* ベナムスプラッシャー */ case SC_PHYS_SHIELD: case SC_MBARRIER: case SC_HALT_REGENERATE: case SC_HIDE: break; case SC_FLYING_BACKPACK: updateflag = SP_WEIGHT; break; default: if (battle_config.error_log) printf ("UnknownStatusChange [%d]\n", type); return 0; } if (bl->type == BL_PC && type < SC_SENDMAX) clif_status_change (bl, type, 1); /* アイコン表示 */ /* optionの変更 */ switch (type) { case SC_STONE: case SC_FREEZE: case SC_STAN: case SC_SLEEP: battle_stopattack (bl); /* 攻撃停止 */ skill_stop_dancing (bl, 0); /* 演奏/ダンスの中断 */ { /* 同時に掛からないステータス異常を解除 */ int i; for (i = SC_STONE; i <= SC_SLEEP; i++) { if (sc_data[i].timer != -1) { (*sc_count)--; delete_timer (sc_data[i].timer, skill_status_change_timer); sc_data[i].timer = -1; } } } if (type == SC_STONE) *opt1 = 6; else *opt1 = type - SC_STONE + 1; opt_flag = 1; break; case SC_POISON: if (sc_data[SC_SLOWPOISON].timer == -1) { *opt2 |= 0x1; opt_flag = 1; } break; case SC_CURSE: case SC_SILENCE: case SC_BLIND: *opt2 |= 1 << (type - SC_POISON); opt_flag = 1; break; case SC_SLOWPOISON: *opt2 &= ~0x1; *opt2 |= 0x200; opt_flag = 1; break; case SC_SIGNUMCRUCIS: *opt2 |= 0x40; opt_flag = 1; break; case SC_HIDING: case SC_CLOAKING: battle_stopattack (bl); /* 攻撃停止 */ *option |= ((type == SC_HIDING) ? 2 : 4); opt_flag = 1; break; case SC_CHASEWALK: battle_stopattack (bl); /* 攻撃停止 */ *option |= 16388; opt_flag = 1; break; case SC_SIGHT: *option |= 1; opt_flag = 1; break; case SC_RUWACH: *option |= 8192; opt_flag = 1; break; case SC_WEDDING: *option |= 4096; opt_flag = 1; } if (opt_flag) /* optionの変更 */ clif_changeoption (bl); (*sc_count)++; /* ステータス異常の数 */ sc_data[type].val1 = val1; sc_data[type].val2 = val2; sc_data[type].val3 = val3; sc_data[type].val4 = val4; if (sc_data[type].spell_invocation) // Supplant by newer spell spell_effect_report_termination (sc_data[type].spell_invocation, bl->id, type, 1); sc_data[type].spell_invocation = spell_invocation; /* タイマー設定 */ sc_data[type].timer = add_timer (gettick () + tick, skill_status_change_timer, bl->id, type); if (bl->type == BL_PC && calc_flag) pc_calcstatus (sd, 0); /* ステータス再計算 */ if (bl->type == BL_PC && updateflag) clif_updatestatus (sd, updateflag); /* ステータスをクライアントに送る */ return 0; } /*========================================== * ステータス異常全解除 *------------------------------------------ */ int skill_status_change_clear (struct block_list *bl, int type) { struct status_change *sc_data; short *sc_count, *option, *opt1, *opt2, *opt3; int i; nullpo_retr (0, bl); nullpo_retr (0, sc_data = battle_get_sc_data (bl)); nullpo_retr (0, sc_count = battle_get_sc_count (bl)); nullpo_retr (0, option = battle_get_option (bl)); nullpo_retr (0, opt1 = battle_get_opt1 (bl)); nullpo_retr (0, opt2 = battle_get_opt2 (bl)); nullpo_retr (0, opt3 = battle_get_opt3 (bl)); if (*sc_count == 0) return 0; for (i = 0; i < MAX_STATUSCHANGE; i++) { if (sc_data[i].timer != -1) { /* 異常があるならタイマーを削除する */ /* delete_timer(sc_data[i].timer, skill_status_change_timer); sc_data[i].timer = -1; if (!type && i < SC_SENDMAX) clif_status_change(bl, i, 0); */ skill_status_change_end (bl, i, -1); } } *sc_count = 0; *opt1 = 0; *opt2 = 0; *opt3 = 0; *option &= OPTION_MASK; if (night_flag == 1 && type == BL_PC) // by [Yor] *opt2 |= STATE_BLIND; if (!type || type & 2) clif_changeoption (bl); return 0; } /* クローキング検査(周りに移動不可能地帯があるか) */ int skill_check_cloaking (struct block_list *bl) { struct map_session_data *sd = NULL; static int dx[] = { -1, 0, 1, -1, 1, -1, 0, 1 }; static int dy[] = { -1, -1, -1, 0, 0, 1, 1, 1 }; int end = 1, i; nullpo_retr (0, bl); if (pc_checkskill (sd, AS_CLOAKING) > 2) return 0; if (bl->type == BL_PC && battle_config.pc_cloak_check_type & 1) return 0; if (bl->type == BL_MOB && battle_config.monster_cloak_check_type & 1) return 0; for (i = 0; i < sizeof (dx) / sizeof (dx[0]); i++) { int c = map_getcell (bl->m, bl->x + dx[i], bl->y + dy[i]); if (c == 1 || c == 5) end = 0; } if (end) { skill_status_change_end (bl, SC_CLOAKING, -1); *battle_get_option (bl) &= ~4; /* 念のための処理 */ } return end; } /* *---------------------------------------------------------------------------- * スキルユニット *---------------------------------------------------------------------------- */ /*========================================== * 演奏/ダンススキルかどうか判定 * 引数 スキルID * 戻り ダンスじゃない=0 合奏=2 それ以外のダンス=1 *------------------------------------------ */ int skill_is_danceskill (int id) { int i; switch (id) { case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BD_RAGNAROK: /* 神々の黄昏 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ i = 2; break; case BA_DISSONANCE: /* 不協和音 */ case BA_FROSTJOKE: /* 寒いジョーク */ case BA_WHISTLE: /* 口笛 */ case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ case BA_POEMBRAGI: /* ブラギの詩 */ case BA_APPLEIDUN: /* イドゥンの林檎 */ case DC_UGLYDANCE: /* 自分勝手なダンス */ case DC_SCREAM: /* スクリーム */ case DC_HUMMING: /* ハミング */ case DC_DONTFORGETME: /* 私を忘れないで… */ case DC_FORTUNEKISS: /* 幸運のキス */ case DC_SERVICEFORYOU: /* サービスフォーユー */ i = 1; break; default: i = 0; } return i; } /*========================================== * 演奏/ダンスをやめる * flag 1で合奏中なら相方にユニットを任せる * *------------------------------------------ */ void skill_stop_dancing (struct block_list *src, int flag) { struct status_change *sc_data; struct skill_unit_group *group; nullpo_retv (src); sc_data = battle_get_sc_data (src); if (sc_data && sc_data[SC_DANCING].timer == -1) return; group = (struct skill_unit_group *) sc_data[SC_DANCING].val2; //ダンスのスキルユニットIDはval2に入ってる if (group && src->type == BL_PC && sc_data && sc_data[SC_DANCING].val4) { //合奏中断 struct map_session_data *dsd = map_id2sd (sc_data[SC_DANCING].val4); //相方のsd取得 if (flag) { //ログアウトなど片方が落ちても演奏が継続される if (dsd && src->id == group->src_id) { //グループを持ってるPCが落ちる group->src_id = sc_data[SC_DANCING].val4; //相方にグループを任せる if (flag & 1) //ログアウト dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 if (flag & 2) //ハエ飛びなど return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり } else if (dsd && dsd->bl.id == group->src_id) { //相方がグループを持っているPCが落ちる(自分はグループを持っていない) if (flag & 1) //ログアウト dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 if (flag & 2) //ハエ飛びなど return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり } skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる //そしてグループは消さない&消さないのでステータス計算もいらない? return; } else { if (dsd && src->id == group->src_id) { //グループを持ってるPCが止める skill_status_change_end ((struct block_list *) dsd, SC_DANCING, -1); //相手のステータスを終了させる } if (dsd && dsd->bl.id == group->src_id) { //相方がグループを持っているPCが止める(自分はグループを持っていない) skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる } } } if (flag & 2 && group && src->type == BL_PC) { //ハエで飛んだときとかはユニットも飛ぶ struct map_session_data *sd = (struct map_session_data *) src; skill_unit_move_unit_group (group, sd->bl.m, (sd->to_x - sd->bl.x), (sd->to_y - sd->bl.y)); return; } skill_delunitgroup (group); if (src->type == BL_PC) pc_calcstatus ((struct map_session_data *) src, 0); } /*========================================== * スキルユニット初期化 *------------------------------------------ */ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y) { struct skill_unit *unit; nullpo_retr (NULL, group); nullpo_retr (NULL, unit = &group->unit[idx]); if (!unit->alive) group->alive_count++; unit->bl.id = map_addobject (&unit->bl); unit->bl.type = BL_SKILL; unit->bl.m = group->map; unit->bl.x = x; unit->bl.y = y; unit->group = group; unit->val1 = unit->val2 = 0; unit->alive = 1; map_addblock (&unit->bl); clif_skill_setunit (unit); return unit; } void skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap); /*========================================== * スキルユニット削除 *------------------------------------------ */ int skill_delunit (struct skill_unit *unit) { struct skill_unit_group *group; int range; nullpo_retr (0, unit); if (!unit->alive) return 0; nullpo_retr (0, group = unit->group); /* onlimitイベント呼び出し */ skill_unit_onlimit (unit, gettick ()); /* ondeleteイベント呼び出し */ range = group->range; map_foreachinarea (skill_unit_timer_sub_ondelete, unit->bl.m, unit->bl.x - range, unit->bl.y - range, unit->bl.x + range, unit->bl.y + range, 0, &unit->bl, gettick ()); clif_skill_delunit (unit); unit->group = NULL; unit->alive = 0; map_delobjectnofree (unit->bl.id, BL_SKILL); if (group->alive_count > 0 && (--group->alive_count) <= 0) skill_delunitgroup (group); return 0; } /*========================================== * スキルユニットグループ初期化 *------------------------------------------ */ static int skill_unit_group_newid = 10; struct skill_unit_group *skill_initunitgroup (struct block_list *src, int count, int skillid, int skilllv, int unit_id) { int i; struct skill_unit_group *group = NULL, *list = NULL; int maxsug = 0; nullpo_retr (NULL, src); if (src->type == BL_PC) { list = ((struct map_session_data *) src)->skillunit; maxsug = MAX_SKILLUNITGROUP; } else if (src->type == BL_MOB) { list = ((struct mob_data *) src)->skillunit; maxsug = MAX_MOBSKILLUNITGROUP; } if (list) { for (i = 0; i < maxsug; i++) /* 空いているもの検索 */ if (list[i].group_id == 0) { group = &list[i]; break; } if (group == NULL) { /* 空いてないので古いもの検索 */ int j = 0; unsigned maxdiff = 0, x, tick = gettick (); for (i = 0; i < maxsug; i++) if ((x = DIFF_TICK (tick, list[i].tick)) > maxdiff) { maxdiff = x; j = i; } skill_delunitgroup (&list[j]); group = &list[j]; } } if (group == NULL) { printf ("skill_initunitgroup: error unit group !\n"); exit (1); } group->src_id = src->id; group->party_id = battle_get_party_id (src); group->guild_id = battle_get_guild_id (src); group->group_id = skill_unit_group_newid++; if (skill_unit_group_newid <= 0) skill_unit_group_newid = 10; CREATE (group->unit, struct skill_unit, count); group->unit_count = count; group->val1 = group->val2 = 0; group->skill_id = skillid; group->skill_lv = skilllv; group->unit_id = unit_id; group->map = src->m; group->range = 0; group->limit = 10000; group->interval = 1000; group->tick = gettick (); group->valstr = NULL; if (skill_is_danceskill (skillid)) { struct map_session_data *sd = NULL; if (src->type == BL_PC && (sd = (struct map_session_data *) src)) { sd->skillid_dance = skillid; sd->skilllv_dance = skilllv; } skill_status_change_start (src, SC_DANCING, skillid, (int) group, 0, 0, skill_get_time (skillid, skilllv) + 1000, 0); switch (skillid) { //合奏スキルは相方をダンス状態にする case BD_LULLABY: /* 子守歌 */ case BD_RICHMANKIM: /* ニヨルドの宴 */ case BD_ETERNALCHAOS: /* 永遠の混沌 */ case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ case BD_ROKISWEIL: /* ロキの叫び */ case BD_INTOABYSS: /* 深淵の中に */ case BD_SIEGFRIED: /* 不死身のジークフリード */ case BD_RAGNAROK: /* 神々の黄昏 */ case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ { int range = 1; int c = 0; if (sd) { map_foreachinarea (skill_check_condition_use_sub, sd->bl.m, sd->bl.x - range, sd->bl.y - range, sd->bl.x + range, sd->bl.y + range, BL_PC, &sd->bl, &c); } } } } return group; } /*========================================== * スキルユニットグループ削除 *------------------------------------------ */ int skill_delunitgroup (struct skill_unit_group *group) { struct block_list *src; int i; nullpo_retr (0, group); if (group->unit_count <= 0) return 0; src = map_id2bl (group->src_id); if (skill_is_danceskill (group->skill_id)) { //ダンススキルはダンス状態を解除する if (src) skill_status_change_end (src, SC_DANCING, -1); } group->alive_count = 0; if (group->unit != NULL) { for (i = 0; i < group->unit_count; i++) if (group->unit[i].alive) skill_delunit (&group->unit[i]); } if (group->valstr != NULL) { map_freeblock (group->valstr); group->valstr = NULL; } map_freeblock (group->unit); /* free()の替わり */ group->unit = NULL; group->src_id = 0; group->group_id = 0; group->unit_count = 0; return 0; } /*========================================== * スキルユニットグループ全削除 *------------------------------------------ */ int skill_clear_unitgroup (struct block_list *src) { struct skill_unit_group *group = NULL; int maxsug = 0; nullpo_retr (0, src); if (src->type == BL_PC) { group = ((struct map_session_data *) src)->skillunit; maxsug = MAX_SKILLUNITGROUP; } else if (src->type == BL_MOB) { group = ((struct mob_data *) src)->skillunit; maxsug = MAX_MOBSKILLUNITGROUP; } if (group) { int i; for (i = 0; i < maxsug; i++) if (group[i].group_id > 0 && group[i].src_id == src->id) skill_delunitgroup (&group[i]); } return 0; } /*========================================== * スキルユニットグループの被影響tick検索 *------------------------------------------ */ struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct block_list *bl, int group_id) { int i, j = 0, k, s = group_id % MAX_SKILLUNITGROUPTICKSET; struct skill_unit_group_tickset *set = NULL; nullpo_retr (0, bl); if (bl->type == BL_PC) { set = ((struct map_session_data *) bl)->skillunittick; } else { set = ((struct mob_data *) bl)->skillunittick; } if (set == NULL) return 0; for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) if (set[(k = (i + s) % MAX_SKILLUNITGROUPTICKSET)].group_id == group_id) return &set[k]; else if (set[k].group_id == 0) j = k; return &set[j]; } /*========================================== * スキルユニットグループの被影響tick削除 *------------------------------------------ */ int skill_unitgrouptickset_delete (struct block_list *bl, int group_id) { int i, s = group_id % MAX_SKILLUNITGROUPTICKSET; struct skill_unit_group_tickset *set = NULL, *ts; nullpo_retr (0, bl); if (bl->type == BL_PC) { set = ((struct map_session_data *) bl)->skillunittick; } else { set = ((struct mob_data *) bl)->skillunittick; } if (set != NULL) { for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) if ((ts = &set[(i + s) % MAX_SKILLUNITGROUPTICKSET])->group_id == group_id) ts->group_id = 0; } return 0; } /*========================================== * スキルユニットタイマー発動処理用(foreachinarea) *------------------------------------------ */ static void skill_unit_timer_sub_onplace (struct block_list *bl, va_list ap) { struct block_list *src; struct skill_unit *su; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); src = va_arg (ap, struct block_list *); tick = va_arg (ap, unsigned int); su = (struct skill_unit *) src; if (su && su->alive) { struct skill_unit_group *sg; sg = su->group; if (sg && battle_check_target (src, bl, sg->target_flag) > 0) skill_unit_onplace (su, bl, tick); } } /*========================================== * スキルユニットタイマー削除処理用(foreachinarea) *------------------------------------------ */ void skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap) { struct block_list *src; struct skill_unit *su; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); src = va_arg (ap, struct block_list *); tick = va_arg (ap, unsigned int); su = (struct skill_unit *) src; if (su && su->alive) { struct skill_unit_group *sg; sg = su->group; if (sg && battle_check_target (src, bl, sg->target_flag) > 0) skill_unit_ondelete (su, bl, tick); } } /*========================================== * スキルユニットタイマー処理用(foreachobject) *------------------------------------------ */ static void skill_unit_timer_sub (struct block_list *bl, va_list ap) { struct skill_unit *unit; struct skill_unit_group *group; int range; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (unit = (struct skill_unit *) bl); nullpo_retv (group = unit->group); tick = va_arg (ap, unsigned int); if (!unit->alive) return; range = (unit->range != 0) ? unit->range : group->range; /* onplaceイベント呼び出し */ if (unit->alive && unit->range >= 0) { map_foreachinarea (skill_unit_timer_sub_onplace, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, 0, bl, tick); if (group->unit_id == 0xaa && DIFF_TICK (tick, group->tick) >= 6000 * group->val2) { map_foreachinarea (skill_idun_heal, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, 0, unit); group->val2++; } } /* 時間切れ削除 */ if (unit->alive && (DIFF_TICK (tick, group->tick) >= group->limit || DIFF_TICK (tick, group->tick) >= unit->limit)) { switch (group->unit_id) { case 0x8f: /* ブラストマイン */ group->unit_id = 0x8c; clif_changelook (bl, LOOK_BASE, group->unit_id); group->limit = DIFF_TICK (tick + 1500, group->tick); unit->limit = DIFF_TICK (tick + 1500, group->tick); break; case 0x90: /* スキッドトラップ */ case 0x91: /* アンクルスネア */ case 0x93: /* ランドマイン */ case 0x94: /* ショックウェーブトラップ */ case 0x95: /* サンドマン */ case 0x96: /* フラッシャー */ case 0x97: /* フリージングトラップ */ case 0x98: /* クレイモアートラップ */ case 0x99: /* トーキーボックス */ { struct block_list *src = map_id2bl (group->src_id); if (group->unit_id == 0x91 && group->val2); else { if (src && src->type == BL_PC) { struct item item_tmp; memset (&item_tmp, 0, sizeof (item_tmp)); item_tmp.nameid = 1065; item_tmp.identify = 1; map_addflooritem (&item_tmp, 1, bl->m, bl->x, bl->y, NULL, NULL, NULL, 0); // 罠返還 } } } default: skill_delunit (unit); } } if (group->unit_id == 0x8d) { unit->val1 -= 5; if (unit->val1 <= 0 && unit->limit + group->tick > tick + 700) unit->limit = DIFF_TICK (tick + 700, group->tick); } } /*========================================== * スキルユニットタイマー処理 *------------------------------------------ */ static void skill_unit_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) { map_freeblock_lock (); map_foreachobject (skill_unit_timer_sub, BL_SKILL, tick); map_freeblock_unlock (); } /*========================================== * スキルユニット移動時処理用(foreachinarea) *------------------------------------------ */ static void skill_unit_out_all_sub (struct block_list *bl, va_list ap) { struct skill_unit *unit; struct skill_unit_group *group; struct block_list *src; int range; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (src = va_arg (ap, struct block_list *)); nullpo_retv (unit = (struct skill_unit *) bl); nullpo_retv (group = unit->group); tick = va_arg (ap, unsigned int); if (!unit->alive || src->prev == NULL) return; range = (unit->range != 0) ? unit->range : group->range; if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) return; if (src->x >= bl->x - range && src->x <= bl->x + range && src->y >= bl->y - range && src->y <= bl->y + range) skill_unit_onout (unit, src, tick); } /*========================================== * スキルユニット移動時処理 *------------------------------------------ */ int skill_unit_out_all (struct block_list *bl, unsigned int tick, int range) { nullpo_retr (0, bl); if (bl->prev == NULL) return 0; if (range < 7) range = 7; map_foreachinarea (skill_unit_out_all_sub, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, BL_SKILL, bl, tick); return 0; } /*========================================== * スキルユニット移動時処理用(foreachinarea) *------------------------------------------ */ static void skill_unit_move_sub (struct block_list *bl, va_list ap) { struct skill_unit *unit; struct skill_unit_group *group; struct block_list *src; int range; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (unit = (struct skill_unit *) bl); nullpo_retv (src = va_arg (ap, struct block_list *)); tick = va_arg (ap, unsigned int); if (!unit->alive || src->prev == NULL) return; if ((group = unit->group) == NULL) return; range = (unit->range != 0) ? unit->range : group->range; if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) return; if (src->x >= bl->x - range && src->x <= bl->x + range && src->y >= bl->y - range && src->y <= bl->y + range) skill_unit_onplace (unit, src, tick); else skill_unit_onout (unit, src, tick); } /*========================================== * スキルユニット移動時処理 *------------------------------------------ */ int skill_unit_move (struct block_list *bl, unsigned int tick, int range) { nullpo_retr (0, bl); if (bl->prev == NULL) return 0; if (range < 7) range = 7; map_foreachinarea (skill_unit_move_sub, bl->m, bl->x - range, bl->y - range, bl->x + range, bl->y + range, BL_SKILL, bl, tick); return 0; } /*========================================== * スキルユニット自体の移動時処理(foreachinarea) *------------------------------------------ */ static void skill_unit_move_unit_group_sub (struct block_list *bl, va_list ap) { struct skill_unit *unit; struct skill_unit_group *group; struct block_list *src; int range; unsigned int tick; nullpo_retv (bl); nullpo_retv (ap); nullpo_retv (src = va_arg (ap, struct block_list *)); nullpo_retv (unit = (struct skill_unit *) src); nullpo_retv (group = unit->group); tick = va_arg (ap, unsigned int); if (!unit->alive || bl->prev == NULL) return; range = (unit->range != 0) ? unit->range : group->range; if (range < 0 || battle_check_target (src, bl, group->target_flag) <= 0) return; if (bl->x >= src->x - range && bl->x <= src->x + range && bl->y >= src->y - range && bl->y <= src->y + range) skill_unit_onplace (unit, bl, tick); else skill_unit_onout (unit, bl, tick); } /*========================================== * スキルユニット自体の移動時処理 * 引数はグループと移動量 *------------------------------------------ */ int skill_unit_move_unit_group (struct skill_unit_group *group, int m, int dx, int dy) { nullpo_retr (0, group); if (group->unit_count <= 0) return 0; if (group->unit != NULL) { if (!battle_config.unit_movement_type) { int i; for (i = 0; i < group->unit_count; i++) { struct skill_unit *unit = &group->unit[i]; if (unit->alive && !(m == unit->bl.m && dx == 0 && dy == 0)) { int range = unit->range; map_delblock (&unit->bl); unit->bl.m = m; unit->bl.x += dx; unit->bl.y += dy; map_addblock (&unit->bl); clif_skill_setunit (unit); if (range > 0) { if (range < 7) range = 7; map_foreachinarea (skill_unit_move_unit_group_sub, unit->bl.m, unit->bl.x - range, unit->bl.y - range, unit->bl.x + range, unit->bl.y + range, 0, &unit->bl, gettick ()); } } } } else { int i, j, *r_flag, *s_flag, *m_flag; struct skill_unit *unit1; struct skill_unit *unit2; r_flag = (int *) malloc (sizeof (int) * group->unit_count); s_flag = (int *) malloc (sizeof (int) * group->unit_count); m_flag = (int *) malloc (sizeof (int) * group->unit_count); memset (r_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ memset (s_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ memset (m_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ //先にフラグを全部決める for (i = 0; i < group->unit_count; i++) { int move_check = 0; // かぶりフラグ unit1 = &group->unit[i]; for (j = 0; j < group->unit_count; j++) { unit2 = &group->unit[j]; if (unit1->bl.m == m && unit1->bl.x + dx == unit2->bl.x && unit1->bl.y + dy == unit2->bl.y) { //移動先にユニットがかぶってたら s_flag[i] = 1; // 移動前のユニットナンバーの継承フラグon r_flag[j] = 1; // かぶるユニットナンバーの残留フラグon move_check = 1; //ユニットがかぶった。 break; } } if (!move_check) // ユニットがかぶってなかったら m_flag[i] = 1; // 移動前ユニットナンバーの移動フラグon } //フラグに基づいてユニット移動 for (i = 0; i < group->unit_count; i++) { unit1 = &group->unit[i]; if (m_flag[i]) { // 移動フラグがonで if (!r_flag[i]) { // 残留フラグがoffなら //単純移動(rangeも継承の必要無し) int range = unit1->range; map_delblock (&unit1->bl); unit1->bl.m = m; unit1->bl.x += dx; unit1->bl.y += dy; map_addblock (&unit1->bl); clif_skill_setunit (unit1); if (range > 0) { if (range < 7) range = 7; map_foreachinarea (skill_unit_move_unit_group_sub, unit1->bl.m, unit1->bl.x - range, unit1->bl.y - range, unit1->bl.x + range, unit1->bl.y + range, 0, &unit1->bl, gettick ()); } } else { // 残留フラグがonなら //空ユニットになるので、継承可能なユニットを探す for (j = 0; j < group->unit_count; j++) { unit2 = &group->unit[j]; if (s_flag[j] && !r_flag[j]) { // 継承移動(range継承付き) int range = unit1->range; map_delblock (&unit2->bl); unit2->bl.m = m; unit2->bl.x = unit1->bl.x + dx; unit2->bl.y = unit1->bl.y + dy; unit2->range = unit1->range; map_addblock (&unit2->bl); clif_skill_setunit (unit2); if (range > 0) { if (range < 7) range = 7; map_foreachinarea (skill_unit_move_unit_group_sub, unit2->bl.m, unit2->bl.x - range, unit2->bl.y - range, unit2->bl.x + range, unit2->bl.y + range, 0, &unit2->bl, gettick ()); } s_flag[j] = 0; // 継承完了したのでoff break; } } } } } free (r_flag); free (s_flag); free (m_flag); } } return 0; } /*---------------------------------------------------------------------------- * アイテム合成 *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * 初期化系 */ static int scan_stat (char *statname) { if (!strcasecmp (statname, "str")) return SP_STR; if (!strcasecmp (statname, "dex")) return SP_DEX; if (!strcasecmp (statname, "agi")) return SP_AGI; if (!strcasecmp (statname, "vit")) return SP_VIT; if (!strcasecmp (statname, "int")) return SP_INT; if (!strcasecmp (statname, "luk")) return SP_LUK; if (!strcasecmp (statname, "none")) return 0; else fprintf (stderr, "Unknown stat `%s'\n", statname); return 0; } /*========================================== * スキル関係ファイル読み込み * skill_db.txt スキルデータ * skill_cast_db.txt スキルの詠唱時間とディレイデータ *------------------------------------------ */ static int skill_readdb (void) { int i, j, k, l; FILE *fp; char line[1024], *p; /* The main skill database */ memset (skill_db, 0, sizeof (skill_db)); fp = fopen_ ("db/skill_db.txt", "r"); if (fp == NULL) { printf ("can't read db/skill_db.txt\n"); return 1; } while (fgets (line, 1020, fp)) { char *split[50], *split2[MAX_SKILL_LEVEL]; if (line[0] == '/' && line[1] == '/') continue; for (j = 0, p = line; j < 18 && p; j++) { while (*p == '\t' || *p == ' ') p++; split[j] = p; p = strchr (p, ','); if (p) *p++ = 0; } if (split[17] == NULL || j < 18) { fprintf (stderr, "Incomplete skill db data online (%d entries)\n", j); continue; } i = atoi (split[0]); if (i < 0 || i > MAX_SKILL_DB) continue; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].range[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); skill_db[i].hit = atoi (split[2]); skill_db[i].inf = atoi (split[3]); skill_db[i].pl = atoi (split[4]); skill_db[i].nk = atoi (split[5]); skill_db[i].max_raise = atoi (split[6]); skill_db[i].max = atoi (split[7]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[8]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].num[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); if (strcasecmp (split[9], "yes") == 0) skill_db[i].castcancel = 1; else skill_db[i].castcancel = 0; skill_db[i].cast_def_rate = atoi (split[9]); skill_db[i].inf2 = atoi (split[10]); skill_db[i].maxcount = atoi (split[11]); if (strcasecmp (split[13], "weapon") == 0) skill_db[i].skill_type = BF_WEAPON; else if (strcasecmp (split[12], "magic") == 0) skill_db[i].skill_type = BF_MAGIC; else if (strcasecmp (split[12], "misc") == 0) skill_db[i].skill_type = BF_MISC; else skill_db[i].skill_type = 0; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[14]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].blewcount[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); if (!strcasecmp (split[15], "passive")) { skill_pool_register (i); skill_db[i].poolflags = SKILL_POOL_FLAG; } else if (!strcasecmp (split[15], "active")) { skill_pool_register (i); skill_db[i].poolflags = SKILL_POOL_FLAG | SKILL_POOL_ACTIVE; } else skill_db[i].poolflags = 0; skill_db[i].stat = scan_stat (split[16]); char *tmp = strdup (split[17]); skill_names[i].desc = tmp; { // replace "_" by " " char *s = tmp; while ((s = strchr (s, '_'))) *s = ' '; if ((s = strchr (tmp, '\t')) || (s = strchr (tmp, ' ')) || (s = strchr (tmp, '\n'))) *s = '\000'; } } fclose_ (fp); printf ("read db/skill_db.txt done\n"); fp = fopen_ ("db/skill_require_db.txt", "r"); if (fp == NULL) { printf ("can't read db/skill_require_db.txt\n"); return 1; } while (fgets (line, 1020, fp)) { char *split[51], *split2[MAX_SKILL_LEVEL]; if (line[0] == '/' && line[1] == '/') continue; for (j = 0, p = line; j < 30 && p; j++) { while (*p == '\t' || *p == ' ') p++; split[j] = p; p = strchr (p, ','); if (p) *p++ = 0; } if (split[29] == NULL || j < 30) continue; i = atoi (split[0]); if (i < 0 || i > MAX_SKILL_DB) continue; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].hp[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].mhp[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].sp[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].hp_rate[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[5]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].sp_rate[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[6]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].zeny[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[7]; j < 32 && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < 32 && split2[k]; k++) { l = atoi (split2[k]); if (l == 99) { skill_db[i].weapon = 0xffffffff; break; } else skill_db[i].weapon |= 1 << l; } if (strcasecmp (split[8], "hiding") == 0) skill_db[i].state = ST_HIDING; else if (strcasecmp (split[8], "cloaking") == 0) skill_db[i].state = ST_CLOAKING; else if (strcasecmp (split[8], "hidden") == 0) skill_db[i].state = ST_HIDDEN; else if (strcasecmp (split[8], "riding") == 0) skill_db[i].state = ST_RIDING; else if (strcasecmp (split[8], "falcon") == 0) skill_db[i].state = ST_FALCON; else if (strcasecmp (split[8], "cart") == 0) skill_db[i].state = ST_CART; else if (strcasecmp (split[8], "shield") == 0) skill_db[i].state = ST_SHIELD; else if (strcasecmp (split[8], "sight") == 0) skill_db[i].state = ST_SIGHT; else if (strcasecmp (split[8], "explosionspirits") == 0) skill_db[i].state = ST_EXPLOSIONSPIRITS; else if (strcasecmp (split[8], "recover_weight_rate") == 0) skill_db[i].state = ST_RECOV_WEIGHT_RATE; else if (strcasecmp (split[8], "move_enable") == 0) skill_db[i].state = ST_MOVE_ENABLE; else if (strcasecmp (split[8], "water") == 0) skill_db[i].state = ST_WATER; else skill_db[i].state = ST_NONE; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[9]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].spiritball[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); skill_db[i].itemid[0] = atoi (split[10]); skill_db[i].amount[0] = atoi (split[11]); skill_db[i].itemid[1] = atoi (split[12]); skill_db[i].amount[1] = atoi (split[13]); skill_db[i].itemid[2] = atoi (split[14]); skill_db[i].amount[2] = atoi (split[15]); skill_db[i].itemid[3] = atoi (split[16]); skill_db[i].amount[3] = atoi (split[17]); skill_db[i].itemid[4] = atoi (split[18]); skill_db[i].amount[4] = atoi (split[19]); skill_db[i].itemid[5] = atoi (split[20]); skill_db[i].amount[5] = atoi (split[21]); skill_db[i].itemid[6] = atoi (split[22]); skill_db[i].amount[6] = atoi (split[23]); skill_db[i].itemid[7] = atoi (split[24]); skill_db[i].amount[7] = atoi (split[25]); skill_db[i].itemid[8] = atoi (split[26]); skill_db[i].amount[8] = atoi (split[27]); skill_db[i].itemid[9] = atoi (split[28]); skill_db[i].amount[9] = atoi (split[29]); } fclose_ (fp); printf ("read db/skill_require_db.txt done\n"); /* ? */ fp = fopen_ ("db/skill_cast_db.txt", "r"); if (fp == NULL) { printf ("can't read db/skill_cast_db.txt\n"); return 1; } while (fgets (line, 1020, fp)) { char *split[50], *split2[MAX_SKILL_LEVEL]; memset (split, 0, sizeof (split)); // [Valaris] thanks to fov if (line[0] == '/' && line[1] == '/') continue; for (j = 0, p = line; j < 5 && p; j++) { while (*p == '\t' || *p == ' ') p++; split[j] = p; p = strchr (p, ','); if (p) *p++ = 0; } if (split[4] == NULL || j < 5) continue; i = atoi (split[0]); if (i < 0 || i > MAX_SKILL_DB) continue; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].cast[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].delay[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].upkeep_time[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); memset (split2, 0, sizeof (split2)); for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].upkeep_time2[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); } fclose_ (fp); printf ("read db/skill_cast_db.txt done\n"); fp = fopen_ ("db/skill_castnodex_db.txt", "r"); if (fp == NULL) { printf ("can't read db/skill_castnodex_db.txt\n"); return 1; } while (fgets (line, 1020, fp)) { char *split[50], *split2[MAX_SKILL_LEVEL]; memset (split, 0, sizeof (split)); if (line[0] == '/' && line[1] == '/') continue; for (j = 0, p = line; j < 2 && p; j++) { while (*p == '\t' || *p == ' ') p++; split[j] = p; p = strchr (p, ','); if (p) *p++ = 0; } i = atoi (split[0]); if (i < 0 || i > MAX_SKILL_DB) continue; memset (split2, 0, sizeof (split2)); for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) { split2[j] = p; p = strchr (p, ':'); if (p) *p++ = 0; } for (k = 0; k < MAX_SKILL_LEVEL; k++) skill_db[i].castnodex[k] = (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); } fclose_ (fp); printf ("read db/skill_castnodex_db.txt done\n"); return 0; } void skill_reload (void) { /* * * * * */ do_init_skill (); } /*========================================== * スキル関係初期化処理 *------------------------------------------ */ int do_init_skill (void) { skill_readdb (); add_timer_interval (gettick () + SKILLUNITTIMER_INVERVAL, skill_unit_timer, 0, 0, SKILLUNITTIMER_INVERVAL); return 0; }